PageRenderTime 112ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/transaction/ConcurrentTransactionAwareProxyTests.java

https://github.com/ftaiolivista/spring-batch
Java | 279 lines | 201 code | 58 blank | 20 comment | 13 complexity | 5ac27bbf01a57d63a9761f183071707c MD5 | raw file
  1. /*
  2. * Copyright 2006-2009 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.batch.support.transaction;
  17. import static org.junit.Assert.assertEquals;
  18. import static org.junit.Assert.assertFalse;
  19. import static org.junit.Assert.assertTrue;
  20. import static org.junit.Assert.fail;
  21. import java.util.ArrayList;
  22. import java.util.HashMap;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Set;
  26. import java.util.concurrent.Callable;
  27. import java.util.concurrent.CompletionService;
  28. import java.util.concurrent.ExecutionException;
  29. import java.util.concurrent.ExecutorCompletionService;
  30. import java.util.concurrent.ExecutorService;
  31. import java.util.concurrent.Executors;
  32. import org.apache.commons.logging.Log;
  33. import org.apache.commons.logging.LogFactory;
  34. import org.junit.After;
  35. import org.junit.Before;
  36. import org.junit.Ignore;
  37. import org.junit.Test;
  38. import org.springframework.transaction.PlatformTransactionManager;
  39. import org.springframework.transaction.TransactionStatus;
  40. import org.springframework.transaction.support.TransactionCallback;
  41. import org.springframework.transaction.support.TransactionTemplate;
  42. import org.springframework.util.Assert;
  43. /**
  44. * @author Dave Syer
  45. *
  46. */
  47. public class ConcurrentTransactionAwareProxyTests {
  48. private static Log logger = LogFactory.getLog(ConcurrentTransactionAwareProxyTests.class);
  49. private PlatformTransactionManager transactionManager = new ResourcelessTransactionManager();
  50. int outerMax = 20;
  51. int innerMax = 30;
  52. private ExecutorService executor;
  53. private CompletionService<List<String>> completionService;
  54. @Before
  55. public void init() {
  56. executor = Executors.newFixedThreadPool(outerMax);
  57. completionService = new ExecutorCompletionService<List<String>>(executor);
  58. }
  59. @After
  60. public void close() {
  61. executor.shutdown();
  62. }
  63. @Test(expected = Throwable.class)
  64. public void testConcurrentTransactionalSet() throws Exception {
  65. Set<String> set = TransactionAwareProxyFactory.createTransactionalSet();
  66. testSet(set);
  67. }
  68. @Test
  69. public void testConcurrentTransactionalAppendOnlySet() throws Exception {
  70. Set<String> set = TransactionAwareProxyFactory.createAppendOnlyTransactionalSet();
  71. testSet(set);
  72. }
  73. @Test
  74. public void testConcurrentTransactionalAppendOnlyList() throws Exception {
  75. List<String> list = TransactionAwareProxyFactory.createAppendOnlyTransactionalList();
  76. testList(list, false);
  77. }
  78. @Ignore("This fails too often and is a false negative")
  79. @Test
  80. public void testConcurrentTransactionalList() throws Exception {
  81. List<String> list = TransactionAwareProxyFactory.createTransactionalList();
  82. try {
  83. testList(list, true);
  84. fail("Expected ExecutionException or AssertionError (but don't panic if it didn't happen: it probably just means we got lucky for a change)");
  85. }
  86. catch (ExecutionException e) {
  87. String message = e.getCause().getMessage();
  88. assertTrue("Wrong message: " + message, message.startsWith("Lost update"));
  89. }
  90. catch (AssertionError e) {
  91. String message = e.getMessage();
  92. assertTrue("Wrong message: " + message, message.startsWith("Wrong number of results"));
  93. }
  94. }
  95. @Test
  96. public void testConcurrentTransactionalAppendOnlyMap() throws Exception {
  97. Map<Long, Map<String, String>> map = TransactionAwareProxyFactory.createAppendOnlyTransactionalMap();
  98. testMap(map);
  99. }
  100. @Test(expected = ExecutionException.class)
  101. public void testConcurrentTransactionalMap() throws Exception {
  102. Map<Long, Map<String, String>> map = TransactionAwareProxyFactory.createTransactionalMap();
  103. testMap(map);
  104. }
  105. @Test
  106. public void testTransactionalContains() throws Exception {
  107. final Map<Long, Map<String, String>> map = TransactionAwareProxyFactory.createAppendOnlyTransactionalMap();
  108. boolean result = (Boolean) new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
  109. public Object doInTransaction(TransactionStatus status) {
  110. return map.containsKey("foo");
  111. }
  112. });
  113. assertFalse(result);
  114. }
  115. private void testSet(final Set<String> set) throws Exception {
  116. for (int i = 0; i < outerMax; i++) {
  117. final int count = i;
  118. completionService.submit(new Callable<List<String>>() {
  119. public List<String> call() throws Exception {
  120. List<String> list = new ArrayList<String>();
  121. for (int i = 0; i < innerMax; i++) {
  122. String value = count + "bar" + i;
  123. saveInSetAndAssert(set, value);
  124. list.add(value);
  125. }
  126. return list;
  127. }
  128. });
  129. }
  130. for (int i = 0; i < outerMax; i++) {
  131. List<String> result = completionService.take().get();
  132. assertEquals(innerMax, result.size());
  133. }
  134. assertEquals(innerMax * outerMax, set.size());
  135. }
  136. private void testList(final List<String> list, final boolean mutate) throws Exception {
  137. for (int i = 0; i < outerMax; i++) {
  138. completionService.submit(new Callable<List<String>>() {
  139. public List<String> call() throws Exception {
  140. List<String> result = new ArrayList<String>();
  141. for (int i = 0; i < innerMax; i++) {
  142. String value = "bar" + i;
  143. saveInListAndAssert(list, value);
  144. result.add(value);
  145. // Need to slow it down to allow threads to interleave
  146. Thread.sleep(10L);
  147. if (mutate) {
  148. list.remove(value);
  149. list.add(value);
  150. }
  151. }
  152. logger.info("Added: " + innerMax + " values");
  153. return result;
  154. }
  155. });
  156. }
  157. for (int i = 0; i < outerMax; i++) {
  158. List<String> result = completionService.take().get();
  159. assertEquals("Wrong number of results in inner task", innerMax, result.size());
  160. }
  161. assertEquals("Wrong number of results in aggregate", innerMax * outerMax, list.size());
  162. }
  163. private void testMap(final Map<Long, Map<String, String>> map) throws Exception {
  164. int numberOfKeys = outerMax;
  165. for (int i = 0; i < outerMax; i++) {
  166. for (int j = 0; j < numberOfKeys; j++) {
  167. final long id = j * 1000 + 123L + i;
  168. completionService.submit(new Callable<List<String>>() {
  169. public List<String> call() throws Exception {
  170. List<String> list = new ArrayList<String>();
  171. for (int i = 0; i < innerMax; i++) {
  172. String value = "bar" + i;
  173. list.add(saveInMapAndAssert(map, id, value).get("foo"));
  174. }
  175. return list;
  176. }
  177. });
  178. }
  179. for (int j = 0; j < numberOfKeys; j++) {
  180. completionService.take().get();
  181. }
  182. }
  183. }
  184. private String saveInSetAndAssert(final Set<String> set, final String value) {
  185. new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
  186. public Object doInTransaction(TransactionStatus status) {
  187. set.add(value);
  188. return null;
  189. }
  190. });
  191. Assert.state(set.contains(value), "Lost update: value=" + value);
  192. return value;
  193. }
  194. private String saveInListAndAssert(final List<String> list, final String value) {
  195. new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
  196. public Object doInTransaction(TransactionStatus status) {
  197. list.add(value);
  198. return null;
  199. }
  200. });
  201. Assert.state(list.contains(value), "Lost update: value=" + value);
  202. return value;
  203. }
  204. private Map<String, String> saveInMapAndAssert(final Map<Long, Map<String, String>> map, final Long id,
  205. final String value) {
  206. new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
  207. public Object doInTransaction(TransactionStatus status) {
  208. if (!map.containsKey(id)) {
  209. map.put(id, new HashMap<String, String>());
  210. }
  211. map.get(id).put("foo", value);
  212. return null;
  213. }
  214. });
  215. Map<String, String> result = map.get(id);
  216. Assert.state(result != null, "Lost insert: null String at value=" + value);
  217. String foo = result.get("foo");
  218. Assert.state(value.equals(foo), "Lost update: wrong value=" + value + " (found " + foo + ") for id=" + id);
  219. return result;
  220. }
  221. }