diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractTransactionalResourceItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractTransactionalResourceItemWriter.java deleted file mode 100644 index ecbc75318..000000000 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractTransactionalResourceItemWriter.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2006-2007 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.batch.item.database; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.springframework.batch.item.ClearFailedException; -import org.springframework.batch.item.FlushFailedException; -import org.springframework.batch.item.ItemWriter; -import org.springframework.batch.repeat.RepeatContext; -import org.springframework.batch.repeat.support.RepeatSynchronizationManager; -import org.springframework.transaction.support.TransactionSynchronizationManager; -import org.springframework.util.Assert; - -/** - * Stores items in transactional resource and flushes aggressively in case of - * failure. This is useful for batch update writers which need to identify the - * failed item after failed flush. - * - * @see BatchSqlUpdateItemWriter - * @see HibernateAwareItemWriter - * - * @author Dave Syer - * @author Robert Kasanicky - */ -public abstract class AbstractTransactionalResourceItemWriter implements ItemWriter { - - private Set failed = new HashSet(); - - /** - * Flushing delegated to subclass surrounded by binding and unbinding of - * transactional resources. - * - * @see org.springframework.batch.item.ItemWriter#flush() - */ - public final void flush() throws FlushFailedException { - bindTransactionResources(); - try { - doFlush(); - } - catch (RuntimeException e) { - synchronized (failed) { - failed.addAll(getProcessed()); - } - // This used to contain a call to onError, however, I think this - // should be handled within the step. - throw e; - } - finally { - unbindTransactionResources(); - } - } - - /** - * Delegate to subclass to actually do the writing, but flushes aggressively - * if the item was previously part of a failed chunk. - * - * @throws Exception - * - * @see org.springframework.batch.item.ItemWriter#write(java.util.List) - */ - public final void write(List output) throws Exception { - bindTransactionResources(); - getProcessed().addAll(output); - doWrite(output); - flushIfNecessary(output); - } - - /** - * Delegate to subclass and unbind transactional resources, effectively - * clearing the item buffer. - */ - public final void clear() throws ClearFailedException { - try { - doClear(); - } - finally { - unbindTransactionResources(); - } - } - - /** - * Callback method of {@link #flush()}. - */ - protected abstract void doFlush() throws FlushFailedException; - - /** - * Callback method of {@link #clear()}. - */ - protected abstract void doClear() throws ClearFailedException; - - /** - * Callback method of {@link #write(List)}. - */ - protected abstract void doWrite(List output) throws Exception; - - /** - * @return Key for items processed in the current transaction - * {@link RepeatContext}. - */ - protected abstract String getResourceKey(); - - private void flushIfNecessary(List outputs) { - Set flush = new HashSet(); - synchronized (failed) { - for (T output : outputs) { - if (failed.contains(output)) { - flush.add(output); - } - } - } - if (!flush.isEmpty()) { - // Force early completion to commit aggressively if we encounter a - // failed item (from a failed chunk but we don't know which one was - // the problem). - RepeatSynchronizationManager.setCompleteOnly(); - // Remove the failed item from the cache, otherwise it could grow - // unnecessarily large. - failed.removeAll(flush); - // Flush now, so that if there is a failure this record can be - // skipped. - flush(); - } - - } - - /** - * Set up the {@link RepeatContext} as a transaction resource. - * - * @param context the context to set - */ - private void bindTransactionResources() { - if (TransactionSynchronizationManager.hasResource(getResourceKey())) { - return; - } - TransactionSynchronizationManager.bindResource(getResourceKey(), new HashSet()); - } - - /** - * Remove the transaction resource associated with this context. - */ - private void unbindTransactionResources() { - if (!TransactionSynchronizationManager.hasResource(getResourceKey())) { - return; - } - TransactionSynchronizationManager.unbindResource(getResourceKey()); - } - - /** - * Accessor for the list of processed items in this transaction. - * - * @return the processed - */ - @SuppressWarnings("unchecked") - protected Set getProcessed() { - Assert.state(TransactionSynchronizationManager.hasResource(getResourceKey()), - "Processed items not bound to transaction."); - Set processed = (Set) TransactionSynchronizationManager.getResource(getResourceKey()); - return processed; - } -} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/BatchSqlUpdateItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/BatchSqlUpdateItemWriter.java index 45ab98fd3..25d565bfc 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/BatchSqlUpdateItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/BatchSqlUpdateItemWriter.java @@ -17,12 +17,10 @@ package org.springframework.batch.item.database; import java.sql.PreparedStatement; import java.sql.SQLException; -import java.util.ArrayList; import java.util.List; -import org.springframework.batch.item.ClearFailedException; import org.springframework.batch.item.ItemWriter; -import org.springframework.batch.repeat.RepeatContext; +import org.springframework.batch.item.support.AbstractItemWriter; import org.springframework.beans.factory.InitializingBean; import org.springframework.dao.DataAccessException; import org.springframework.dao.EmptyResultDataAccessException; @@ -42,9 +40,7 @@ import org.springframework.util.Assert; * {@link ItemPreparedStatementSetter}, which is responsible for mapping the * item to a PreparedStatement.
* - * It is expected that {@link #write(List)} is called inside a transaction, - * and that {@link #flush()} is then subsequently called before the transaction - * commits, or {@link #clear()} before it rolls back.
+ * It is expected that {@link #write(List)} is called inside a transaction.
* * The writer is thread safe after its properties are set (normal singleton * behaviour), so it can be used to write in multiple concurrent transactions. @@ -56,12 +52,7 @@ import org.springframework.util.Assert; * @author Dave Syer * */ -public class BatchSqlUpdateItemWriter extends AbstractTransactionalResourceItemWriter implements InitializingBean { - - /** - * Key for items processed in the current transaction {@link RepeatContext}. - */ - private static final String ITEMS_PROCESSED = BatchSqlUpdateItemWriter.class.getName() + ".ITEMS_PROCESSED"; +public class BatchSqlUpdateItemWriter extends AbstractItemWriter implements InitializingBean { private JdbcOperations jdbcTemplate; @@ -114,22 +105,18 @@ public class BatchSqlUpdateItemWriter extends AbstractTransactionalResourceIt Assert.notNull(jdbcTemplate, "BatchSqlUpdateItemWriter requires an data source."); Assert.notNull(preparedStatementSetter, "BatchSqlUpdateItemWriter requires a ItemPreparedStatementSetter"); } - - /** - * Create and execute batch prepared statement. - * @throws EmptyResultDataAccessException if any of the items does not cause - * an update + + /* (non-Javadoc) + * @see org.springframework.batch.item.ItemWriter#write(java.util.List) */ - protected void doFlush() throws EmptyResultDataAccessException { + public void write(final List items) throws Exception { - final List processed = new ArrayList(getProcessed()); - - if (!processed.isEmpty()) { + if (!items.isEmpty()) { int[] values = (int[]) jdbcTemplate.execute(sql, new PreparedStatementCallback() { public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException { - for (T item : processed) { + for (T item : items) { preparedStatementSetter.setValues(item, ps); ps.addBatch(); } @@ -142,7 +129,7 @@ public class BatchSqlUpdateItemWriter extends AbstractTransactionalResourceIt int value = values[i]; if (value == 0) { throw new EmptyResultDataAccessException("Item " + i + " of " + values.length - + " did not update any rows: [" + processed.get(i) + "]", 1); + + " did not update any rows: [" + items.get(i) + "]", 1); } } } @@ -151,20 +138,4 @@ public class BatchSqlUpdateItemWriter extends AbstractTransactionalResourceIt } - protected String getResourceKey() { - return ITEMS_PROCESSED; - } - - /** - * No-op. - */ - protected void doWrite(List item) { - } - - /** - * No-op. - */ - protected void doClear() throws ClearFailedException { - } - } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateAwareItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateAwareItemWriter.java index 035ee6bd4..122693425 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateAwareItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/HibernateAwareItemWriter.java @@ -18,9 +18,8 @@ package org.springframework.batch.item.database; import java.util.List; import org.hibernate.SessionFactory; -import org.springframework.batch.item.ClearFailedException; import org.springframework.batch.item.ItemWriter; -import org.springframework.batch.repeat.RepeatContext; +import org.springframework.batch.item.support.AbstractItemWriter; import org.springframework.beans.factory.InitializingBean; import org.springframework.orm.hibernate3.HibernateOperations; import org.springframework.orm.hibernate3.HibernateTemplate; @@ -32,8 +31,8 @@ import org.springframework.util.Assert; * {@link ItemWriter} (the delegate). A delegate is required, and will be used * to do the actual writing of the item.
* - * It is expected that {@link #write(List)} is called inside a transaction, - * and that {@link #flush()} is then subsequently called before the transaction + * It is expected that {@link #write(List)} is called inside a transaction, and + * that {@link #flush()} is then subsequently called before the transaction * commits, or {@link #clear()} before it rolls back.
* * The writer is thread safe after its properties are set (normal singleton @@ -46,12 +45,7 @@ import org.springframework.util.Assert; * @author Dave Syer * */ -public class HibernateAwareItemWriter extends AbstractTransactionalResourceItemWriter implements InitializingBean { - - /** - * Key for items processed in the current transaction {@link RepeatContext}. - */ - private static final String ITEMS_PROCESSED = HibernateAwareItemWriter.class.getName() + ".ITEMS_PROCESSED"; +public class HibernateAwareItemWriter extends AbstractItemWriter implements InitializingBean { private ItemWriter delegate; @@ -86,7 +80,8 @@ public class HibernateAwareItemWriter extends AbstractTransactionalResourceIt } /** - * Check mandatory properties - there must be a delegate and hibernateTemplate. + * Check mandatory properties - there must be a delegate and + * hibernateTemplate. */ public void afterPropertiesSet() throws Exception { Assert.notNull(delegate, "HibernateAwareItemWriter requires an ItemWriter as a delegate."); @@ -94,30 +89,20 @@ public class HibernateAwareItemWriter extends AbstractTransactionalResourceIt } /** - * Delegate to subclass and flush the hibernate session. + * Delegate the writing and then flush and clear te hibernate session. + * + * @see org.springframework.batch.item.ItemWriter#write(java.util.List) */ - protected void doFlush() { - delegate.flush(); - hibernateTemplate.flush(); - // This should happen when the transaction commits anyway, but to be - // sure... - hibernateTemplate.clear(); - } - - /** - * Call the delegate clear() method, and then clear the hibernate session. - */ - protected void doClear() throws ClearFailedException { - delegate.clear(); - hibernateTemplate.clear(); - } - - protected String getResourceKey() { - return ITEMS_PROCESSED; - } - - protected void doWrite(List item) throws Exception { - delegate.write(item); + public void write(List items) throws Exception { + delegate.write(items); + try { + hibernateTemplate.flush(); + } + finally { + // This should happen when the transaction commits anyway, but to be + // sure... + hibernateTemplate.clear(); + } } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaAwareItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaAwareItemWriter.java index c89277ee3..5fcd7a5c8 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaAwareItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaAwareItemWriter.java @@ -5,25 +5,25 @@ import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; -import org.springframework.batch.item.ClearFailedException; import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.support.AbstractItemWriter; import org.springframework.beans.factory.InitializingBean; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.orm.jpa.EntityManagerFactoryUtils; import org.springframework.util.Assert; /** - * {@link org.springframework.batch.item.ItemWriter} that is aware of the JPA EntityManagerFactory and can - * take some responsibilities to do with chunk boundaries away from a less smart - * {@link org.springframework.batch.item.ItemWriter} (the delegate). A delegate is required, and will be used - * to do the actual writing of the item.
- * - * It is required that {@link #write(List)} is called inside a transaction, - * and that {@link #flush()} is then subsequently called before the transaction - * commits, or {@link #clear()} before it rolls back.
- * - * The reader must be configured with an {@link javax.persistence.EntityManagerFactory} that is capable - * of participating in Spring managed transactions. + * {@link org.springframework.batch.item.ItemWriter} that is aware of the JPA + * EntityManagerFactory and can take some responsibilities to do with chunk + * boundaries away from a less smart + * {@link org.springframework.batch.item.ItemWriter} (the delegate). A delegate + * is required, and will be used to do the actual writing of the item.
+ * + * It is required that {@link #write(List)} is called inside a transaction.
+ * + * The reader must be configured with an + * {@link javax.persistence.EntityManagerFactory} that is capable of + * participating in Spring managed transactions. * * The writer is thread safe after its properties are set (normal singleton * behaviour), so it can be used to write in multiple concurrent transactions. @@ -31,25 +31,21 @@ import org.springframework.util.Assert; * internally, and this collection is never cleared, so it is not a great idea * to go on using the writer indefinitely. Normally it would be used for the * duration of a batch job and then discarded. - * + * * @author Dave Syer * @author Thomas Risberg - * + * */ -public class JpaAwareItemWriter extends AbstractTransactionalResourceItemWriter implements InitializingBean { - - /** - * Key for items processed in the current transaction {@link org.springframework.batch.repeat.RepeatContext}. - */ - private static final String ITEMS_PROCESSED = JpaAwareItemWriter.class.getName() + ".ITEMS_PROCESSED"; +public class JpaAwareItemWriter extends AbstractItemWriter implements InitializingBean { private ItemWriter delegate; private EntityManagerFactory entityManagerFactory; /** - * Public setter for the {@link org.springframework.batch.item.ItemWriter} property. - * + * Public setter for the {@link org.springframework.batch.item.ItemWriter} + * property. + * * @param delegate the delegate to set */ public void setDelegate(ItemWriter delegate) { @@ -58,7 +54,7 @@ public class JpaAwareItemWriter extends AbstractTransactionalResourceItemWrit /** * Set the EntityManager to be used internally. - * + * * @param entityManagerFactory the entityManagerFactory to set */ public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) { @@ -66,7 +62,8 @@ public class JpaAwareItemWriter extends AbstractTransactionalResourceItemWrit } /** - * Check mandatory properties - there must be a delegate and entityManagerFactory. + * Check mandatory properties - there must be a delegate and + * entityManagerFactory. */ public void afterPropertiesSet() throws Exception { Assert.notNull(delegate, "An ItemWriter to be used as a delegate is required."); @@ -74,38 +71,23 @@ public class JpaAwareItemWriter extends AbstractTransactionalResourceItemWrit } /** - * Delegate to subclass and flush the EntityManager. + * Delegate the writing to the delegate writer and then flush and clear the + * entity manager. + * + * @see org.springframework.batch.item.ItemWriter#write(java.util.List) */ - protected void doFlush() { - delegate.flush(); - EntityManager entityManager = - EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory); + public void write(List items) throws Exception { + EntityManager entityManager = EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory); if (entityManager == null) { throw new DataAccessResourceFailureException("Unable to obtain a transactional EntityManager"); } - entityManager.flush(); - entityManager.clear(); - } - - /** - * Call the delegate clear() method, and then clear the EntityManager. - */ - protected void doClear() throws ClearFailedException { - delegate.clear(); - EntityManager entityManager = - EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory); - if (entityManager == null) { - throw new DataAccessResourceFailureException("Unable to obtain a transactional EntityManager"); + delegate.write(items); + try { + entityManager.flush(); + } + finally { + entityManager.clear(); } - entityManager.clear(); - } - - protected String getResourceKey() { - return ITEMS_PROCESSED; - } - - protected void doWrite(List item) throws Exception { - delegate.write(item); } } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/BatchSqlUpdateItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/BatchSqlUpdateItemWriterTests.java index 45dfa6bb0..285858d7c 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/BatchSqlUpdateItemWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/BatchSqlUpdateItemWriterTests.java @@ -15,25 +15,26 @@ */ package org.springframework.batch.item.database; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; + import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; import junit.framework.TestCase; -import static org.easymock.EasyMock.*; -import org.springframework.batch.repeat.RepeatContext; -import org.springframework.batch.repeat.context.RepeatContextSupport; import org.springframework.batch.repeat.support.RepeatSynchronizationManager; import org.springframework.dao.DataAccessException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.UncategorizedSQLException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementCallback; -import org.springframework.transaction.support.TransactionSynchronizationManager; /** * @author Dave Syer @@ -47,12 +48,11 @@ public class BatchSqlUpdateItemWriterTests extends TestCase { protected List list = new ArrayList(); - private RepeatContext context = new RepeatContextSupport(null); - private PreparedStatement ps; /* * (non-Javadoc) + * * @see junit.framework.TestCase#setUp() */ protected void setUp() throws Exception { @@ -75,25 +75,21 @@ public class BatchSqlUpdateItemWriterTests extends TestCase { list.add(item); } }); - TransactionSynchronizationManager.bindResource(writer.getResourceKey(), new HashSet( - Collections.singleton("spam"))); - RepeatSynchronizationManager.register(context); } /* * (non-Javadoc) + * * @see junit.framework.TestCase#tearDown() */ protected void tearDown() throws Exception { - if (TransactionSynchronizationManager.hasResource(writer.getResourceKey())) { - TransactionSynchronizationManager.unbindResource(writer.getResourceKey()); - } RepeatSynchronizationManager.clear(); } /** * Test method for - * {@link org.springframework.batch.item.database.BatchSqlUpdateItemWriter#afterPropertiesSet()}. + * {@link org.springframework.batch.item.database.BatchSqlUpdateItemWriter#afterPropertiesSet()} + * . * @throws Exception */ public void testAfterPropertiesSet() throws Exception { @@ -109,83 +105,41 @@ public class BatchSqlUpdateItemWriterTests extends TestCase { /** * Test method for - * {@link org.springframework.batch.item.database.BatchSqlUpdateItemWriter#write(List)}. + * {@link org.springframework.batch.item.database.BatchSqlUpdateItemWriter#flush()} + * . * @throws Exception */ - public void testWrite() throws Exception { - writer.setSql("foo"); - writer.write(Collections.singletonList("bar")); - // Nothing happens till we flush - assertEquals(0, list.size()); - } - - /** - * Test method for - * {@link org.springframework.batch.item.database.BatchSqlUpdateItemWriter#clear()}. - */ - public void testClear() { - assertTrue(TransactionSynchronizationManager.hasResource(writer.getResourceKey())); - writer.clear(); - assertFalse(TransactionSynchronizationManager.hasResource(writer.getResourceKey())); - } - - /** - * Test method for - * {@link org.springframework.batch.item.database.BatchSqlUpdateItemWriter#flush()}. - * @throws SQLException - */ - public void testFlush() throws SQLException { - assertTrue(TransactionSynchronizationManager.hasResource(writer.getResourceKey())); - ps.addBatch(); // there is one item in the buffer to start - expectLastCall().times(1); - expect(ps.executeBatch()).andReturn(new int[0]); + public void testWriteAndFlush() throws Exception { + ps.addBatch(); + expectLastCall(); + expect(ps.executeBatch()).andReturn(new int[] { 123 }); replay(ps); - writer.flush(); - assertFalse(TransactionSynchronizationManager.hasResource(writer.getResourceKey())); + writer.write(Collections.singletonList("bar")); assertEquals(2, list.size()); assertTrue(list.contains("SQL")); } /** * Test method for - * {@link org.springframework.batch.item.database.BatchSqlUpdateItemWriter#flush()}. - * @throws Exception - */ - public void testWriteAndFlush() throws Exception { - assertTrue(TransactionSynchronizationManager.hasResource(writer.getResourceKey())); - ps.addBatch(); - expectLastCall().times(2); - expect(ps.executeBatch()).andReturn(new int[] { 123 }); - replay(ps); - writer.write(Collections.singletonList("bar")); - writer.flush(); - assertFalse(TransactionSynchronizationManager.hasResource(writer.getResourceKey())); - assertEquals(3, list.size()); - assertTrue(list.contains("SQL")); - } - - /** - * Test method for - * {@link org.springframework.batch.item.database.BatchSqlUpdateItemWriter#flush()}. + * {@link org.springframework.batch.item.database.BatchSqlUpdateItemWriter#flush()} + * . * @throws Exception */ public void testWriteAndFlushWithEmptyUpdate() throws Exception { - assertTrue(TransactionSynchronizationManager.hasResource(writer.getResourceKey())); ps.addBatch(); - expectLastCall().times(2); - expect(ps.executeBatch()).andReturn(new int[] {0}); + expectLastCall(); + expect(ps.executeBatch()).andReturn(new int[] { 0 }); replay(ps); - writer.write(Collections.singletonList("bar")); try { - writer.flush(); + writer.write(Collections.singletonList("bar")); fail("Expected EmptyResultDataAccessException"); - } catch (EmptyResultDataAccessException e) { + } + catch (EmptyResultDataAccessException e) { // expected String message = e.getMessage(); - assertTrue("Wrong message: "+message, message.indexOf("did not update")>=0); + assertTrue("Wrong message: " + message, message.indexOf("did not update") >= 0); } - assertFalse(TransactionSynchronizationManager.hasResource(writer.getResourceKey())); - assertEquals(3, list.size()); + assertEquals(2, list.size()); assertTrue(list.contains("SQL")); } @@ -199,17 +153,15 @@ public class BatchSqlUpdateItemWriterTests extends TestCase { }); ps.addBatch(); expectLastCall().times(1); - expect(ps.executeBatch()).andReturn(new int[] {123}); + expect(ps.executeBatch()).andReturn(new int[] { 123 }); replay(ps); - writer.write(Collections.singletonList("foo")); try { - writer.flush(); + writer.write(Collections.singletonList("foo")); fail("Expected RuntimeException"); } catch (RuntimeException e) { assertEquals("bar", e.getMessage()); } - assertFalse(TransactionSynchronizationManager.hasResource(writer.getResourceKey())); assertEquals(2, list.size()); writer.setItemPreparedStatementSetter(new ItemPreparedStatementSetter() { public void setValues(String item, PreparedStatement ps) throws SQLException { @@ -217,21 +169,10 @@ public class BatchSqlUpdateItemWriterTests extends TestCase { } }); writer.write(Collections.singletonList("foo")); - writer.flush(); verify(ps); assertEquals(4, list.size()); assertTrue(list.contains("SQL")); assertTrue(list.contains("foo")); - assertTrue(context.isCompleteOnly()); - } - - /** - * Flushing without writing items previously should be handled gracefully. - */ - public void testEmptyFlush() { - // items are bound on write, so we unbind them first - TransactionSynchronizationManager.unbindResource(writer.getResourceKey()); - writer.flush(); } } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/HibernateAwareItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/HibernateAwareItemWriterTests.java index 1987efe00..2d75aac69 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/HibernateAwareItemWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/HibernateAwareItemWriterTests.java @@ -21,14 +21,10 @@ import java.util.List; import junit.framework.TestCase; -import org.springframework.batch.item.ClearFailedException; -import org.springframework.batch.item.FlushFailedException; -import org.springframework.batch.item.ItemWriter; -import org.springframework.batch.repeat.context.RepeatContextSupport; +import org.springframework.batch.item.support.AbstractItemWriter; import org.springframework.batch.repeat.support.RepeatSynchronizationManager; import org.springframework.dao.DataAccessException; import org.springframework.orm.hibernate3.HibernateTemplate; -import org.springframework.transaction.support.TransactionSynchronizationManager; /** * @author Dave Syer @@ -40,30 +36,21 @@ public class HibernateAwareItemWriterTests extends TestCase { public void flush() throws DataAccessException { list.add("flush"); } + public void clear() { - list.add("clear"); + list.add("clear"); }; } - private class StubItemWriter implements ItemWriter { + private class StubItemWriter extends AbstractItemWriter { public void write(List items) { list.addAll(items); } - - public void clear() throws ClearFailedException { - list.add("delegateClear"); - } - - public void flush() throws FlushFailedException { - list.add("delegateFlush"); - } } HibernateAwareItemWriter writer = new HibernateAwareItemWriter(); - - final List list = new ArrayList(); - private RepeatContextSupport context; + final List list = new ArrayList(); /* * (non-Javadoc) @@ -72,26 +59,23 @@ public class HibernateAwareItemWriterTests extends TestCase { */ protected void setUp() throws Exception { writer.setDelegate(new StubItemWriter()); - context = new RepeatContextSupport(null); - RepeatSynchronizationManager.register(context); writer.setHibernateTemplate(new HibernateTemplateWrapper()); list.clear(); } - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see junit.framework.TestCase#tearDown() */ protected void tearDown() throws Exception { - String key = writer.getResourceKey(); - if (TransactionSynchronizationManager.hasResource(key)) { - TransactionSynchronizationManager.unbindResource(key); - } RepeatSynchronizationManager.clear(); } /** * Test method for - * {@link org.springframework.batch.item.database.HibernateAwareItemWriter#afterPropertiesSet()}. + * {@link org.springframework.batch.item.database.HibernateAwareItemWriter#afterPropertiesSet()} + * . * * @throws Exception */ @@ -100,16 +84,17 @@ public class HibernateAwareItemWriterTests extends TestCase { try { writer.afterPropertiesSet(); fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { + } + catch (IllegalArgumentException e) { // expected - assertTrue("Wrong message for exception: " + e.getMessage(), e - .getMessage().indexOf("delegate") >= 0); + assertTrue("Wrong message for exception: " + e.getMessage(), e.getMessage().indexOf("delegate") >= 0); } } /** * Test method for - * {@link org.springframework.batch.item.database.HibernateAwareItemWriter#afterPropertiesSet()}. + * {@link org.springframework.batch.item.database.HibernateAwareItemWriter#afterPropertiesSet()} + * . * * @throws Exception */ @@ -117,25 +102,12 @@ public class HibernateAwareItemWriterTests extends TestCase { writer.afterPropertiesSet(); } - public void testWrite() throws Exception { + public void testWriteAndFlushSunnyDay() throws Exception { writer.write(Collections.singletonList("foo")); - assertEquals(1, list.size()); + assertEquals(3, list.size()); assertTrue(list.contains("foo")); - } - - public void testFlushWithFailure() throws Exception{ - final RuntimeException ex = new RuntimeException("bar"); - writer.setHibernateTemplate(new HibernateTemplate() { - public void flush() throws DataAccessException { - throw ex; - } - }); - try { - writer.flush(); - fail("Expected RuntimeException"); - } catch (RuntimeException e) { - assertEquals("bar", e.getMessage()); - } + assertTrue(list.contains("flush")); + assertTrue(list.contains("clear")); } public void testWriteAndFlushWithFailure() throws Exception { @@ -145,48 +117,24 @@ public class HibernateAwareItemWriterTests extends TestCase { throw ex; } }); - writer.write(Collections.singletonList("foo")); try { - writer.flush(); + writer.write(Collections.singletonList("foo")); fail("Expected RuntimeException"); - } catch (RuntimeException e) { + } + catch (RuntimeException e) { assertEquals("bar", e.getMessage()); } assertEquals(2, list.size()); assertTrue(list.contains("foo")); - assertTrue(list.contains("delegateFlush")); writer.setHibernateTemplate(new HibernateTemplateWrapper() { public void flush() throws DataAccessException { list.add("flush"); } }); writer.write(Collections.singletonList("foo")); - assertEquals(6, list.size()); + System.err.println(list); assertTrue(list.contains("flush")); assertTrue(list.contains("clear")); - assertTrue(context.isCompleteOnly()); } - /** - * Test method for - * {@link org.springframework.batch.item.database.HibernateAwareItemWriter#flush()}. - */ - public void testFlush() throws Exception{ - writer.flush(); - assertEquals(3, list.size()); - assertTrue(list.contains("flush")); - assertTrue(list.contains("clear")); - assertTrue(list.contains("delegateFlush")); - } - - /** - * Test method for - * {@link org.springframework.batch.item.database.HibernateAwareItemWriter#clear()}. - */ - public void testClear() throws Exception{ - writer.clear(); - assertEquals(2, list.size()); - assertTrue(list.contains("clear")); - assertTrue(list.contains("delegateClear")); - } } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaAwareItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaAwareItemWriterTests.java index 324d2062c..e10b6178f 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaAwareItemWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaAwareItemWriterTests.java @@ -16,30 +16,35 @@ package org.springframework.batch.item.database; -import static org.easymock.EasyMock.*; -import static org.junit.Assert.*; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expectLastCall; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; + import org.junit.Before; import org.junit.Test; - -import org.springframework.orm.jpa.EntityManagerHolder; import org.springframework.batch.item.ItemWriter; +import org.springframework.orm.jpa.EntityManagerHolder; import org.springframework.transaction.support.TransactionSynchronizationManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.EntityManager; - -import java.util.Collections; -import java.util.List; -import java.util.ArrayList; - /** * @author Thomas Risberg - * + * */ public class JpaAwareItemWriterTests { JpaAwareItemWriter writer = new JpaAwareItemWriter(); - + ItemWriter delegate; EntityManagerFactory emf; @@ -47,10 +52,10 @@ public class JpaAwareItemWriterTests { final List list = new ArrayList(); @Before - @SuppressWarnings({"unchecked"}) + @SuppressWarnings( { "unchecked" }) public void setUp() throws Exception { if (TransactionSynchronizationManager.isSynchronizationActive()) { - TransactionSynchronizationManager.clearSynchronization(); + TransactionSynchronizationManager.clearSynchronization(); } delegate = createMock("delegate", ItemWriter.class); writer.setDelegate(delegate); @@ -64,49 +69,39 @@ public class JpaAwareItemWriterTests { try { writer.afterPropertiesSet(); fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { + } + catch (IllegalArgumentException e) { // expected - assertTrue("Wrong message for exception: " + e.getMessage(), e - .getMessage().indexOf("delegate") >= 0); + assertTrue("Wrong message for exception: " + e.getMessage(), e.getMessage().indexOf("delegate") >= 0); } writer.setDelegate(delegate); try { writer.afterPropertiesSet(); fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { + } + catch (IllegalArgumentException e) { // expected - assertTrue("Wrong message for exception: " + e.getMessage(), e - .getMessage().indexOf("EntityManagerFactory") >= 0); + assertTrue("Wrong message for exception: " + e.getMessage(), + e.getMessage().indexOf("EntityManagerFactory") >= 0); } } @Test - public void testWrite() throws Exception { - delegate.write(Collections.singletonList("foo")); - replay(delegate); - writer.write(Collections.singletonList("foo")); - verify(delegate); - } - - @Test - public void testFlushWithFailure() throws Exception{ - final RuntimeException ex = new RuntimeException("bar"); + public void testWriteAndFlushSunnyDay() throws Exception { EntityManager em = createMock("em", EntityManager.class); - em.joinTransaction(); em.flush(); - expectLastCall().andThrow(ex); + em.clear(); replay(em); - expect(emf.createEntityManager()).andReturn(em); replay(emf); TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em)); - delegate.flush(); + List items = Arrays.asList(new String[] { "foo", "spam" }); + delegate.write(items); replay(delegate); - try { - writer.flush(); - fail("Expected RuntimeException"); - } catch (RuntimeException e) { - assertEquals("bar", e.getMessage()); - } + + writer.write(items); + + verify(delegate); + verify(em); TransactionSynchronizationManager.unbindResource(emf); } @@ -116,65 +111,25 @@ public class JpaAwareItemWriterTests { EntityManager em = createMock("em", EntityManager.class); em.flush(); expectLastCall().andThrow(ex); - em.flush(); em.clear(); replay(em); replay(emf); TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em)); - delegate.write(Collections.singletonList("foo")); - delegate.flush(); - delegate.write(Collections.singletonList("spam")); - delegate.flush(); + List items = Arrays.asList(new String[] { "foo", "spam" }); + delegate.write(items); replay(delegate); - writer.write(Collections.singletonList("foo")); try { - writer.flush(); + writer.write(items); fail("Expected RuntimeException"); - } catch (RuntimeException e) { + } + catch (RuntimeException e) { assertEquals("bar", e.getMessage()); } - writer.write(Collections.singletonList("spam")); - writer.flush(); - verify(delegate); verify(em); TransactionSynchronizationManager.unbindResource(emf); } - @Test - public void testFlush() throws Exception{ - EntityManager em = createMock("em", EntityManager.class); - em.flush(); - em.clear(); - replay(em); - replay(emf); - TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em)); - delegate.flush(); - replay(delegate); - - writer.flush(); - - verify(delegate); - verify(em); - TransactionSynchronizationManager.unbindResource(emf); - } - - @Test - public void testClear() throws Exception{ - EntityManager em = createMock("em", EntityManager.class); - em.clear(); - replay(em); - replay(emf); - TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em)); - delegate.clear(); - replay(delegate); - - writer.clear(); - - verify(delegate); - verify(em); - TransactionSynchronizationManager.unbindResource(emf); - } }