├── .gitignore ├── src ├── main │ ├── resources │ │ ├── spring │ │ │ └── batch │ │ │ │ ├── job │ │ │ │ └── complex │ │ │ │ │ ├── crosscutting │ │ │ │ │ ├── autothreadconf │ │ │ │ │ │ ├── auto-thread-conf-placeholder-job.properties │ │ │ │ │ │ ├── auto-thread-conf-job.xml │ │ │ │ │ │ └── auto-thread-conf-placeholder-job.xml │ │ │ │ │ └── interstepcommunication │ │ │ │ │ │ ├── interstep-communication-jobcontext-job.xml │ │ │ │ │ │ ├── interstep-communication-databean-job.xml │ │ │ │ │ │ └── interstep-communication-promotion-job.xml │ │ │ │ │ ├── jdbc │ │ │ │ │ ├── jdbc-generic-export-to-file-job.properties │ │ │ │ │ ├── jdbc-generic-export-to-database-job.properties │ │ │ │ │ ├── jdbc-generic-export-to-file-job.xml │ │ │ │ │ └── jdbc-generic-export-to-database-job.xml │ │ │ │ │ ├── skip │ │ │ │ │ ├── skip-policy-simple-job.xml │ │ │ │ │ └── skip-simple-job.xml │ │ │ │ │ ├── aggregating │ │ │ │ │ ├── aggregating-items-in-writer-job.xml │ │ │ │ │ └── aggregating-items-in-reader-job.xml │ │ │ │ │ └── file │ │ │ │ │ ├── renamefile │ │ │ │ │ ├── rename-file-simple-job.xml │ │ │ │ │ ├── rename-file-partition-job.xml │ │ │ │ │ └── rename-file-partition-extra-step-job.xml │ │ │ │ │ └── split │ │ │ │ │ └── split-file-simple-job.xml │ │ │ │ └── setup │ │ │ │ └── general │ │ │ │ ├── job-database.xml │ │ │ │ └── job-context.xml │ │ └── log4j │ │ │ └── log4j.properties │ └── java │ │ └── de │ │ └── langmi │ │ └── spring │ │ └── batch │ │ └── examples │ │ └── complex │ │ ├── file │ │ ├── renamefile │ │ │ ├── partition │ │ │ │ ├── BatchConstants.java │ │ │ │ ├── HeaderLineCallbackHandler.java │ │ │ │ ├── extrastep │ │ │ │ │ ├── PropagateFileNamesListener.java │ │ │ │ │ └── RenameFilesTasklet.java │ │ │ │ └── RenameFileListener.java │ │ │ └── simple │ │ │ │ ├── SimpleRenameFileTaskletStep.java │ │ │ │ └── SimpleItemReader.java │ │ └── split │ │ │ ├── SplitFilesItemWriter.java │ │ │ └── GetLineCountTasklet.java │ │ ├── skip │ │ ├── simple │ │ │ ├── SkipJobCustomException.java │ │ │ ├── SkipJobItemWriter.java │ │ │ ├── SkipJobItemProcessor.java │ │ │ └── SkipJobListener.java │ │ └── policy │ │ │ └── simple │ │ │ └── SkipJobPolicy.java │ │ ├── support │ │ ├── SimpleItemWriter.java │ │ ├── TestDataStringsFactoryBean.java │ │ ├── TestDataSimpleItemsFactoryBean.java │ │ ├── SimpleItem.java │ │ └── CustomMultiResourcePartitioner.java │ │ ├── crosscutting │ │ ├── autothreadconf │ │ │ ├── LoggingAsyncTaskExecutor.java │ │ │ └── AsyncTaskExecutorFactory.java │ │ └── interstepcommunication │ │ │ ├── promotion │ │ │ ├── ChangingStepExecutionContextTasklet.java │ │ │ └── ReadingJobExecutionContextTasklet.java │ │ │ ├── jobcontext │ │ │ ├── ChangingJobExecutionContextTasklet.java │ │ │ └── ReadingJobExecutionContextTasklet.java │ │ │ └── databean │ │ │ ├── ChangingDataBeanTasklet.java │ │ │ └── ReadingDataBeanTasklet.java │ │ ├── jdbc │ │ └── generic │ │ │ └── support │ │ │ └── MapPreparedStatementSetter.java │ │ └── aggregating │ │ ├── AggregatedItem.java │ │ ├── AggregatedItemWriter.java │ │ ├── AggregatingTestDataSimpleItemsFactoryBean.java │ │ ├── reader │ │ └── AggregateSimpleItemsReader.java │ │ └── writer │ │ ├── ReaderExhaustedWrapper.java │ │ └── AggregateSimpleItemsWriter.java └── test │ ├── resources │ ├── input │ │ ├── multi │ │ │ ├── input-1.txt │ │ │ ├── input-0.txt │ │ │ ├── input-2.txt │ │ │ ├── input-3.txt │ │ │ ├── input-4.txt │ │ │ └── input-5.txt │ │ └── simple │ │ │ └── input.txt │ └── spring │ │ └── batch │ │ └── setup │ │ └── test │ │ └── job-test-context.xml │ └── java │ └── de │ └── langmi │ └── spring │ └── batch │ └── examples │ └── complex │ ├── crosscutting │ ├── interstepcommunication │ │ ├── databean │ │ │ └── DatabeanInterStepCommunicationJobConfigurationTest.java │ │ ├── promotion │ │ │ └── PromotionInterStepCommunicationJobConfigurationTest.java │ │ └── jobcontext │ │ │ └── JobContextInterStepCommunicationJobConfigurationTest.java │ └── autothreadconf │ │ ├── AutoThreadConfJobConfigurationTest.java │ │ └── AutoThreadConfPlaceholderJobConfigurationTest.java │ ├── skip │ ├── SkipSimpleJobConfigurationTest.java │ └── SkipPolicySimpleJobConfigurationTest.java │ ├── aggregating │ ├── reader │ │ ├── AggregatingItemsInReaderJobConfigurationTest.java │ │ └── AggregateSimpleItemsReaderTest.java │ └── writer │ │ └── AggregatingItemsInWriterJobConfigurationTest.java │ ├── file │ ├── renamefile │ │ ├── RenameFileSimpleJobConfigurationTest.java │ │ ├── RenameFilePartitionJobConfigurationTest.java │ │ └── RenameFilePartitionExtraStepJobConfigurationTest.java │ └── split │ │ ├── GetLineCountTaskletTest.java │ │ ├── SplitFilesItemWriterTest.java │ │ └── SplitFileSimpleJobConfigurationTest.java │ └── jdbc │ └── generic │ └── export │ ├── JdbcGenericExportToFileJobConfigurationTest.java │ └── JdbcGenericExportToDatabaseJobConfigurationTest.java └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | # Texteditor history files 2 | *~ 3 | # Mac 4 | .DS_Store 5 | # eclipse IDE 6 | .settings 7 | # maven build output 8 | target -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/crosscutting/autothreadconf/auto-thread-conf-placeholder-job.properties: -------------------------------------------------------------------------------- 1 | concurrency.limit=1 -------------------------------------------------------------------------------- /src/test/resources/input/multi/input-1.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 4 5 | 5 6 | 6 7 | 7 8 | 8 9 | 9 10 | 10 11 | 11 12 | 12 13 | 13 14 | 14 15 | 15 16 | 16 17 | 17 18 | 18 19 | 19 20 | 20 -------------------------------------------------------------------------------- /src/test/resources/input/multi/input-0.txt: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | 2 4 | 3 5 | 4 6 | 5 7 | 6 8 | 7 9 | 8 10 | 9 11 | 10 12 | 11 13 | 12 14 | 13 15 | 14 16 | 15 17 | 16 18 | 17 19 | 18 20 | 19 21 | -------------------------------------------------------------------------------- /src/test/resources/input/multi/input-2.txt: -------------------------------------------------------------------------------- 1 | 2 2 | 3 3 | 4 4 | 5 5 | 6 6 | 7 7 | 8 8 | 9 9 | 10 10 | 11 11 | 12 12 | 13 13 | 14 14 | 15 15 | 16 16 | 17 17 | 18 18 | 19 19 | 20 20 | 21 -------------------------------------------------------------------------------- /src/test/resources/input/multi/input-3.txt: -------------------------------------------------------------------------------- 1 | 3 2 | 4 3 | 5 4 | 6 5 | 7 6 | 8 7 | 9 8 | 10 9 | 11 10 | 12 11 | 13 12 | 14 13 | 15 14 | 16 15 | 17 16 | 18 17 | 19 18 | 20 19 | 21 20 | 22 -------------------------------------------------------------------------------- /src/test/resources/input/multi/input-4.txt: -------------------------------------------------------------------------------- 1 | 4 2 | 5 3 | 6 4 | 7 5 | 8 6 | 9 7 | 10 8 | 11 9 | 12 10 | 13 11 | 14 12 | 15 13 | 16 14 | 17 15 | 18 16 | 19 17 | 20 18 | 21 19 | 22 20 | 23 -------------------------------------------------------------------------------- /src/test/resources/input/multi/input-5.txt: -------------------------------------------------------------------------------- 1 | 5 2 | 6 3 | 7 4 | 8 5 | 9 6 | 10 7 | 11 8 | 12 9 | 13 10 | 14 11 | 15 12 | 16 13 | 17 14 | 18 15 | 19 16 | 20 17 | 21 18 | 22 19 | 23 20 | 24 -------------------------------------------------------------------------------- /src/test/resources/input/simple/input.txt: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | 2 4 | 3 5 | 4 6 | 5 7 | 6 8 | 7 9 | 8 10 | 9 11 | 10 12 | 11 13 | 12 14 | 13 15 | 14 16 | 15 17 | 16 18 | 17 19 | 18 20 | 19 21 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/jdbc/jdbc-generic-export-to-file-job.properties: -------------------------------------------------------------------------------- 1 | # default commit rate for the job steps 2 | commit.rate=10 3 | # output file 4 | output.file=file:target/test-outputs/complex/jdbc/generic/jdbc-generic-export-data-output.txt 5 | # sql, multiline property with \ 6 | sql=\ 7 | SELECT \ 8 | ID, \ 9 | NAME \ 10 | FROM TEST \ 11 | ORDER BY ID -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/jdbc/jdbc-generic-export-to-database-job.properties: -------------------------------------------------------------------------------- 1 | # default commit rate for the job steps 2 | commit.rate=10 3 | # sql, multiline property with \ 4 | read-sql=\ 5 | SELECT \ 6 | ID, \ 7 | NAME \ 8 | FROM ROOT \ 9 | ORDER BY ID 10 | # sql, multiline property with \ 11 | write-sql=\ 12 | INSERT INTO TARGET \ 13 | (ID, NAME) VALUES (?, ?) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2011 Michael R. Lange . 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/file/renamefile/partition/BatchConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.renamefile.partition; 17 | 18 | /** 19 | * Constants holder class. 20 | * 21 | * @author Michael R. Lange 22 | */ 23 | public class BatchConstants { 24 | 25 | public static final String CONTEXT_NAME_BUSINESS_KEY = "business.key"; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/skip/simple/SkipJobCustomException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.skip.simple; 17 | 18 | /** 19 | * SkipJobCustomException. 20 | * 21 | * @author Michael R. Lange 22 | */ 23 | public class SkipJobCustomException extends Exception { 24 | 25 | public SkipJobCustomException(String string) { 26 | super(string); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/resources/log4j/log4j.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011 Michael R. Lange . 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | log4j.rootCategory=ERROR, stdout 16 | 17 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 18 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 19 | log4j.appender.stdout.layout.ConversionPattern=%d %p %t [%c] - <%m>%n 20 | 21 | #log4j.category.org.springframework.batch=DEBUG 22 | #log4j.category.org.springframework.transaction=INFO 23 | #log4j.category.org.hibernate.SQL=DEBUG 24 | # for debugging datasource initialization 25 | # log4j.category.test.jdbc=DEBUG 26 | log4j.category.de.langmi=DEBUG 27 | -------------------------------------------------------------------------------- /src/test/resources/spring/batch/setup/test/job-test-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 22 | 23 | Spring Batch Test Setup 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/support/SimpleItemWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.support; 17 | 18 | import java.util.List; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import org.springframework.batch.item.ItemWriter; 22 | 23 | /** 24 | * ItemWriter for SimpleItems. 25 | * 26 | * @author Michael R. Lange 27 | */ 28 | public class SimpleItemWriter implements ItemWriter { 29 | 30 | private final Logger LOG = LoggerFactory.getLogger(this.getClass()); 31 | 32 | @Override 33 | public void write(List items) throws Exception { 34 | LOG.debug("writing:"); 35 | for (SimpleItem simpleItem : items) { 36 | LOG.debug(simpleItem.toString()); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/skip/simple/SkipJobItemWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange. 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 de.langmi.spring.batch.examples.complex.skip.simple; 17 | 18 | import java.util.List; 19 | import org.springframework.batch.item.ItemWriter; 20 | 21 | /** 22 | * SkipJobItemWriter writes actually nothing, but throws exception for certain items. 23 | * 24 | * @author Michael R. Lange 25 | */ 26 | public class SkipJobItemWriter implements ItemWriter { 27 | 28 | /** {@inheritDoc} */ 29 | @Override 30 | public void write(List items) throws Exception { 31 | for (String item : items) { 32 | // throw exception for specific items 33 | if (Integer.valueOf(item) == 6 || Integer.valueOf(item) == 17) { 34 | throw new SkipJobCustomException("provoked error with:" + item); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/crosscutting/autothreadconf/LoggingAsyncTaskExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.crosscutting.autothreadconf; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import org.springframework.core.task.SimpleAsyncTaskExecutor; 21 | 22 | /** 23 | * Logging variant for SimpleAsyncTaskExecutor - logs the used the concurrency limit. 24 | * 25 | * @author Michael R. Lange 26 | */ 27 | public class LoggingAsyncTaskExecutor extends SimpleAsyncTaskExecutor { 28 | 29 | private final Logger LOG = LoggerFactory.getLogger(this.getClass()); 30 | 31 | @Override 32 | public void setConcurrencyLimit(int concurrencyLimit) { 33 | super.setConcurrencyLimit(concurrencyLimit); 34 | LOG.info("TaskExecutor ConcurrencyLimit:" + String.valueOf(concurrencyLimit)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/jdbc/generic/support/MapPreparedStatementSetter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.jdbc.generic.support; 17 | 18 | import java.sql.PreparedStatement; 19 | import java.sql.SQLException; 20 | import java.util.Map; 21 | import org.springframework.batch.item.database.ItemPreparedStatementSetter; 22 | 23 | /** 24 | * MapPreparedStatementSetter. 25 | * 26 | * @author Michael R. Lange 27 | */ 28 | public class MapPreparedStatementSetter implements ItemPreparedStatementSetter> { 29 | 30 | @Override 31 | public void setValues(Map item, PreparedStatement ps) throws SQLException { 32 | for (int i = 0; i < item.size(); i++) { 33 | // PreparedStatements start with 1 34 | ps.setObject(i + 1, item.get(String.valueOf(i))); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/skip/simple/SkipJobItemProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.skip.simple; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import org.springframework.batch.item.ItemProcessor; 21 | 22 | /** 23 | * Simple pass through processor, throws exception for certain items. 24 | * 25 | * @author Michael R. Lange 26 | */ 27 | public class SkipJobItemProcessor implements ItemProcessor { 28 | 29 | private final Logger LOG = LoggerFactory.getLogger(this.getClass()); 30 | 31 | @Override 32 | public String process(String item) throws Exception { 33 | LOG.debug("processor called:" + item); 34 | if (Integer.valueOf(item) == 5 || Integer.valueOf(item) == 16) { 35 | throw new SkipJobCustomException("provoked error with:" + item); 36 | } 37 | // do nothing 38 | return item; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/support/TestDataStringsFactoryBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.support; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import org.springframework.beans.factory.FactoryBean; 21 | 22 | /** 23 | * Provides List with test data. 24 | * 25 | * @author Michael R. Lange 26 | */ 27 | public class TestDataStringsFactoryBean implements FactoryBean> { 28 | 29 | /** Public to make it usable for test assertions. */ 30 | public static final int COUNT = 20; 31 | 32 | @Override 33 | public List getObject() throws Exception { 34 | List data = new ArrayList(); 35 | for (int i = 0; i < COUNT; i++) { 36 | data.add(String.valueOf(i)); 37 | } 38 | return data; 39 | } 40 | 41 | @Override 42 | public Class getObjectType() { 43 | return List.class; 44 | } 45 | 46 | @Override 47 | public boolean isSingleton() { 48 | return true; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/aggregating/AggregatedItem.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.aggregating; 17 | 18 | /** 19 | * AggregatedItem. 20 | * 21 | * @author Michael R. Lange 22 | */ 23 | public class AggregatedItem { 24 | 25 | private int id; 26 | private int sum = 0; 27 | 28 | public AggregatedItem(int id) { 29 | this.id = id; 30 | } 31 | 32 | 33 | public AggregatedItem(int id, int sum) { 34 | this.id = id; 35 | this.sum = sum; 36 | } 37 | 38 | public int getId() { 39 | return id; 40 | } 41 | 42 | public void setId(int id) { 43 | this.id = id; 44 | } 45 | 46 | public int getSum() { 47 | return sum; 48 | } 49 | 50 | public void setSum(int sum) { 51 | this.sum = sum; 52 | } 53 | 54 | public void add(int value) { 55 | this.sum = this.sum + value; 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "AggregatedItem{" + "id=" + id + ", sum=" + sum + '}'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/support/TestDataSimpleItemsFactoryBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.support; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import org.springframework.beans.factory.FactoryBean; 21 | 22 | /** 23 | * Provides List with test data. 24 | * 25 | * @author Michael R. Lange 26 | */ 27 | public class TestDataSimpleItemsFactoryBean implements FactoryBean> { 28 | 29 | /** Public to make it usable for test assertions. */ 30 | public static final int COUNT = 20; 31 | 32 | @Override 33 | public List getObject() throws Exception { 34 | List data = new ArrayList(); 35 | for (int i = 0; i < COUNT; i++) { 36 | data.add(new SimpleItem(i, i)); 37 | } 38 | return data; 39 | } 40 | 41 | @Override 42 | public Class getObjectType() { 43 | return List.class; 44 | } 45 | 46 | @Override 47 | public boolean isSingleton() { 48 | return true; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/crosscutting/autothreadconf/AsyncTaskExecutorFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.crosscutting.autothreadconf; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import org.springframework.core.task.SimpleAsyncTaskExecutor; 21 | 22 | /** 23 | * Factory creates a taskexecutor and sets its concurrency limit to 24 | * the count of available cpu. 25 | * 26 | * @author Michael R. Lange 27 | */ 28 | public class AsyncTaskExecutorFactory { 29 | 30 | private static final Logger LOG = LoggerFactory.getLogger(AsyncTaskExecutorFactory.class); 31 | 32 | public static SimpleAsyncTaskExecutor createInstance() { 33 | SimpleAsyncTaskExecutor instance = new SimpleAsyncTaskExecutor(); 34 | 35 | // set concurrencyLimit according to available processors 36 | Runtime runtime = Runtime.getRuntime(); 37 | int nrCpu = runtime.availableProcessors(); 38 | instance.setConcurrencyLimit(nrCpu); 39 | 40 | LOG.info("TaskExecutor ConcurrencyLimit:" + String.valueOf(nrCpu)); 41 | 42 | return instance; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/skip/policy/simple/SkipJobPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.skip.policy.simple; 17 | 18 | import de.langmi.spring.batch.examples.complex.skip.simple.SkipJobCustomException; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import org.springframework.batch.core.step.skip.SkipLimitExceededException; 22 | import org.springframework.batch.core.step.skip.SkipPolicy; 23 | 24 | /** 25 | * SkipJobPolicy. 26 | * 27 | * @author Michael R. Lange 28 | */ 29 | public class SkipJobPolicy implements SkipPolicy { 30 | 31 | private final Logger LOG = LoggerFactory.getLogger(this.getClass()); 32 | private int skipLimit; 33 | 34 | @Override 35 | public boolean shouldSkip(Throwable t, int skipCount) throws SkipLimitExceededException { 36 | LOG.debug("shouldSkip:" + t.toString()); 37 | if (t instanceof SkipJobCustomException && skipCount <= skipLimit) { 38 | return true; 39 | } else { 40 | return false; 41 | } 42 | } 43 | 44 | public void setSkipLimit(int skipLimit) { 45 | this.skipLimit = skipLimit; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/crosscutting/interstepcommunication/promotion/ChangingStepExecutionContextTasklet.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.crosscutting.interstepcommunication.promotion; 17 | 18 | import org.springframework.batch.core.StepContribution; 19 | import org.springframework.batch.core.scope.context.ChunkContext; 20 | import org.springframework.batch.core.step.tasklet.Tasklet; 21 | import org.springframework.batch.item.ExecutionContext; 22 | import org.springframework.batch.repeat.RepeatStatus; 23 | 24 | /** 25 | * Tasklet which accesses the (Step){@link ExecutionContext} directly to 26 | * set a value for a future step. 27 | * 28 | * @author Michael R. Lange 29 | * @see http://stackoverflow.com/questions/8117060/spring-batch-storing-in-jobexecutioncontext-from-tasklet-and-accessing-in-anot 30 | */ 31 | public class ChangingStepExecutionContextTasklet implements Tasklet { 32 | 33 | /** {@inheritDoc} */ 34 | @Override 35 | public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { 36 | // set variable in JobExecutionContext 37 | chunkContext.getStepContext().getStepExecution().getExecutionContext().put("value", "foo"); 38 | 39 | // exit the step 40 | return RepeatStatus.FINISHED; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/crosscutting/interstepcommunication/jobcontext/ChangingJobExecutionContextTasklet.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.crosscutting.interstepcommunication.jobcontext; 17 | 18 | import org.springframework.batch.core.StepContribution; 19 | import org.springframework.batch.core.scope.context.ChunkContext; 20 | import org.springframework.batch.core.step.tasklet.Tasklet; 21 | import org.springframework.batch.item.ExecutionContext; 22 | import org.springframework.batch.repeat.RepeatStatus; 23 | 24 | /** 25 | * Tasklet which accesses the (Job){@link ExecutionContext} directly to 26 | * set a value for a future step. 27 | * 28 | * @author Michael R. Lange 29 | * @see http://stackoverflow.com/questions/8117060/spring-batch-storing-in-jobexecutioncontext-from-tasklet-and-accessing-in-anot 30 | */ 31 | public class ChangingJobExecutionContextTasklet implements Tasklet { 32 | 33 | /** {@inheritDoc} */ 34 | @Override 35 | public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { 36 | // set variable in JobExecutionContext 37 | chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().put("value", "foo"); 38 | 39 | // exit the step 40 | return RepeatStatus.FINISHED; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/crosscutting/interstepcommunication/databean/ChangingDataBeanTasklet.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.crosscutting.interstepcommunication.databean; 17 | 18 | import java.util.Map; 19 | import org.springframework.batch.core.StepContribution; 20 | import org.springframework.batch.core.scope.context.ChunkContext; 21 | import org.springframework.batch.core.step.tasklet.Tasklet; 22 | import org.springframework.batch.repeat.RepeatStatus; 23 | 24 | /** 25 | * Tasklet which accesses a (Spring) Bean to set a value for a future step. 26 | * 27 | * @author Michael R. Lange 28 | * @see http://stackoverflow.com/questions/8117060/spring-batch-storing-in-jobexecutioncontext-from-tasklet-and-accessing-in-anot 29 | */ 30 | public class ChangingDataBeanTasklet implements Tasklet { 31 | 32 | /** Databean. */ 33 | private Map dataMap; 34 | 35 | /** {@inheritDoc} */ 36 | @Override 37 | public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { 38 | // set variable in data bean 39 | dataMap.put("value", "foo"); 40 | 41 | // exit the step 42 | return RepeatStatus.FINISHED; 43 | } 44 | 45 | public void setDataMap(Map dataMap) { 46 | this.dataMap = dataMap; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/file/renamefile/simple/SimpleRenameFileTaskletStep.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.renamefile.simple; 17 | 18 | import org.springframework.batch.core.StepContribution; 19 | import org.springframework.batch.core.scope.context.ChunkContext; 20 | import org.springframework.batch.core.step.tasklet.Tasklet; 21 | import org.springframework.batch.repeat.RepeatStatus; 22 | import org.springframework.core.io.UrlResource; 23 | 24 | /** 25 | * SimpleRenameFileTaskletStep. 26 | * 27 | * @author Michael R. Lange 28 | */ 29 | public class SimpleRenameFileTaskletStep implements Tasklet { 30 | 31 | 32 | /** */ 33 | @Override 34 | public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { 35 | 36 | // get used output file 37 | String outputFilePath = (String) chunkContext.getStepContext().getJobParameters().get("output.file"); 38 | UrlResource oldFile = new UrlResource(outputFilePath); 39 | // get desired output file name 40 | String desiredOutputFilePath = (String) chunkContext.getStepContext().getJobExecutionContext().get("desired.output.file"); 41 | UrlResource newFile = new UrlResource(desiredOutputFilePath); 42 | 43 | // rename 44 | oldFile.getFile().renameTo(newFile.getFile()); 45 | 46 | return RepeatStatus.FINISHED; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/support/SimpleItem.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.support; 17 | 18 | /** 19 | * Simple Domain Class for Spring Batch Examples. 20 | * Has an own id and a shared id, which is meant as shared between many items, 21 | * which are somehow related. 22 | * 23 | * @author Michael R. Lange 24 | */ 25 | public class SimpleItem { 26 | 27 | private int id; 28 | private int sharedId; 29 | private int value; 30 | 31 | public SimpleItem() { 32 | } 33 | 34 | public SimpleItem(int id, int value) { 35 | this.id = id; 36 | this.value = value; 37 | } 38 | 39 | public SimpleItem(int id, int sharedId, int value) { 40 | this.id = id; 41 | this.sharedId = sharedId; 42 | this.value = value; 43 | } 44 | 45 | public int getId() { 46 | return id; 47 | } 48 | 49 | public void setId(int id) { 50 | this.id = id; 51 | } 52 | 53 | public int getSharedId() { 54 | return sharedId; 55 | } 56 | 57 | public void setSharedId(int sharedId) { 58 | this.sharedId = sharedId; 59 | } 60 | 61 | public int getValue() { 62 | return value; 63 | } 64 | 65 | public void setValue(int value) { 66 | this.value = value; 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return "SimpleItem{" + "id=" + id + ", sharedId=" + sharedId + ", value=" + value + '}'; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/aggregating/AggregatedItemWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.aggregating; 17 | 18 | import java.util.List; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import org.springframework.batch.core.ExitStatus; 22 | import org.springframework.batch.core.StepExecution; 23 | import org.springframework.batch.core.StepExecutionListener; 24 | import org.springframework.batch.item.ItemWriter; 25 | 26 | /** 27 | * ItemWriter for AggregatedItems. 28 | * 29 | * @author Michael R. Lange 30 | */ 31 | public class AggregatedItemWriter implements ItemWriter, StepExecutionListener { 32 | 33 | private final Logger LOG = LoggerFactory.getLogger(this.getClass()); 34 | private int count = 0; 35 | 36 | @Override 37 | public void write(List items) throws Exception { 38 | LOG.debug("writing:"); 39 | for (AggregatedItem item : items) { 40 | LOG.debug(item.toString()); 41 | count++; 42 | } 43 | } 44 | 45 | @Override 46 | public void beforeStep(StepExecution stepExecution) { 47 | LOG.debug("beforeStep"); 48 | // no-op 49 | } 50 | 51 | @Override 52 | public ExitStatus afterStep(StepExecution stepExecution) { 53 | LOG.debug("afterStep"); 54 | stepExecution.getExecutionContext().put("real.write.count", count); 55 | return stepExecution.getExitStatus(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/setup/general/job-database.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 22 | 23 | Job database setup. 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/crosscutting/interstepcommunication/databean/DatabeanInterStepCommunicationJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.crosscutting.interstepcommunication.databean; 17 | 18 | import static org.junit.Assert.*; 19 | import org.junit.Test; 20 | import org.junit.runner.RunWith; 21 | import org.springframework.batch.core.BatchStatus; 22 | import org.springframework.batch.core.JobExecution; 23 | import org.springframework.batch.test.JobLauncherTestUtils; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.test.context.ContextConfiguration; 26 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 27 | 28 | /** 29 | * JobConfigurationTest. 30 | * 31 | * @author Michael R. Lange 32 | */ 33 | @ContextConfiguration(locations = { 34 | "classpath*:spring/batch/job/complex/crosscutting/interstepcommunication/interstep-communication-databean-job.xml", 35 | "classpath*:spring/batch/setup/**/*.xml"}) 36 | @RunWith(SpringJUnit4ClassRunner.class) 37 | public class DatabeanInterStepCommunicationJobConfigurationTest { 38 | 39 | /** JobLauncherTestUtils Bean. */ 40 | @Autowired 41 | private JobLauncherTestUtils jobLauncherTestUtils; 42 | 43 | /** Launch Test. */ 44 | @Test 45 | public void launchJob() throws Exception { 46 | // launch the job 47 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(); 48 | 49 | // assert job run status 50 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/crosscutting/interstepcommunication/promotion/PromotionInterStepCommunicationJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.crosscutting.interstepcommunication.promotion; 17 | 18 | import static org.junit.Assert.*; 19 | import org.junit.Test; 20 | import org.junit.runner.RunWith; 21 | import org.springframework.batch.core.BatchStatus; 22 | import org.springframework.batch.core.JobExecution; 23 | import org.springframework.batch.test.JobLauncherTestUtils; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.test.context.ContextConfiguration; 26 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 27 | 28 | /** 29 | * JobConfigurationTest. 30 | * 31 | * @author Michael R. Lange 32 | */ 33 | @ContextConfiguration(locations = { 34 | "classpath*:spring/batch/job/complex/crosscutting/interstepcommunication/interstep-communication-promotion-job.xml", 35 | "classpath*:spring/batch/setup/**/*.xml"}) 36 | @RunWith(SpringJUnit4ClassRunner.class) 37 | public class PromotionInterStepCommunicationJobConfigurationTest { 38 | 39 | /** JobLauncherTestUtils Bean. */ 40 | @Autowired 41 | private JobLauncherTestUtils jobLauncherTestUtils; 42 | 43 | /** Launch Test. */ 44 | @Test 45 | public void launchJob() throws Exception { 46 | // launch the job 47 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(); 48 | 49 | // assert job run status 50 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/crosscutting/interstepcommunication/jobcontext/JobContextInterStepCommunicationJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.crosscutting.interstepcommunication.jobcontext; 17 | 18 | import static org.junit.Assert.*; 19 | import org.junit.Test; 20 | import org.junit.runner.RunWith; 21 | import org.springframework.batch.core.BatchStatus; 22 | import org.springframework.batch.core.JobExecution; 23 | import org.springframework.batch.test.JobLauncherTestUtils; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.test.context.ContextConfiguration; 26 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 27 | 28 | /** 29 | * JobConfigurationTest. 30 | * 31 | * @author Michael R. Lange 32 | */ 33 | @ContextConfiguration(locations = { 34 | "classpath*:spring/batch/job/complex/crosscutting/interstepcommunication/interstep-communication-jobcontext-job.xml", 35 | "classpath*:spring/batch/setup/**/*.xml"}) 36 | @RunWith(SpringJUnit4ClassRunner.class) 37 | public class JobContextInterStepCommunicationJobConfigurationTest { 38 | 39 | /** JobLauncherTestUtils Bean. */ 40 | @Autowired 41 | private JobLauncherTestUtils jobLauncherTestUtils; 42 | 43 | /** Launch Test. */ 44 | @Test 45 | public void launchJob() throws Exception { 46 | // launch the job 47 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(); 48 | 49 | // assert job run status 50 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/aggregating/AggregatingTestDataSimpleItemsFactoryBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.aggregating; 17 | 18 | import de.langmi.spring.batch.examples.complex.support.*; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import org.springframework.beans.factory.FactoryBean; 22 | 23 | /** 24 | * Provides List with test data, creates items in sets with an identical shared id. 25 | * 26 | * @author Michael R. Lange 27 | */ 28 | public class AggregatingTestDataSimpleItemsFactoryBean implements FactoryBean> { 29 | 30 | /** Public to make it usable for test assertions. */ 31 | private int itemCount = 20; 32 | private int countDivisor = 2; 33 | private int aggregatedItemCount; 34 | 35 | @Override 36 | public List getObject() throws Exception { 37 | List data = new ArrayList(); 38 | for (int i = 0; i < itemCount; i++) { 39 | // each duo of items has the same shared id 40 | data.add(new SimpleItem(i, i / countDivisor, 1)); 41 | } 42 | 43 | aggregatedItemCount = itemCount / countDivisor; 44 | 45 | return data; 46 | } 47 | 48 | public int getAggregatedCount() { 49 | return aggregatedItemCount; 50 | } 51 | 52 | public int getItemCount() { 53 | return itemCount; 54 | } 55 | 56 | @Override 57 | public Class getObjectType() { 58 | return List.class; 59 | } 60 | 61 | @Override 62 | public boolean isSingleton() { 63 | return true; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/file/renamefile/partition/HeaderLineCallbackHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.renamefile.partition; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import org.springframework.batch.core.ExitStatus; 21 | import org.springframework.batch.core.StepExecution; 22 | import org.springframework.batch.core.StepExecutionListener; 23 | import org.springframework.batch.item.file.LineCallbackHandler; 24 | 25 | /** 26 | * HeaderLineCallbackHandler handles header line from file. 27 | * Is not threadsafe, use with scope="step". 28 | * 29 | * @author Michael R. Lange 30 | */ 31 | public class HeaderLineCallbackHandler implements LineCallbackHandler, StepExecutionListener { 32 | 33 | private StepExecution stepExecution; 34 | private final Logger LOG = LoggerFactory.getLogger(this.getClass()); 35 | 36 | /** 37 | * Handles header line and saves business key to step execution context. 38 | * 39 | * @param line 40 | */ 41 | @Override 42 | public void handleLine(String line) { 43 | // promote line as business key for output file 44 | stepExecution.getExecutionContext().put(BatchConstants.CONTEXT_NAME_BUSINESS_KEY, line); 45 | LOG.info("business key created:'" + line + "'"); 46 | } 47 | 48 | @Override 49 | public void beforeStep(StepExecution stepExecution) { 50 | this.stepExecution = stepExecution; 51 | } 52 | 53 | @Override 54 | public ExitStatus afterStep(StepExecution stepExecution) { 55 | return stepExecution.getExitStatus(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/file/renamefile/partition/extrastep/PropagateFileNamesListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.renamefile.partition.extrastep; 17 | 18 | import de.langmi.spring.batch.examples.complex.file.renamefile.partition.BatchConstants; 19 | import java.util.concurrent.ConcurrentHashMap; 20 | import org.springframework.batch.core.ExitStatus; 21 | import org.springframework.batch.core.StepExecution; 22 | import org.springframework.batch.core.StepExecutionListener; 23 | 24 | /** 25 | * PropagateFileNamesListener puts the current business key for the future 26 | * desired output file and the current output file name in a HashMap bean. 27 | * 28 | * Is not threadsafe ! Use with scope="step". 29 | * 30 | * @author Michael R. Lange 31 | */ 32 | public class PropagateFileNamesListener implements StepExecutionListener { 33 | 34 | private ConcurrentHashMap fileNames; 35 | private String outputFile; 36 | 37 | @Override 38 | public ExitStatus afterStep(StepExecution stepExecution) { 39 | // get business key 40 | String businessKey = (String) stepExecution.getExecutionContext().get(BatchConstants.CONTEXT_NAME_BUSINESS_KEY); 41 | this.fileNames.put(outputFile, businessKey); 42 | 43 | return stepExecution.getExitStatus(); 44 | } 45 | 46 | @Override 47 | public void beforeStep(StepExecution stepExecution) { 48 | // no-op 49 | } 50 | 51 | public void setFileNames(ConcurrentHashMap fileNames) { 52 | this.fileNames = fileNames; 53 | } 54 | 55 | public void setOutputFile(String outputFile) { 56 | this.outputFile = outputFile; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/setup/general/job-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | Spring Batch infrastructure beans. 23 | 24 | 27 | 28 | 30 | 31 | 32 | 33 | 35 | 36 | 38 | 39 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/crosscutting/interstepcommunication/databean/ReadingDataBeanTasklet.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.crosscutting.interstepcommunication.databean; 17 | 18 | import java.util.Map; 19 | import org.apache.commons.lang.StringUtils; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | import org.springframework.batch.core.StepContribution; 23 | import org.springframework.batch.core.scope.context.ChunkContext; 24 | import org.springframework.batch.core.step.tasklet.Tasklet; 25 | import org.springframework.batch.repeat.RepeatStatus; 26 | 27 | /** 28 | * Tasklet which accesses a (Spring) Bean read a value. 29 | * 30 | * @author Michael R. Lange 31 | * @see http://stackoverflow.com/questions/8117060/spring-batch-storing-in-jobexecutioncontext-from-tasklet-and-accessing-in-anot 32 | */ 33 | public class ReadingDataBeanTasklet implements Tasklet { 34 | 35 | private static final Logger LOG = LoggerFactory.getLogger(ChangingDataBeanTasklet.class); 36 | /** Databean. */ 37 | private Map dataMap; 38 | 39 | /** {@inheritDoc} */ 40 | @Override 41 | public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { 42 | // pull variable from data bean 43 | String value = (String) dataMap.get("value"); 44 | 45 | // check for the value 46 | if (StringUtils.trimToNull(value) != null) { 47 | LOG.debug("Read value from databean:" + value); 48 | } else { 49 | throw new Exception("Did not found value in data bean"); 50 | } 51 | 52 | // exit the step 53 | return RepeatStatus.FINISHED; 54 | } 55 | 56 | public void setDataMap(Map dataMap) { 57 | this.dataMap = dataMap; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/file/split/SplitFilesItemWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.split; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import org.springframework.batch.item.ItemWriter; 21 | 22 | /** 23 | * A simple {@link ItemWriter} which splits all items in half and writes each 24 | * half to an ItemWriter. 25 | * 26 | * @author Michael R. Lange 27 | */ 28 | public class SplitFilesItemWriter implements ItemWriter { 29 | 30 | private ItemWriter firstWriter; 31 | private ItemWriter secondWriter; 32 | private int inputLineCount; 33 | private int readCount = 0; 34 | 35 | @Override 36 | public void write(List items) throws Exception { 37 | List itemsFirst = new ArrayList(); 38 | List itemsSecond = new ArrayList(); 39 | 40 | for (String item : items) { 41 | // catch all items 42 | if (readCount < (inputLineCount / 2)) { 43 | itemsFirst.add(item); 44 | } else { 45 | itemsSecond.add(item); 46 | } 47 | readCount++; 48 | } 49 | if (itemsFirst.size() > 0) { 50 | firstWriter.write(itemsFirst); 51 | } 52 | if (itemsSecond.size() > 0) { 53 | secondWriter.write(itemsSecond); 54 | } 55 | } 56 | 57 | public void setInputLineCount(int inputLineCount) { 58 | this.inputLineCount = inputLineCount; 59 | } 60 | 61 | public void setFirstWriter(ItemWriter firstWriter) { 62 | this.firstWriter = firstWriter; 63 | } 64 | 65 | public void setSecondWriter(ItemWriter secondWriter) { 66 | this.secondWriter = secondWriter; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/skip/SkipSimpleJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.skip; 17 | 18 | import de.langmi.spring.batch.examples.complex.support.TestDataStringsFactoryBean; 19 | import static org.junit.Assert.*; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.springframework.batch.core.BatchStatus; 23 | import org.springframework.batch.core.JobExecution; 24 | import org.springframework.batch.core.StepExecution; 25 | import org.springframework.batch.test.JobLauncherTestUtils; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.test.context.ContextConfiguration; 28 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 29 | 30 | /** 31 | * JobConfigurationTest. 32 | * 33 | * @author Michael R. Lange 34 | */ 35 | @ContextConfiguration(locations = { 36 | "classpath*:spring/batch/job/complex/skip/skip-simple-job.xml", 37 | "classpath*:spring/batch/setup/**/*.xml"}) 38 | @RunWith(SpringJUnit4ClassRunner.class) 39 | public class SkipSimpleJobConfigurationTest { 40 | 41 | /** JobLauncherTestUtils Bean. */ 42 | @Autowired 43 | private JobLauncherTestUtils jobLauncherTestUtils; 44 | 45 | /** Launch Test. */ 46 | @Test 47 | public void launchJob() throws Exception { 48 | 49 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobLauncherTestUtils.getUniqueJobParameters()); 50 | 51 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 52 | 53 | // output step summaries 54 | for (StepExecution step : jobExecution.getStepExecutions()) { 55 | assertTrue("Read Count mismatch.", 56 | step.getReadCount() == TestDataStringsFactoryBean.COUNT); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/crosscutting/interstepcommunication/jobcontext/ReadingJobExecutionContextTasklet.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.crosscutting.interstepcommunication.jobcontext; 17 | 18 | import org.apache.commons.lang.StringUtils; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import org.springframework.batch.core.StepContribution; 22 | import org.springframework.batch.core.scope.context.ChunkContext; 23 | import org.springframework.batch.core.step.tasklet.Tasklet; 24 | import org.springframework.batch.item.ExecutionContext; 25 | import org.springframework.batch.repeat.RepeatStatus; 26 | 27 | /** 28 | * Tasklet which accesses the (Job){@link ExecutionContext} directly to 29 | * read a value. 30 | * 31 | * @author Michael R. Lange 32 | * @see http://stackoverflow.com/questions/8117060/spring-batch-storing-in-jobexecutioncontext-from-tasklet-and-accessing-in-anot 33 | */ 34 | public class ReadingJobExecutionContextTasklet implements Tasklet { 35 | 36 | private static final Logger LOG = LoggerFactory.getLogger(ChangingJobExecutionContextTasklet.class); 37 | 38 | /** {@inheritDoc} */ 39 | @Override 40 | public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { 41 | // pull variable from JobExecutionContext, accesses a unmodifiable map 42 | String value = (String) chunkContext.getStepContext().getJobExecutionContext().get("value"); 43 | 44 | // check for the value 45 | if (StringUtils.trimToNull(value) != null) { 46 | LOG.debug("Found value in JobExecutionContext:" + value); 47 | } else { 48 | throw new Exception("Did not found value in JobExecutionContext"); 49 | } 50 | 51 | // exit the step 52 | return RepeatStatus.FINISHED; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/skip/SkipPolicySimpleJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.skip; 17 | 18 | import de.langmi.spring.batch.examples.complex.support.TestDataStringsFactoryBean; 19 | import static org.junit.Assert.*; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.springframework.batch.core.BatchStatus; 23 | import org.springframework.batch.core.JobExecution; 24 | import org.springframework.batch.core.StepExecution; 25 | import org.springframework.batch.test.JobLauncherTestUtils; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.test.context.ContextConfiguration; 28 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 29 | 30 | /** 31 | * JobConfigurationTest. 32 | * 33 | * @author Michael R. Lange 34 | */ 35 | @ContextConfiguration(locations = { 36 | "classpath*:spring/batch/job/complex/skip/skip-policy-simple-job.xml", 37 | "classpath*:spring/batch/setup/**/*.xml"}) 38 | @RunWith(SpringJUnit4ClassRunner.class) 39 | public class SkipPolicySimpleJobConfigurationTest { 40 | 41 | /** JobLauncherTestUtils Bean. */ 42 | @Autowired 43 | private JobLauncherTestUtils jobLauncherTestUtils; 44 | 45 | /** Launch Test. */ 46 | @Test 47 | public void launchJob() throws Exception { 48 | // launch 49 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobLauncherTestUtils.getUniqueJobParameters()); 50 | 51 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 52 | 53 | // output step summaries 54 | for (StepExecution step : jobExecution.getStepExecutions()) { 55 | assertTrue("Read Count mismatch.", 56 | step.getReadCount() == TestDataStringsFactoryBean.COUNT); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/crosscutting/interstepcommunication/promotion/ReadingJobExecutionContextTasklet.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.crosscutting.interstepcommunication.promotion; 17 | 18 | import de.langmi.spring.batch.examples.complex.crosscutting.interstepcommunication.jobcontext.*; 19 | import org.apache.commons.lang.StringUtils; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | import org.springframework.batch.core.StepContribution; 23 | import org.springframework.batch.core.scope.context.ChunkContext; 24 | import org.springframework.batch.core.step.tasklet.Tasklet; 25 | import org.springframework.batch.item.ExecutionContext; 26 | import org.springframework.batch.repeat.RepeatStatus; 27 | 28 | /** 29 | * Tasklet which accesses the (Job){@link ExecutionContext} directly to 30 | * read a value. 31 | * 32 | * @author Michael R. Lange 33 | * @see http://stackoverflow.com/questions/8117060/spring-batch-storing-in-jobexecutioncontext-from-tasklet-and-accessing-in-anot 34 | */ 35 | public class ReadingJobExecutionContextTasklet implements Tasklet { 36 | 37 | private static final Logger LOG = LoggerFactory.getLogger(ChangingJobExecutionContextTasklet.class); 38 | 39 | /** {@inheritDoc} */ 40 | @Override 41 | public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { 42 | // pull variable from JobExecutionContext, accesses a unmodifiable map 43 | String value = (String) chunkContext.getStepContext().getJobExecutionContext().get("value"); 44 | 45 | // check for the value 46 | if (StringUtils.trimToNull(value) != null) { 47 | LOG.debug("Found value in JobExecutionContext:" + value); 48 | } else { 49 | throw new Exception("Did not found value in JobExecutionContext"); 50 | } 51 | 52 | // exit the step 53 | return RepeatStatus.FINISHED; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/crosscutting/interstepcommunication/interstep-communication-jobcontext-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 20 | 21 | 22 | Simple inter step communication example with: 23 | 24 | * 2 Taskletsteps 25 | * both accessing the JobExecutionContext directly 26 | 27 | Please use this with caution, it will cause thread problems for 28 | parallel steps. 29 | 30 | created for answering: 31 | http://stackoverflow.com/questions/8117060/spring-batch-storing-in-jobexecutioncontext-from-tasklet-and-accessing-in-anot 32 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 48 | 49 | 51 | 52 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/aggregating/reader/AggregatingItemsInReaderJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.aggregating.reader; 17 | 18 | import de.langmi.spring.batch.examples.complex.aggregating.AggregatingTestDataSimpleItemsFactoryBean; 19 | import static org.junit.Assert.*; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.springframework.batch.core.BatchStatus; 23 | import org.springframework.batch.core.JobExecution; 24 | import org.springframework.batch.core.StepExecution; 25 | import org.springframework.batch.test.JobLauncherTestUtils; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.test.context.ContextConfiguration; 28 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 29 | 30 | /** 31 | * JobConfigurationTest. 32 | * 33 | * @author Michael R. Lange 34 | */ 35 | @ContextConfiguration(locations = { 36 | "classpath*:spring/batch/job/complex/aggregating/aggregating-items-in-reader-job.xml", 37 | "classpath*:spring/batch/setup/**/*.xml"}) 38 | @RunWith(SpringJUnit4ClassRunner.class) 39 | public class AggregatingItemsInReaderJobConfigurationTest { 40 | 41 | /** JobLauncherTestUtils Bean. */ 42 | @Autowired 43 | private JobLauncherTestUtils jobLauncherTestUtils; 44 | @Autowired 45 | private AggregatingTestDataSimpleItemsFactoryBean testDataFactoryBean; 46 | 47 | /** Launch Test. */ 48 | @Test 49 | public void launchJob() throws Exception { 50 | 51 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobLauncherTestUtils.getUniqueJobParameters()); 52 | 53 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 54 | 55 | // output step summaries 56 | for (StepExecution step : jobExecution.getStepExecutions()) { 57 | assertEquals("Read Count mismatch.", testDataFactoryBean.getAggregatedCount(), 58 | step.getReadCount()); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/file/renamefile/RenameFileSimpleJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.renamefile; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import static org.junit.Assert.*; 21 | import org.junit.Test; 22 | import org.junit.runner.RunWith; 23 | import org.springframework.batch.core.BatchStatus; 24 | import org.springframework.batch.core.JobExecution; 25 | import org.springframework.batch.core.JobParameter; 26 | import org.springframework.batch.core.JobParameters; 27 | import org.springframework.batch.test.JobLauncherTestUtils; 28 | import org.springframework.beans.factory.annotation.Autowired; 29 | import org.springframework.test.context.ContextConfiguration; 30 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 31 | 32 | /** 33 | * JobConfigurationTest. 34 | * 35 | * @author Michael R. Lange 36 | */ 37 | @ContextConfiguration(locations = { 38 | "classpath*:spring/batch/job/complex/file/renamefile/rename-file-simple-job.xml", 39 | "classpath*:spring/batch/setup/**/*.xml"}) 40 | @RunWith(SpringJUnit4ClassRunner.class) 41 | public class RenameFileSimpleJobConfigurationTest { 42 | 43 | /** JobLauncherTestUtils Bean. */ 44 | @Autowired 45 | private JobLauncherTestUtils jobLauncherTestUtils; 46 | 47 | /** Launch Test. */ 48 | @Test 49 | public void launchJob() throws Exception { 50 | // Job parameters 51 | Map jobParametersMap = new HashMap(); 52 | jobParametersMap.put("time", new JobParameter(System.currentTimeMillis())); 53 | jobParametersMap.put("output.file", new JobParameter("file:target/test-outputs/rename-file-simple/output.txt")); 54 | 55 | // launch the job 56 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(new JobParameters(jobParametersMap)); 57 | 58 | // assert job run status 59 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/crosscutting/interstepcommunication/interstep-communication-databean-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | Simple inter step communication example with: 24 | 25 | * 2 Taskletsteps 26 | * both accessing a data bean 27 | 28 | created for answering: 29 | http://stackoverflow.com/questions/8117060/spring-batch-storing-in-jobexecutioncontext-from-tasklet-and-accessing-in-anot 30 | 31 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/skip/skip-policy-simple-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 20 | 21 | 22 | Simple Skip Job Example - with skip policy instead of skip-limit 23 | and skippable-exceptions 24 | 25 | 29 | 30 | 31 | 32 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/aggregating/reader/AggregateSimpleItemsReader.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.aggregating.reader; 17 | 18 | import de.langmi.spring.batch.examples.complex.aggregating.AggregatedItem; 19 | import de.langmi.spring.batch.examples.complex.support.SimpleItem; 20 | import org.springframework.batch.item.ItemReader; 21 | 22 | /** 23 | * AggregateSimpleItemsReader wraps another reader, aggregates the data and creates 24 | * an AggregatedItem. 25 | * 26 | * Is stateful. 27 | * 28 | * @author Michael R. Lange 29 | * @see http://stackoverflow.com/questions/8837487/how-to-process-logically-related-rows-after-itemreader-in-springbatch 30 | */ 31 | public class AggregateSimpleItemsReader implements ItemReader { 32 | 33 | /** Wrapped reaer. */ 34 | private ItemReader delegate; 35 | private AggregatedItem tempData = null; 36 | private boolean isReaderExhausted = false; 37 | 38 | @Override 39 | public AggregatedItem read() throws Exception { 40 | AggregatedItem returnValue = null; 41 | // read delegate until one AggregatedItem is complete or reader is exhausted 42 | while (!isReaderExhausted) { 43 | SimpleItem sItem = delegate.read(); 44 | 45 | if (sItem != null) { 46 | // first run? 47 | if (tempData == null) { 48 | tempData = new AggregatedItem(sItem.getSharedId()); 49 | } 50 | // same shared id ? add value 51 | if (tempData.getId() == sItem.getSharedId()) { 52 | tempData.add(sItem.getValue()); 53 | } else { 54 | // set returnvalue 55 | returnValue = new AggregatedItem(tempData.getId(), tempData.getSum()); 56 | // create new tempData 57 | tempData = new AggregatedItem(sItem.getSharedId(), sItem.getValue()); 58 | // break 59 | break; 60 | } 61 | } else { 62 | returnValue = tempData; 63 | isReaderExhausted = true; 64 | } 65 | } 66 | return returnValue; 67 | } 68 | 69 | public void setDelegate(ItemReader delegate) { 70 | this.delegate = delegate; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/crosscutting/interstepcommunication/interstep-communication-promotion-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | Simple inter step communication example with: 24 | 25 | * 2 Taskletsteps 26 | * ExecutionContextPromotionListener which promotes the StepExecutionContext 27 | data from the first step 28 | * configured strict=true to force checking for the correct value name 29 | 30 | created for answering: 31 | http://stackoverflow.com/questions/8117060/spring-batch-storing-in-jobexecutioncontext-from-tasklet-and-accessing-in-anot 32 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/aggregating/writer/ReaderExhaustedWrapper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.aggregating.writer; 17 | 18 | import org.springframework.batch.core.ExitStatus; 19 | import org.springframework.batch.core.StepExecution; 20 | import org.springframework.batch.core.StepExecutionListener; 21 | import org.springframework.batch.item.ItemReader; 22 | 23 | /** 24 | * A Reader Implementation for 25 | * http://stackoverflow.com/questions/8837487/how-to-process-logically-related-rows-after-itemreader-in-springbatch. 26 | * 27 | * Wraps another reader and peeks ahead for each read to know beforehand if the 28 | * delegate reader is exhausted. Propagates the information to the StepExecutionContext. 29 | * 30 | * Is stateful and not threadsafe. 31 | * 32 | * @author Michael R. Lange 33 | */ 34 | public class ReaderExhaustedWrapper implements ItemReader, StepExecutionListener { 35 | 36 | private ItemReader delegate; 37 | private StepExecution stepExecution; 38 | private T nextItem = null; 39 | private boolean delegateExhausted = false; 40 | 41 | @Override 42 | public T read() throws Exception { 43 | // check if delegate already exhausted to avoid an unnecessary delegate.read() 44 | if (!delegateExhausted) { 45 | T returnItem; 46 | // next filled ? 47 | if (nextItem != null) { 48 | returnItem = nextItem; 49 | nextItem = null; 50 | } else { 51 | // standard read 52 | returnItem = delegate.read(); 53 | } 54 | 55 | // try to peek one item ahead 56 | nextItem = delegate.read(); 57 | // last item reached? 58 | if (nextItem == null) { 59 | stepExecution.getExecutionContext().put("readerExhausted", Boolean.TRUE); 60 | delegateExhausted = true; 61 | } 62 | 63 | return returnItem; 64 | } else { 65 | return null; 66 | } 67 | } 68 | 69 | public void setDelegate(ItemReader delegate) { 70 | this.delegate = delegate; 71 | } 72 | 73 | @Override 74 | public void beforeStep(StepExecution stepExecution) { 75 | this.stepExecution = stepExecution; 76 | } 77 | 78 | @Override 79 | public ExitStatus afterStep(StepExecution stepExecution) { 80 | return stepExecution.getExitStatus(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/aggregating/writer/AggregatingItemsInWriterJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.aggregating.writer; 17 | 18 | import de.langmi.spring.batch.examples.complex.aggregating.AggregatingTestDataSimpleItemsFactoryBean; 19 | import static org.junit.Assert.*; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.springframework.batch.core.BatchStatus; 23 | import org.springframework.batch.core.JobExecution; 24 | import org.springframework.batch.core.StepExecution; 25 | import org.springframework.batch.test.JobLauncherTestUtils; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.test.context.ContextConfiguration; 28 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 29 | 30 | /** 31 | * JobConfigurationTest. 32 | * 33 | * @author Michael R. Lange 34 | */ 35 | @ContextConfiguration(locations = { 36 | "classpath*:spring/batch/job/complex/aggregating/aggregating-items-in-writer-job.xml", 37 | "classpath*:spring/batch/setup/**/*.xml"}) 38 | @RunWith(SpringJUnit4ClassRunner.class) 39 | public class AggregatingItemsInWriterJobConfigurationTest { 40 | 41 | /** JobLauncherTestUtils Bean. */ 42 | @Autowired 43 | private JobLauncherTestUtils jobLauncherTestUtils; 44 | @Autowired 45 | private AggregatingTestDataSimpleItemsFactoryBean testDataFactoryBean; 46 | 47 | /** Launch Test. */ 48 | @Test 49 | public void launchJob() throws Exception { 50 | 51 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobLauncherTestUtils.getUniqueJobParameters()); 52 | 53 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 54 | 55 | // output step summaries 56 | for (StepExecution step : jobExecution.getStepExecutions()) { 57 | assertEquals( 58 | "Read count mismatch.", 59 | testDataFactoryBean.getItemCount(), 60 | step.getReadCount()); 61 | assertEquals( 62 | "Write count mismatch.", 63 | testDataFactoryBean.getItemCount(), 64 | step.getWriteCount()); 65 | assertEquals( 66 | "Real write count mismatch", 67 | testDataFactoryBean.getAggregatedCount(), 68 | step.getExecutionContext().getInt("real.write.count")); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/aggregating/aggregating-items-in-writer-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 20 | 21 | 22 | Job in which the Itemwriter aggregates items. 23 | Works with a wrapper reader which peeks ahead to know if the delegate reader 24 | is exhausted. 25 | 26 | for http://stackoverflow.com/questions/8837487/how-to-process-logically-related-rows-after-itemreader-in-springbatch 27 | 28 | 32 | 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/skip/skip-simple-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 20 | 21 | 22 | Simple Skip Job Example - with "normal" skip configuration, 23 | skip-limit and skippable-exception-classes 24 | 25 | 29 | 30 | 31 | 32 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/file/split/GetLineCountTaskletTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.split; 17 | 18 | import org.springframework.batch.core.StepContribution; 19 | import java.io.FileNotFoundException; 20 | import org.junit.Test; 21 | import org.springframework.batch.core.StepExecution; 22 | import org.springframework.batch.core.scope.context.ChunkContext; 23 | import org.springframework.batch.core.scope.context.StepContext; 24 | import org.springframework.batch.repeat.RepeatStatus; 25 | import org.springframework.batch.test.MetaDataInstanceFactory; 26 | import org.springframework.core.io.FileSystemResource; 27 | import static org.junit.Assert.*; 28 | 29 | /** 30 | * Tests for {@link GetLineCountTasklet}. 31 | * 32 | * @author Michael R. Lange 33 | */ 34 | public class GetLineCountTaskletTest { 35 | 36 | private static String INPUT = "src/test/resources/input/simple/input.txt"; 37 | private static final int EXPECTED_COUNT = 20; 38 | private GetLineCountTasklet tasklet; 39 | 40 | @Test 41 | public void testExecute() throws Exception { 42 | // setup 43 | tasklet = new GetLineCountTasklet(); 44 | tasklet.setResource(new FileSystemResource(INPUT)); 45 | StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution(); 46 | 47 | // execute 48 | RepeatStatus status = tasklet.execute(new StepContribution(stepExecution), new ChunkContext(new StepContext(stepExecution))); 49 | // assertions 50 | assertEquals(RepeatStatus.FINISHED, status); 51 | assertEquals(EXPECTED_COUNT, stepExecution.getExecutionContext().get("line.count")); 52 | } 53 | 54 | @Test 55 | public void testExecuteWithNull() throws Exception { 56 | // setup 57 | tasklet = new GetLineCountTasklet(); 58 | tasklet.setResource(null); 59 | 60 | // execute 61 | try { 62 | tasklet.execute(null, null); 63 | } catch (RuntimeException e) { 64 | assertTrue(e.getMessage().contains("Resource was null")); 65 | } 66 | } 67 | 68 | @Test 69 | public void testExecuteFileDoesNotExist() throws Exception { 70 | // setup 71 | tasklet = new GetLineCountTasklet(); 72 | tasklet.setResource(new FileSystemResource("foobar.txt")); 73 | 74 | // execute 75 | try { 76 | tasklet.execute(null, null); 77 | } catch (Exception e) { 78 | assertTrue(e instanceof FileNotFoundException); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/file/renamefile/partition/extrastep/RenameFilesTasklet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.renamefile.partition.extrastep; 17 | 18 | import java.io.File; 19 | import java.util.Map.Entry; 20 | import java.util.concurrent.ConcurrentHashMap; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | import org.springframework.batch.core.StepContribution; 24 | import org.springframework.batch.core.scope.context.ChunkContext; 25 | import org.springframework.batch.core.step.tasklet.Tasklet; 26 | import org.springframework.batch.repeat.RepeatStatus; 27 | 28 | /** 29 | * Works with value map to rename files according to business keys. 30 | * 31 | * @author Michael R. Lange 32 | */ 33 | public class RenameFilesTasklet implements Tasklet { 34 | 35 | private ConcurrentHashMap fileNames; 36 | private final Logger LOG = LoggerFactory.getLogger(this.getClass()); 37 | private static final String FILE_PREFIX = "file:"; 38 | 39 | @Override 40 | public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { 41 | for (Entry entry : fileNames.entrySet()) { 42 | String oldFileName = entry.getKey(); 43 | // remove "file:" part, sometimes there due to spring resource patterns 44 | if (oldFileName.contains(FILE_PREFIX)) { 45 | oldFileName = oldFileName.replace(FILE_PREFIX, ""); 46 | } 47 | // old file name 48 | File oldFile = new File(oldFileName); 49 | String path = oldFile.getParent(); 50 | String newFilePathAndName = createOutputFileName(path, entry); 51 | 52 | // rename file 53 | File newFile = new File(newFilePathAndName); 54 | oldFile.renameTo(newFile); 55 | 56 | LOG.info("renamed:" + oldFile.getPath() + " to:" + newFilePathAndName); 57 | } 58 | 59 | return RepeatStatus.FINISHED; 60 | } 61 | 62 | /** 63 | * Takes the old name and the business key to create a new output file name. 64 | * 65 | * @param path 66 | * @param entry 67 | * @return 68 | */ 69 | private String createOutputFileName(String path, Entry entry) { 70 | 71 | StringBuilder sb = new StringBuilder(path); 72 | sb.append(File.separator); 73 | sb.append(entry.getValue()); 74 | sb.append(".txt"); 75 | 76 | return sb.toString(); 77 | } 78 | 79 | public void setFileNames(ConcurrentHashMap fileNames) { 80 | this.fileNames = fileNames; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/file/renamefile/partition/RenameFileListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.renamefile.partition; 17 | 18 | import java.io.File; 19 | import org.apache.commons.io.FileUtils; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | import org.springframework.batch.core.ExitStatus; 23 | import org.springframework.batch.core.StepExecution; 24 | import org.springframework.batch.core.StepExecutionListener; 25 | import org.springframework.core.io.Resource; 26 | 27 | /** 28 | * RenameFileListener - renames outputFileResource to a new name with relation 29 | * to a stepcontext-provided business key. Is not threadsafe! Use with scope="step". 30 | * 31 | * The renaming is actually a "copy and delete old file later" implementation. 32 | * Real renaming does not work inside one step, due to spring batch calling 33 | * stream.close after 'afterStep'. It would work with unix/linux operation systems, 34 | * but not with windows. 35 | * So i use file.deleteOnExit which works on all operation systems. 36 | * 37 | * @author Michael R. Lange 38 | */ 39 | public class RenameFileListener implements StepExecutionListener { 40 | 41 | private Resource outputFileResource; 42 | private final Logger LOG = LoggerFactory.getLogger(this.getClass()); 43 | 44 | @Override 45 | public void beforeStep(StepExecution stepExecution) { 46 | // no-op 47 | } 48 | 49 | @Override 50 | public ExitStatus afterStep(StepExecution stepExecution) { 51 | // get business key 52 | String businessKey = (String) stepExecution.getExecutionContext().get(BatchConstants.CONTEXT_NAME_BUSINESS_KEY); 53 | try { 54 | String path = this.outputFileResource.getFile().getParent(); 55 | String newFilePathAndName = path + File.separator + businessKey + ".txt"; 56 | FileUtils.copyFile(outputFileResource.getFile(), new File(newFilePathAndName)); 57 | LOG.info("copied:" + this.outputFileResource.getFile().getPath() + " to:" + newFilePathAndName); 58 | // deletion here is not good, the itemstream will be closed after 59 | // this afterStep method is called 60 | // so get it deleted on jvm exit 61 | this.outputFileResource.getFile().deleteOnExit(); 62 | LOG.info("deleteOnExit for:" + this.outputFileResource.getFile().getPath()); 63 | } catch (Exception ex) { 64 | return new ExitStatus(ExitStatus.FAILED.getExitCode(), ex.getMessage()); 65 | } 66 | 67 | return stepExecution.getExitStatus(); 68 | } 69 | 70 | public void setOutputFileResource(Resource outputFileResource) { 71 | this.outputFileResource = outputFileResource; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/file/split/SplitFilesItemWriterTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.split; 17 | 18 | import org.junit.runner.RunWith; 19 | import org.junit.runners.Parameterized; 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.List; 23 | import org.junit.Test; 24 | import org.junit.runners.Parameterized.Parameters; 25 | import org.springframework.batch.item.ItemWriter; 26 | import static org.junit.Assert.*; 27 | 28 | /** 29 | * Tests for {@link SplitFilesItemWriter}. 30 | * 31 | * @author Michael R. Lange 32 | */ 33 | @RunWith(Parameterized.class) 34 | public class SplitFilesItemWriterTest { 35 | 36 | @Parameters 37 | public static List testData() { 38 | return Arrays.asList(new Object[][]{ 39 | {0}, {1}, {2}, {3}, {157}, {312}, {1099} 40 | }); 41 | } 42 | private int count; 43 | 44 | public SplitFilesItemWriterTest(int count) { 45 | this.count = count; 46 | } 47 | 48 | @Test 49 | public void testWrite() throws Exception { 50 | // setup 51 | SplitFilesItemWriter writer = new SplitFilesItemWriter(); 52 | ListItemWriter firstWriter = new ListItemWriter(); 53 | writer.setFirstWriter(firstWriter); 54 | ListItemWriter secondWriter = new ListItemWriter(); 55 | writer.setSecondWriter(secondWriter); 56 | writer.setInputLineCount(count); 57 | 58 | // write 59 | writer.write(getTestItems(count)); 60 | 61 | // assertions 62 | int firstHalf = count / 2; 63 | int secondHalf = count - firstHalf; 64 | assertEquals(firstHalf, firstWriter.getItems().size()); 65 | assertEquals(secondHalf, secondWriter.getItems().size()); 66 | 67 | } 68 | 69 | /** 70 | * Create item list for writer. 71 | * 72 | * @param count 73 | * @return 74 | */ 75 | private List getTestItems(int count) { 76 | List items = new ArrayList(); 77 | for (int i = 0; i < count; i++) { 78 | items.add(String.valueOf(i)); 79 | } 80 | return items; 81 | } 82 | 83 | /** 84 | * Helper ItemWriter Implementation. 85 | * 86 | * @param 87 | */ 88 | private class ListItemWriter implements ItemWriter { 89 | 90 | private List items = new ArrayList(); 91 | 92 | @Override 93 | public void write(List items) throws Exception { 94 | this.items.addAll(items); 95 | } 96 | 97 | public List getItems() { 98 | return items; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/jdbc/jdbc-generic-export-to-file-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | Example for generic export from database to (cvs)file. 24 | 25 | Job Flow: 26 | * read from database 27 | * sql provided via spring PropertyPlaceholderConfigurer mechanism 28 | * write to file 29 | * output file path provided via spring PropertyPlaceholderconfigurer mechanism 30 | 31 | 32 | 36 | 37 | 38 | 39 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/jdbc/jdbc-generic-export-to-database-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | Example for generic export from database(*) to another. 24 | 25 | Job Flow: 26 | 27 | * read from database 28 | * write to database 29 | * sql statements provided via spring PropertyPlaceholderConfigurer mechanism 30 | * example does not worked with named parameters, should not be too hard to extend it 31 | 32 | (*) In this example i use only tables, but it can be extended 33 | easily with 2 datasources to adress 2 real distinct databases. 34 | 35 | 36 | 40 | 41 | 42 | 43 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/aggregating/writer/AggregateSimpleItemsWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.aggregating.writer; 17 | 18 | import de.langmi.spring.batch.examples.complex.aggregating.AggregatedItem; 19 | import de.langmi.spring.batch.examples.complex.support.SimpleItem; 20 | import java.util.Arrays; 21 | import java.util.List; 22 | import org.springframework.batch.core.ExitStatus; 23 | import org.springframework.batch.core.StepExecution; 24 | import org.springframework.batch.core.StepExecutionListener; 25 | import org.springframework.batch.item.ItemWriter; 26 | 27 | /** 28 | * Itemwriter which aggregates items before delegating to another wrapped itemwriter. 29 | * Actual aggregation is controlled by an shared id of the used items. 30 | * Works only if the items come ordered in sets with same shared id. 31 | * 32 | * @author Michael R. Lange 33 | * @see http://stackoverflow.com/questions/8837487/how-to-process-logically-related-rows-after-itemreader-in-springbatch 34 | */ 35 | public class AggregateSimpleItemsWriter implements ItemWriter, StepExecutionListener { 36 | 37 | private ItemWriter delegate; 38 | private AggregatedItem tempData = null; 39 | private StepExecution stepExecution; 40 | private static final String KEY_READER_EXHAUSTED = "readerExhausted"; 41 | 42 | @Override 43 | public void write(List items) throws Exception { 44 | 45 | // setup with first sharedId at startup 46 | if (tempData == null) { 47 | tempData = new AggregatedItem(items.get(0).getSharedId()); 48 | } 49 | 50 | for (SimpleItem item : items) { 51 | // either actual known id, add to tempData 52 | if (item.getSharedId() == tempData.getId()) { 53 | tempData.add(item.getValue()); 54 | } else { 55 | // or new id, write tempData, empty it, keep new id 56 | delegate.write(Arrays.asList(tempData)); 57 | tempData = null; 58 | tempData = new AggregatedItem(item.getSharedId(), item.getValue()); 59 | } 60 | } 61 | 62 | // check if reader exhausted, flush tempData to delegate 63 | if (stepExecution.getExecutionContext().containsKey(KEY_READER_EXHAUSTED) 64 | && (Boolean) stepExecution.getExecutionContext().get(KEY_READER_EXHAUSTED) 65 | && tempData != null) { 66 | delegate.write(Arrays.asList(tempData)); 67 | } 68 | } 69 | 70 | public void setDelegate(ItemWriter delegate) { 71 | this.delegate = delegate; 72 | } 73 | 74 | @Override 75 | public void beforeStep(StepExecution stepExecution) { 76 | this.stepExecution = stepExecution; 77 | } 78 | 79 | @Override 80 | public ExitStatus afterStep(StepExecution stepExecution) { 81 | return stepExecution.getExitStatus(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/support/CustomMultiResourcePartitioner.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.support; 17 | 18 | import java.io.IOException; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | import org.springframework.batch.core.partition.support.Partitioner; 24 | import org.springframework.batch.item.ExecutionContext; 25 | import org.springframework.core.io.Resource; 26 | import org.springframework.util.Assert; 27 | 28 | /** 29 | * Slightly changed MultiResourcePartitioner, does create output file name too. 30 | * 31 | * @author Michael R. Lange 32 | */ 33 | public class CustomMultiResourcePartitioner implements Partitioner { 34 | 35 | private final Logger LOG = LoggerFactory.getLogger(this.getClass()); 36 | private static final String PARTITION_KEY = "partition"; 37 | private Resource[] resources = new Resource[0]; 38 | private String keyName = "inputFilePath"; 39 | 40 | /** 41 | * The resources to assign to each partition. In Spring configuration you 42 | * can use a pattern to select multiple resources. 43 | * 44 | * @param resources the resources to use 45 | */ 46 | public void setResources(Resource[] resources) { 47 | this.resources = resources; 48 | } 49 | 50 | /** 51 | * Assign the filename of each of the injected resources to an 52 | * {@link ExecutionContext}. 53 | * 54 | * @see Partitioner#partition(int) 55 | */ 56 | @Override 57 | public Map partition(int gridSize) { 58 | Map map = new HashMap(gridSize); 59 | int i = 0; 60 | for (Resource resource : resources) { 61 | ExecutionContext context = new ExecutionContext(); 62 | Assert.state(resource.exists(), "Resource does not exist: " + resource); 63 | try { 64 | context.putString(keyName, resource.getURL().toExternalForm()); 65 | context.put("outputFileName", createOutputFilename(i, resource)); 66 | } catch (IOException e) { 67 | throw new IllegalArgumentException("File could not be located for: " + resource, e); 68 | } 69 | map.put(PARTITION_KEY + i, context); 70 | i++; 71 | } 72 | return map; 73 | } 74 | 75 | /** 76 | * Creates distinct output file name per partition. 77 | * 78 | * @param partitionId 79 | * @param context 80 | * @param resource 81 | * @return 82 | */ 83 | private String createOutputFilename(int partitionId, Resource resource) { 84 | String outputFileName = "output-" + String.valueOf(partitionId) + ".txt"; 85 | LOG.info( 86 | "for inputfile:'" 87 | + resource.getFilename() 88 | + "' outputfilename:'" 89 | + outputFileName 90 | + "' was created"); 91 | 92 | return outputFileName; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/file/renamefile/rename-file-simple-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 20 | 21 | 22 | Rename File Example 23 | 24 | Creates output file name at runtime in relation to business data from e.g. the item reader. 25 | Constructs the name with an item from the input source. 26 | Renaming happens in an after (tasklet)step. 27 | 28 | 32 | 33 | 34 | 35 | 39 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Promotes the value of stepExecution.key 'desired.output.file' to the 69 | JobExecutionContext under the same key. 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/aggregating/aggregating-items-in-reader-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 20 | 21 | 22 | Job in which the Itemwriter aggregates items. 23 | Works with a wrapper reader which peeks ahead to know if the delegate reader 24 | is exhausted. 25 | 26 | for http://stackoverflow.com/questions/8837487/how-to-process-logically-related-rows-after-itemreader-in-springbatch 27 | 28 | 32 | 33 | 34 | 35 | 39 | 40 | 41 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/file/renamefile/simple/SimpleItemReader.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.renamefile.simple; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Iterator; 20 | import java.util.List; 21 | import org.springframework.batch.core.StepExecution; 22 | import org.springframework.batch.core.annotation.BeforeStep; 23 | import org.springframework.batch.item.ExecutionContext; 24 | import org.springframework.batch.item.ItemReader; 25 | import org.springframework.batch.item.ItemStream; 26 | import org.springframework.batch.item.ItemStreamException; 27 | import org.springframework.batch.item.NonTransientResourceException; 28 | import org.springframework.batch.item.ParseException; 29 | import org.springframework.batch.item.UnexpectedInputException; 30 | 31 | /** 32 | * SimpleItemReader - is not threadsafe, uses testdata. 33 | * Takes the first item to create the file name to be used 34 | * for renaming the original output file. 35 | * 36 | * @author Michael R. Lange 37 | */ 38 | public class SimpleItemReader implements ItemStream, ItemReader { 39 | 40 | private List testData; 41 | private Iterator iterator; 42 | private boolean desiredOutputFilePathCreated = false; 43 | private StepExecution stepExecution; 44 | 45 | @Override 46 | public String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException { 47 | if (iterator.hasNext()) { 48 | String item = iterator.next(); 49 | if (!desiredOutputFilePathCreated) { 50 | stepExecution.getExecutionContext().put("desired.output.file", "file:target/test-outputs/rename-files-simple/output-" + item + ".txt"); 51 | desiredOutputFilePathCreated = true; 52 | } 53 | return item; 54 | } else { 55 | return null; 56 | } 57 | } 58 | 59 | /** Opens the Iterator for the testdata. */ 60 | @Override 61 | public void open(ExecutionContext executionContext) throws ItemStreamException { 62 | this.testData = createTestData(20); 63 | this.iterator = testData.iterator(); 64 | } 65 | 66 | /** Method without function */ 67 | @Override 68 | public void update(ExecutionContext executionContext) throws ItemStreamException { 69 | // no-op 70 | } 71 | 72 | /** Method without function */ 73 | @Override 74 | public void close() throws ItemStreamException { 75 | // no-op 76 | } 77 | 78 | /** 79 | * Creates some testdata. 80 | * 81 | * @param count 82 | * @return 83 | */ 84 | private List createTestData(int count) { 85 | List data = new ArrayList(); 86 | for (int i = 0; i < count; i++) { 87 | data.add(String.valueOf(i)); 88 | } 89 | return data; 90 | } 91 | 92 | /** 93 | * Sets the stepExecution. 94 | * 95 | * @param stepExecution 96 | */ 97 | @BeforeStep 98 | public void setStepExecution(StepExecution stepExecution) { 99 | this.stepExecution = stepExecution; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/file/split/GetLineCountTasklet.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.split; 17 | 18 | import java.io.File; 19 | import java.io.FileNotFoundException; 20 | import java.io.FileReader; 21 | import java.io.IOException; 22 | import java.io.LineNumberReader; 23 | import org.springframework.batch.core.StepContribution; 24 | import org.springframework.batch.core.scope.context.ChunkContext; 25 | import org.springframework.batch.core.step.tasklet.Tasklet; 26 | import org.springframework.batch.repeat.RepeatStatus; 27 | import org.springframework.core.io.Resource; 28 | 29 | /** 30 | * Simple {@link Tasklet} which gets the line count from a file. 31 | * 32 | * @author Michael R. Lange 33 | */ 34 | public class GetLineCountTasklet implements Tasklet { 35 | 36 | private Resource resource; 37 | 38 | /** 39 | * Check if resouce is a file and get the line count from the file. 40 | * Line count is 41 | * 42 | * @param contribution 43 | * @param chunkContext 44 | * @return 45 | * @throws Exception 46 | */ 47 | @Override 48 | public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { 49 | 50 | checkResource(); 51 | int lineCount = getLineCount(resource.getFile()); 52 | 53 | chunkContext.getStepContext().getStepExecution().getExecutionContext().put("line.count", lineCount); 54 | 55 | return RepeatStatus.FINISHED; 56 | } 57 | 58 | /** 59 | * Short variant to get the line count of the file, there can be problems with files where 60 | * the last line has no content. 61 | * 62 | * @param file 63 | * @param chunkContext 64 | * @return the line count 65 | * @throws IOException 66 | * @throws FileNotFoundException 67 | */ 68 | private int getLineCount(File file) throws IOException, FileNotFoundException { 69 | LineNumberReader lnr = null; 70 | try { 71 | lnr = new LineNumberReader(new FileReader(file)); 72 | String line = null; 73 | int count = 0; 74 | while ((line = lnr.readLine()) != null) { 75 | count = lnr.getLineNumber(); 76 | } 77 | return count; 78 | } finally { 79 | if (lnr != null) { 80 | lnr.close(); 81 | } 82 | } 83 | } 84 | 85 | /** 86 | * Checks if the resource is a file. 87 | * 88 | * @return 89 | * @throws RuntimeException 90 | */ 91 | private File checkResource() throws RuntimeException { 92 | // is it null ? 93 | if (resource == null) { 94 | throw new NullPointerException("Resource was null."); 95 | } 96 | 97 | // is there a file ? 98 | File file; 99 | try { 100 | file = resource.getFile(); 101 | } catch (IOException e) { 102 | throw new RuntimeException("Could not convert resource to file: [" + resource + "]", e); 103 | } 104 | 105 | return file; 106 | } 107 | 108 | public void setResource(Resource resource) { 109 | this.resource = resource; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/de/langmi/spring/batch/examples/complex/skip/simple/SkipJobListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.skip.simple; 17 | 18 | import java.util.List; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import org.springframework.batch.core.ChunkListener; 22 | import org.springframework.batch.core.ExitStatus; 23 | import org.springframework.batch.core.ItemReadListener; 24 | import org.springframework.batch.core.ItemWriteListener; 25 | import org.springframework.batch.core.JobExecution; 26 | import org.springframework.batch.core.JobExecutionListener; 27 | import org.springframework.batch.core.SkipListener; 28 | import org.springframework.batch.core.StepExecution; 29 | import org.springframework.batch.core.StepExecutionListener; 30 | 31 | /** 32 | * SkipJobListener. 33 | * 34 | * @author Michael R. Lange 35 | */ 36 | public class SkipJobListener implements 37 | JobExecutionListener, 38 | SkipListener, 39 | StepExecutionListener, 40 | ChunkListener, 41 | ItemWriteListener, 42 | ItemReadListener { 43 | 44 | private final Logger LOG = LoggerFactory.getLogger(this.getClass()); 45 | 46 | @Override 47 | public void beforeJob(JobExecution jobExecution) { 48 | LOG.debug("before job"); 49 | } 50 | 51 | @Override 52 | public void afterJob(JobExecution jobExecution) { 53 | LOG.debug("after job"); 54 | for (StepExecution stepExecution : jobExecution.getStepExecutions()) { 55 | LOG.debug(stepExecution.getSummary()); 56 | } 57 | } 58 | 59 | @Override 60 | public void beforeStep(StepExecution stepExecution) { 61 | LOG.debug("before step"); 62 | } 63 | 64 | @Override 65 | public ExitStatus afterStep(StepExecution stepExecution) { 66 | LOG.debug("after step"); 67 | return stepExecution.getExitStatus(); 68 | } 69 | 70 | @Override 71 | public void beforeChunk() { 72 | LOG.debug("before chunk"); 73 | } 74 | 75 | @Override 76 | public void afterChunk() { 77 | LOG.debug("after chunk"); 78 | } 79 | 80 | @Override 81 | public void beforeWrite(List items) { 82 | LOG.debug("before write:" + items.toString()); 83 | } 84 | 85 | @Override 86 | public void afterWrite(List items) { 87 | LOG.debug("after write:" + items.toString()); 88 | } 89 | 90 | @Override 91 | public void onWriteError(Exception exception, List items) { 92 | LOG.debug("on write error"); 93 | } 94 | 95 | @Override 96 | public void onSkipInRead(Throwable t) { 97 | LOG.debug("on skip in read"); 98 | } 99 | 100 | @Override 101 | public void onSkipInWrite(String item, Throwable t) { 102 | LOG.debug("on skip in write:" + item); 103 | } 104 | 105 | @Override 106 | public void onSkipInProcess(String item, Throwable t) { 107 | LOG.debug("on skip in process:" + item); 108 | } 109 | 110 | @Override 111 | public void beforeRead() { 112 | LOG.debug("before read"); 113 | } 114 | 115 | @Override 116 | public void afterRead(String item) { 117 | LOG.debug("after read:" + item); 118 | } 119 | 120 | @Override 121 | public void onReadError(Exception ex) { 122 | LOG.debug("on read error"); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/crosscutting/autothreadconf/AutoThreadConfJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.crosscutting.autothreadconf; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import static org.junit.Assert.*; 21 | import org.junit.Test; 22 | import org.junit.runner.RunWith; 23 | import org.springframework.batch.core.BatchStatus; 24 | import org.springframework.batch.core.JobExecution; 25 | import org.springframework.batch.core.JobParameter; 26 | import org.springframework.batch.core.JobParameters; 27 | import org.springframework.batch.core.StepExecution; 28 | import org.springframework.batch.test.JobLauncherTestUtils; 29 | import org.springframework.beans.factory.annotation.Autowired; 30 | import org.springframework.test.context.ContextConfiguration; 31 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 32 | 33 | /** 34 | * Automatic Thread Configuration JobConfigurationTest. 35 | * 36 | * @author Michael R. Lange 37 | */ 38 | @ContextConfiguration(locations = { 39 | "classpath*:spring/batch/job/complex/crosscutting/autothreadconf/auto-thread-conf-job.xml", 40 | "classpath*:spring/batch/setup/**/*.xml"}) 41 | @RunWith(SpringJUnit4ClassRunner.class) 42 | public class AutoThreadConfJobConfigurationTest { 43 | 44 | @Autowired 45 | private JobLauncherTestUtils jobLauncherTestUtils; 46 | private static final int READ_COUNT_PER_FILE = 20; 47 | private static final int READ_COUNT_OVERALL = 120; 48 | private static final int STEP_COUNT = 7; 49 | 50 | @Test 51 | public void launchJob() throws Exception { 52 | // Job parameters 53 | Map jobParametersMap = new HashMap(); 54 | jobParametersMap.put("time", new JobParameter(System.currentTimeMillis())); 55 | jobParametersMap.put("input.file.pattern", new JobParameter("file:src/test/resources/input/multi/*.txt")); 56 | jobParametersMap.put("output.file.path", new JobParameter("file:target/test-outputs/auto-thread-conf/")); 57 | 58 | // launch the job 59 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(new JobParameters(jobParametersMap)); 60 | 61 | // assert job run status 62 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 63 | 64 | assertEquals(STEP_COUNT, jobExecution.getStepExecutions().size()); 65 | 66 | // assert step meta data 67 | boolean partitionStepFound = false; 68 | boolean childrenStepFound = false; 69 | for (StepExecution step : jobExecution.getStepExecutions()) { 70 | // spring batch works with 7 "steps" here, the PartitionStep itself 71 | // and the created children 72 | if ("businessStep".equals(step.getStepName())) { 73 | assertEquals("Read Count mismatch, changed input?", 74 | READ_COUNT_OVERALL, step.getReadCount()); 75 | partitionStepFound = true; 76 | } 77 | // the children steps follow the pattern 78 | // ":partition:" 79 | if (step.getStepName().contains("concreteBusinessStep:partition")) { 80 | assertEquals("Read Count mismatch, changed input?", 81 | READ_COUNT_PER_FILE, step.getReadCount()); 82 | childrenStepFound = true; 83 | } 84 | } 85 | assertTrue("Changed step names?", partitionStepFound && childrenStepFound); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/file/renamefile/RenameFilePartitionJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.renamefile; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import static org.junit.Assert.*; 21 | import org.junit.Test; 22 | import org.junit.runner.RunWith; 23 | import org.springframework.batch.core.BatchStatus; 24 | import org.springframework.batch.core.JobExecution; 25 | import org.springframework.batch.core.JobParameter; 26 | import org.springframework.batch.core.JobParameters; 27 | import org.springframework.batch.core.StepExecution; 28 | import org.springframework.batch.test.JobLauncherTestUtils; 29 | import org.springframework.beans.factory.annotation.Autowired; 30 | import org.springframework.test.context.ContextConfiguration; 31 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 32 | 33 | /** 34 | * JobConfigurationTest. 35 | * 36 | * @author Michael R. Lange 37 | */ 38 | @ContextConfiguration(locations = { 39 | "classpath*:spring/batch/job/complex/file/renamefile/rename-file-partition-job.xml", 40 | "classpath*:spring/batch/setup/**/*.xml"}) 41 | @RunWith(SpringJUnit4ClassRunner.class) 42 | public class RenameFilePartitionJobConfigurationTest { 43 | 44 | /** JobLauncherTestUtils Bean. */ 45 | @Autowired 46 | private JobLauncherTestUtils jobLauncherTestUtils; 47 | /** Each file has 20 lines, but the first header line will not be processed. */ 48 | private static final int READ_COUNT_PER_FILE = 19; 49 | /** Its 114 cause of the header lines per file. */ 50 | private static final int READ_COUNT_OVERALL = 114; 51 | private static final int STEP_COUNT = 7; 52 | 53 | /** Launch Test. */ 54 | @Test 55 | public void launchJob() throws Exception { 56 | // Job parameters 57 | Map jobParametersMap = new HashMap(); 58 | jobParametersMap.put("time", new JobParameter(System.currentTimeMillis())); 59 | jobParametersMap.put("input.file.pattern", new JobParameter("file:src/test/resources/input/multi/*.txt")); 60 | jobParametersMap.put("output.file.path", new JobParameter("file:target/test-outputs/rename-file-partition/")); 61 | 62 | // launch the job 63 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(new JobParameters(jobParametersMap)); 64 | 65 | // assert job run status 66 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 67 | 68 | 69 | assertEquals(STEP_COUNT, jobExecution.getStepExecutions().size()); 70 | 71 | // assert step meta data 72 | boolean partitionStepFound = false; 73 | boolean childrenStepFound = false; 74 | for (StepExecution step : jobExecution.getStepExecutions()) { 75 | // spring batch works with 7 "steps" here, the PartitionStep itself 76 | // and the created children 77 | if ("businessStep".equals(step.getStepName())) { 78 | assertEquals("Read Count mismatch, changed input?", 79 | READ_COUNT_OVERALL, step.getReadCount()); 80 | partitionStepFound = true; 81 | } 82 | // the children steps follow the pattern 83 | // ":partition:" 84 | if (step.getStepName().contains("concreteBusinessStep:partition")) { 85 | assertEquals("Read Count mismatch, changed input?", 86 | READ_COUNT_PER_FILE, step.getReadCount()); 87 | childrenStepFound = true; 88 | } 89 | } 90 | assertTrue("Changed step names?", partitionStepFound && childrenStepFound); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/file/renamefile/RenameFilePartitionExtraStepJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.renamefile; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import static org.junit.Assert.*; 21 | import org.junit.Test; 22 | import org.junit.runner.RunWith; 23 | import org.springframework.batch.core.BatchStatus; 24 | import org.springframework.batch.core.JobExecution; 25 | import org.springframework.batch.core.JobParameter; 26 | import org.springframework.batch.core.JobParameters; 27 | import org.springframework.batch.core.StepExecution; 28 | import org.springframework.batch.test.JobLauncherTestUtils; 29 | import org.springframework.beans.factory.annotation.Autowired; 30 | import org.springframework.test.context.ContextConfiguration; 31 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 32 | 33 | /** 34 | * JobConfigurationTest. 35 | * 36 | * @author Michael R. Lange 37 | */ 38 | @ContextConfiguration(locations = { 39 | "classpath*:spring/batch/job/complex/file/renamefile/rename-file-partition-extra-step-job.xml", 40 | "classpath*:spring/batch/setup/**/*.xml"}) 41 | @RunWith(SpringJUnit4ClassRunner.class) 42 | public class RenameFilePartitionExtraStepJobConfigurationTest { 43 | 44 | /** JobLauncherTestUtils Bean. */ 45 | @Autowired 46 | private JobLauncherTestUtils jobLauncherTestUtils; 47 | /** Each file has 20 lines, but the first header line will not be processed. */ 48 | private static final int READ_COUNT_PER_FILE = 19; 49 | /** Its 114 cause of the header lines per file. */ 50 | private static final int READ_COUNT_OVERALL = 114; 51 | private static final int STEP_COUNT = 8; 52 | 53 | /** Launch Test. */ 54 | @Test 55 | public void launchJob() throws Exception { 56 | // Job parameters 57 | Map jobParametersMap = new HashMap(); 58 | jobParametersMap.put("time", new JobParameter(System.currentTimeMillis())); 59 | jobParametersMap.put("input.file.pattern", new JobParameter("file:src/test/resources/input/multi/*.txt")); 60 | jobParametersMap.put("output.file.path", new JobParameter("file:target/test-outputs/rename-file-partition-extra-step/")); 61 | 62 | // launch the job 63 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(new JobParameters(jobParametersMap)); 64 | 65 | // assert job run status 66 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 67 | 68 | 69 | assertEquals(STEP_COUNT, jobExecution.getStepExecutions().size()); 70 | 71 | // assert step meta data 72 | boolean partitionStepFound = false; 73 | boolean childrenStepFound = false; 74 | for (StepExecution step : jobExecution.getStepExecutions()) { 75 | // spring batch works with 8 "steps" here, the PartitionStep itself 76 | // and the created children + the last taskletStep 77 | if ("businessStep".equals(step.getStepName())) { 78 | assertEquals("Read Count mismatch, changed input?", 79 | READ_COUNT_OVERALL, step.getReadCount()); 80 | partitionStepFound = true; 81 | } 82 | // the children steps follow the pattern 83 | // ":partition:" 84 | if (step.getStepName().contains("concreteBusinessStep:partition")) { 85 | assertEquals("Read Count mismatch, changed input?", 86 | READ_COUNT_PER_FILE, step.getReadCount()); 87 | childrenStepFound = true; 88 | } 89 | } 90 | assertTrue("Changed step names?", partitionStepFound && childrenStepFound); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/aggregating/reader/AggregateSimpleItemsReaderTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.aggregating.reader; 17 | 18 | import de.langmi.spring.batch.examples.complex.aggregating.AggregatedItem; 19 | import de.langmi.spring.batch.examples.complex.support.SimpleItem; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import org.junit.Before; 23 | import org.junit.Test; 24 | import static org.junit.Assert.*; 25 | import org.springframework.batch.item.ItemReader; 26 | import org.springframework.batch.item.support.IteratorItemReader; 27 | 28 | /** 29 | * AggregateSimpleItemsReaderTest. 30 | * 31 | * @author Michael R. Lange 32 | */ 33 | public class AggregateSimpleItemsReaderTest { 34 | 35 | /** Reader under test. */ 36 | private AggregateSimpleItemsReader reader = null; 37 | 38 | /** 39 | * Test with ID change, should result in two aggregated items. 40 | */ 41 | @Test 42 | public void testReadWithIdChange() throws Exception { 43 | // create first set of simple items with shared id 44 | int count = 5; 45 | int sharedId = 1000; 46 | int value = 1; 47 | List testData = createTestData(count, sharedId, value); 48 | // create second set of simple items with shared id 49 | int countSecond = 6; 50 | int sharedIdSecond = 2000; 51 | int valueSecond = 3; 52 | List testData2 = createTestData(countSecond, sharedIdSecond, valueSecond); 53 | // combine them 54 | testData.addAll(testData2); 55 | // setup wrapped delegate 56 | ItemReader delegate = new IteratorItemReader(testData); 57 | reader.setDelegate(delegate); 58 | 59 | // 1. aggregated item 60 | AggregatedItem item = reader.read(); 61 | assertNotNull(item); 62 | assertEquals(sharedId, item.getId()); 63 | assertEquals(count * value, item.getSum()); 64 | // 2. aggregated item 65 | AggregatedItem itemSecond = reader.read(); 66 | assertNotNull(itemSecond); 67 | assertEquals(sharedIdSecond, itemSecond.getId()); 68 | assertEquals(countSecond * valueSecond, itemSecond.getSum()); 69 | // there should be no next item 70 | assertNull(reader.read()); 71 | } 72 | 73 | /** 74 | * Test without ID change, should result in one aggregated item only. 75 | */ 76 | @Test 77 | public void testReadWithoutIdChange() throws Exception { 78 | int count = 5; 79 | int sharedId = 1000; 80 | int value = 1; 81 | ItemReader delegate = 82 | new IteratorItemReader(createTestData(count, sharedId, value)); 83 | reader.setDelegate(delegate); 84 | 85 | AggregatedItem item = reader.read(); 86 | assertNotNull(item); 87 | assertEquals(sharedId, item.getId()); 88 | assertEquals(count * value, item.getSum()); 89 | // there should be no next item 90 | assertNull(reader.read()); 91 | } 92 | 93 | @Before 94 | public void before() { 95 | this.reader = new AggregateSimpleItemsReader(); 96 | } 97 | 98 | /** 99 | * Testdata util method. 100 | * 101 | * @param count 102 | * @param sharedId 103 | * @param value 104 | * @return 105 | */ 106 | private List createTestData(int count, int sharedId, int value) { 107 | List testData = new ArrayList(); 108 | for (int i = 0; i < count; i++) { 109 | testData.add(new SimpleItem(i, sharedId, value)); 110 | } 111 | return testData; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/crosscutting/autothreadconf/AutoThreadConfPlaceholderJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.crosscutting.autothreadconf; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import org.junit.BeforeClass; 21 | import static org.junit.Assert.*; 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.springframework.batch.core.BatchStatus; 25 | import org.springframework.batch.core.JobExecution; 26 | import org.springframework.batch.core.JobParameter; 27 | import org.springframework.batch.core.JobParameters; 28 | import org.springframework.batch.core.StepExecution; 29 | import org.springframework.batch.test.JobLauncherTestUtils; 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.test.context.ContextConfiguration; 32 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 33 | 34 | /** 35 | * Automatic Thread Configuration with Placeholder JobConfigurationTest. 36 | * 37 | * @author Michael R. Lange 38 | */ 39 | @ContextConfiguration(locations = { 40 | "classpath*:spring/batch/job/complex/crosscutting/autothreadconf/auto-thread-conf-placeholder-job.xml", 41 | "classpath*:spring/batch/setup/**/*.xml"}) 42 | @RunWith(SpringJUnit4ClassRunner.class) 43 | public class AutoThreadConfPlaceholderJobConfigurationTest { 44 | 45 | @Autowired 46 | private JobLauncherTestUtils jobLauncherTestUtils; 47 | private static final int READ_COUNT_PER_FILE = 20; 48 | private static final int READ_COUNT_OVERALL = 120; 49 | private static final int STEP_COUNT = 7; 50 | 51 | @BeforeClass 52 | public static void setup() { 53 | // set the system property for the placeholder mechanism 54 | System.setProperty("concurrency.limit", String.valueOf(2)); 55 | } 56 | 57 | @Test 58 | public void launchJob() throws Exception { 59 | // Job parameters 60 | Map jobParametersMap = new HashMap(); 61 | jobParametersMap.put("time", new JobParameter(System.currentTimeMillis())); 62 | jobParametersMap.put("input.file.pattern", new JobParameter("file:src/test/resources/input/multi/*.txt")); 63 | jobParametersMap.put("output.file.path", new JobParameter("file:target/test-outputs/auto-thread-conf-placeholder/")); 64 | 65 | // launch the job 66 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(new JobParameters(jobParametersMap)); 67 | 68 | 69 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 70 | 71 | assertEquals(STEP_COUNT, jobExecution.getStepExecutions().size()); 72 | 73 | // assert step meta data 74 | boolean partitionStepFound = false; 75 | boolean childrenStepFound = false; 76 | for (StepExecution step : jobExecution.getStepExecutions()) { 77 | // spring batch works with 7 "steps" here, the PartitionStep itself 78 | // and the created children 79 | if ("businessStep".equals(step.getStepName())) { 80 | assertEquals("Read Count mismatch, changed input?", 81 | READ_COUNT_OVERALL, step.getReadCount()); 82 | partitionStepFound = true; 83 | } 84 | // the children steps follow the pattern 85 | // ":partition:" 86 | if (step.getStepName().contains("concreteBusinessStep:partition")) { 87 | assertEquals("Read Count mismatch, changed input?", 88 | READ_COUNT_PER_FILE, step.getReadCount()); 89 | childrenStepFound = true; 90 | } 91 | } 92 | assertTrue("Changed step names?", partitionStepFound && childrenStepFound); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/crosscutting/autothreadconf/auto-thread-conf-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 20 | 21 | 22 | Simple Automatic Thread Configuration Example 23 | 24 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | * concurrencyLimit matches count of read files to force multi-threading 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | * resource is set via step scoped late binding (see multiresourcereader) 68 | * strict mode - we expect input files 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | resource is set via step scoped late binding 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/jdbc/generic/export/JdbcGenericExportToFileJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.jdbc.generic.export; 17 | 18 | import java.sql.PreparedStatement; 19 | import java.sql.SQLException; 20 | import org.junit.After; 21 | import javax.sql.DataSource; 22 | import static org.junit.Assert.*; 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | import org.springframework.batch.core.BatchStatus; 27 | import org.springframework.batch.core.JobExecution; 28 | import org.springframework.batch.core.JobParameters; 29 | import org.springframework.batch.core.StepExecution; 30 | import org.springframework.batch.test.JobLauncherTestUtils; 31 | import org.springframework.beans.factory.annotation.Autowired; 32 | import org.springframework.jdbc.core.BatchPreparedStatementSetter; 33 | import org.springframework.jdbc.core.JdbcTemplate; 34 | import org.springframework.test.context.ContextConfiguration; 35 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 36 | 37 | /** 38 | * JobConfigurationTest. 39 | * 40 | * @author Michael R. Lange 41 | */ 42 | @ContextConfiguration(locations = { 43 | "classpath*:spring/batch/job/complex/jdbc/jdbc-generic-export-to-file-job.xml", 44 | "classpath*:spring/batch/setup/**/*.xml"}) 45 | @RunWith(SpringJUnit4ClassRunner.class) 46 | public class JdbcGenericExportToFileJobConfigurationTest { 47 | 48 | /** JobLauncherTestUtils Bean. */ 49 | @Autowired 50 | private JobLauncherTestUtils jobLauncherTestUtils; 51 | @Autowired 52 | private DataSource dataSource; 53 | private JdbcTemplate jdbcTemplate; 54 | /** CREATE statement for BUSINESS_OBJECTS table. */ 55 | private static final String CREATE_TABLE_SQL = "CREATE TABLE TEST (ID INTEGER GENERATED BY DEFAULT AS IDENTITY, NAME VARCHAR (100))"; 56 | private static final String INSERT = "INSERT INTO TEST (NAME) VALUES (?)"; 57 | private static final String DELETE_TABLE_SQL = "DROP TABLE TEST"; 58 | private static final String SHUTDOWN_HSQLDB = "SHUTDOWN"; 59 | private static final int EXPECTED_COUNT = 40; 60 | 61 | /** Launch Test. */ 62 | @Test 63 | public void launchJob() throws Exception { 64 | // launch the job 65 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(new JobParameters()); 66 | 67 | // assert job run status 68 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 69 | 70 | // assert read/written items 71 | for (StepExecution stepExecution : jobExecution.getStepExecutions()) { 72 | assertEquals(EXPECTED_COUNT, stepExecution.getReadCount()); 73 | assertEquals(EXPECTED_COUNT, stepExecution.getWriteCount()); 74 | } 75 | } 76 | 77 | /** 78 | * Setup the test with some test data. 79 | * 80 | * @throws Exception 81 | */ 82 | @Before 83 | public void before() throws Exception { 84 | // provide jdbc template for setup and later assertions 85 | jdbcTemplate = new JdbcTemplate(dataSource); 86 | // setup business data table 87 | jdbcTemplate.execute(CREATE_TABLE_SQL); 88 | // insert test data 89 | jdbcTemplate.batchUpdate(INSERT, new BatchPreparedStatementSetter() { 90 | 91 | @Override 92 | public void setValues(PreparedStatement ps, int i) throws SQLException { 93 | ps.setString(1, String.valueOf(i)); 94 | } 95 | 96 | @Override 97 | public int getBatchSize() { 98 | return EXPECTED_COUNT; 99 | } 100 | }); 101 | } 102 | 103 | /** 104 | * Teardown the test by deleting the test data. 105 | * 106 | * @throws Exception 107 | */ 108 | @After 109 | public void after() throws Exception { 110 | // clear table 111 | jdbcTemplate.execute(DELETE_TABLE_SQL); 112 | // shutdown HSQLDB properly 113 | jdbcTemplate.execute(SHUTDOWN_HSQLDB); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/file/split/split-file-simple-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 20 | 21 | 22 | Split File Example 23 | 24 | Splits a file in two even halves. 25 | 26 | - tasklet reads line-count for the input file 27 | - custom writer controls which lines are written into which output-file 28 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 61 | 62 | 63 | 64 | 67 | 68 | 69 | 70 | 71 | 72 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 102 | 103 | Promotes the value of stepExecution.key 'line.count' to the 104 | JobExecutionContext under the same key. 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/jdbc/generic/export/JdbcGenericExportToDatabaseJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.jdbc.generic.export; 17 | 18 | import java.sql.PreparedStatement; 19 | import java.sql.SQLException; 20 | import org.junit.After; 21 | import javax.sql.DataSource; 22 | import static org.junit.Assert.*; 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | import org.springframework.batch.core.BatchStatus; 27 | import org.springframework.batch.core.JobExecution; 28 | import org.springframework.batch.core.JobParameters; 29 | import org.springframework.batch.core.StepExecution; 30 | import org.springframework.batch.test.JobLauncherTestUtils; 31 | import org.springframework.beans.factory.annotation.Autowired; 32 | import org.springframework.jdbc.core.BatchPreparedStatementSetter; 33 | import org.springframework.jdbc.core.JdbcTemplate; 34 | import org.springframework.test.context.ContextConfiguration; 35 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 36 | 37 | /** 38 | * JobConfigurationTest. 39 | * 40 | * @author Michael R. Lange 41 | */ 42 | @ContextConfiguration(locations = { 43 | "classpath*:spring/batch/job/complex/jdbc/jdbc-generic-export-to-database-job.xml", 44 | "classpath*:spring/batch/setup/**/*.xml"}) 45 | @RunWith(SpringJUnit4ClassRunner.class) 46 | public class JdbcGenericExportToDatabaseJobConfigurationTest { 47 | 48 | /** JobLauncherTestUtils Bean. */ 49 | @Autowired 50 | private JobLauncherTestUtils jobLauncherTestUtils; 51 | @Autowired 52 | private DataSource dataSource; 53 | private JdbcTemplate jdbcTemplate; 54 | /** CREATE statement for BUSINESS_OBJECTS table. */ 55 | private static final String CREATE_ROOT_TABLE_SQL = "CREATE TABLE ROOT (ID INTEGER GENERATED BY DEFAULT AS IDENTITY, NAME VARCHAR (100))"; 56 | private static final String CREATE_TARGET_TABLE_SQL = "CREATE TABLE TARGET (ID INTEGER, NAME VARCHAR (100))"; 57 | private static final String INSERT = "INSERT INTO ROOT (NAME) VALUES (?)"; 58 | private static final String DELETE_ROOT_TABLE_SQL = "DROP TABLE ROOT"; 59 | private static final String DELETE_TARGET_TABLE_SQL = "DROP TABLE TARGET"; 60 | private static final String SHUTDOWN_HSQLDB = "SHUTDOWN"; 61 | private static final String COUNT_SQL = "SELECT COUNT(*) FROM TARGET"; 62 | private static final int EXPECTED_COUNT = 40; 63 | 64 | /** Launch Test. */ 65 | @Test 66 | public void launchJob() throws Exception { 67 | // launch the job 68 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(new JobParameters()); 69 | 70 | // assert job run status 71 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 72 | 73 | // assert read/written items 74 | for (StepExecution stepExecution : jobExecution.getStepExecutions()) { 75 | assertEquals(EXPECTED_COUNT, stepExecution.getReadCount()); 76 | assertEquals(EXPECTED_COUNT, stepExecution.getWriteCount()); 77 | } 78 | 79 | // assert items are written successfully to target table 80 | assertEquals(EXPECTED_COUNT, jdbcTemplate.queryForInt(COUNT_SQL)); 81 | } 82 | 83 | /** 84 | * Setup the test with some test data. 85 | * 86 | * @throws Exception 87 | */ 88 | @Before 89 | public void before() throws Exception { 90 | // provide jdbc template for setup and later assertions 91 | jdbcTemplate = new JdbcTemplate(dataSource); 92 | // setup root table 93 | jdbcTemplate.execute(CREATE_ROOT_TABLE_SQL); 94 | // setup target table 95 | jdbcTemplate.execute(CREATE_TARGET_TABLE_SQL); 96 | // insert test data into root table 97 | jdbcTemplate.batchUpdate(INSERT, new BatchPreparedStatementSetter() { 98 | 99 | @Override 100 | public void setValues(PreparedStatement ps, int i) throws SQLException { 101 | ps.setString(1, String.valueOf(i)); 102 | } 103 | 104 | @Override 105 | public int getBatchSize() { 106 | return EXPECTED_COUNT; 107 | } 108 | }); 109 | } 110 | 111 | /** 112 | * Teardown the test by deleting the test data. 113 | * 114 | * @throws Exception 115 | */ 116 | @After 117 | public void after() throws Exception { 118 | // clear tables 119 | jdbcTemplate.execute(DELETE_ROOT_TABLE_SQL); 120 | jdbcTemplate.execute(DELETE_TARGET_TABLE_SQL); 121 | // shutdown HSQLDB properly 122 | jdbcTemplate.execute(SHUTDOWN_HSQLDB); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/crosscutting/autothreadconf/auto-thread-conf-placeholder-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 23 | 24 | 25 | Simple Automatic Thread Configuration Example with placeholder. 26 | 27 | 31 | 32 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | 45 | 46 | * concurrencyLimit matches count of read files to force multi-threading 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | * resource is set via step scoped late binding (see multiresourcereader) 71 | * strict mode - we expect input files 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | resource is set via step scoped late binding 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /src/test/java/de/langmi/spring/batch/examples/complex/file/split/SplitFileSimpleJobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011 Michael R. Lange . 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 de.langmi.spring.batch.examples.complex.file.split; 17 | 18 | import java.io.File; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import java.util.Scanner; 22 | import static org.junit.Assert.*; 23 | import org.junit.Test; 24 | import org.junit.runner.RunWith; 25 | import org.springframework.batch.core.BatchStatus; 26 | import org.springframework.batch.core.JobExecution; 27 | import org.springframework.batch.core.JobParameter; 28 | import org.springframework.batch.core.JobParameters; 29 | import org.springframework.batch.core.StepExecution; 30 | import org.springframework.batch.test.JobLauncherTestUtils; 31 | import org.springframework.beans.factory.annotation.Autowired; 32 | import org.springframework.test.context.ContextConfiguration; 33 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 34 | 35 | /** 36 | * JobConfigurationTest. 37 | * 38 | * @author Michael R. Lange 39 | */ 40 | @ContextConfiguration(locations = { 41 | "classpath*:spring/batch/job/complex/file/split/split-file-simple-job.xml", 42 | "classpath*:spring/batch/setup/**/*.xml"}) 43 | @RunWith(SpringJUnit4ClassRunner.class) 44 | public class SplitFileSimpleJobConfigurationTest { 45 | 46 | /** JobLauncherTestUtils Bean. */ 47 | @Autowired 48 | private JobLauncherTestUtils jobLauncherTestUtils; 49 | private static final int EXPECTED_COUNT = 20; 50 | private static final String ENCODING_UTF_8 = "UTF-8"; 51 | private static final String LINE_SEPARATOR = System.getProperty("line.separator"); 52 | private static final String OUTPUT_FIRST = "target/test-outputs/split-file-simple/output-first.txt"; 53 | private static final String OUTPUT_SECOND = "target/test-outputs/split-file-simple/output-second.txt"; 54 | 55 | /** Launch Test. */ 56 | @Test 57 | public void launchJob() throws Exception { 58 | // Job parameters 59 | Map jobParametersMap = new HashMap(); 60 | jobParametersMap.put("time", new JobParameter(System.currentTimeMillis())); 61 | jobParametersMap.put("input.file", new JobParameter("file:src/test/resources/input/simple/input.txt")); 62 | jobParametersMap.put("output.file.first", new JobParameter("file:" + OUTPUT_FIRST)); 63 | jobParametersMap.put("output.file.second", new JobParameter("file:" + OUTPUT_SECOND)); 64 | 65 | // launch the job 66 | JobExecution jobExecution = jobLauncherTestUtils.launchJob(new JobParameters(jobParametersMap)); 67 | 68 | // assert job run status 69 | assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); 70 | 71 | // assert read/write counts 72 | for (StepExecution step : jobExecution.getStepExecutions()) { 73 | // the first tasklet step logs nothing 74 | if ("getLineCountStep".equals(step.getStepName())) { 75 | assertEquals(0, step.getReadCount()); 76 | assertEquals(0, step.getWriteCount()); 77 | // its "one" execution and thus one commit 78 | assertEquals(1, step.getCommitCount()); 79 | } 80 | if ("splitFilesStep".equals(step.getStepName())) { 81 | assertEquals(EXPECTED_COUNT, step.getReadCount()); 82 | assertEquals(EXPECTED_COUNT, step.getWriteCount()); 83 | // commit interval is 5 (see job.xml) 84 | // commit-count: 20 / 5 = 4 85 | // but there will be an additional commit for the 86 | // last "read but end of data" cycle 87 | assertEquals(5, step.getCommitCount()); 88 | } 89 | } 90 | 91 | // check output contains right amount of lines 92 | assertEquals(EXPECTED_COUNT / 2, getLineCount(OUTPUT_FIRST)); 93 | assertEquals(EXPECTED_COUNT / 2, getLineCount(OUTPUT_SECOND)); 94 | } 95 | 96 | /** 97 | * Get the line count for a file using Java 1.5 possibilities. 98 | * 99 | * @param file 100 | * @return 101 | * @throws Exception 102 | */ 103 | private static int getLineCount(String file) throws Exception { 104 | 105 | Scanner scanner = null; 106 | 107 | try { 108 | scanner = new Scanner(new File(file), ENCODING_UTF_8); 109 | // read lines, not tokens for line-content 110 | scanner.useDelimiter(LINE_SEPARATOR); 111 | int count = 0; 112 | while (scanner.hasNextLine()) { 113 | scanner.nextLine(); 114 | count++; 115 | } 116 | return count; 117 | } finally { 118 | if (scanner != null) { 119 | scanner.close(); 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/file/renamefile/rename-file-partition-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 20 | 21 | 22 | Rename Files Example with a MultiResourcePartitioner 23 | 24 | * partition step to read multiple files 25 | * for each file, a specific business key is extracted from the header 26 | * file renaming happens in afterStep listener 27 | * its actually a copy-and-delete-old-file version 28 | 29 | 33 | 34 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | * concurrencyLimit > 1 to force multi-threading 53 | * CustomMultiResourcePartitioner creates distinct output file names too 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | * resource is set via step scoped late binding (see multiresourcereader) 82 | * linesToSkip - count for header lines, set to 1, there is only 1 header line 83 | * strict mode - we expect input files 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 97 | 98 | * handles the header line 99 | * extracts business key 100 | * promotes the key to the step execution context for further use 101 | 102 | 103 | 104 | 105 | 106 | resource is set via step scoped late binding 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | * pulls current output file name from context 117 | * copies to desired output file 118 | * calls file.deleteOnExit for old output file 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /src/main/resources/spring/batch/job/complex/file/renamefile/rename-file-partition-extra-step-job.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 20 | 21 | 22 | Rename Files Tasklet Example: generic version. 23 | 24 | * partition step to read multiple files 25 | * for each file, a specific business key is extracted from the header 26 | * file renaming happens in an extra (tasklet)step 27 | 28 | 32 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * concurrencyLimit > 1 to force multi-threading 56 | * CustomMultiResourcePartitioner creates distinct output file names too 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | * resource is set via step scoped late binding (see multiresourcereader) 85 | * linesToSkip - count for header lines, set to 1, there is only 1 header line 86 | * strict mode - we expect input files 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 100 | 101 | * handles the header line 102 | * extracts business key 103 | * promotes the key to the step execution context for further use 104 | 105 | 106 | 107 | 108 | 109 | resource is set via step scoped late binding 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | Takes the fileNamesMap and renames the output files. New Name is 125 | constructed with the business key from the map. 126 | 127 | 128 | 129 | 130 | 131 | 132 | Map to hold the old filenames and businesskeys to create desired output 133 | file name. 134 | 135 | 136 | 137 | --------------------------------------------------------------------------------