├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── toptal
│ │ ├── BatchApplication.java
│ │ ├── BirthdayFilterProcessor.java
│ │ ├── Customer.java
│ │ ├── CustomerItemReader.java
│ │ ├── CustomerItemWriter.java
│ │ ├── CustomerReportJobConfig.java
│ │ └── TransactionValidatingProcessor.java
└── resources
│ └── application.properties
└── test
└── java
└── com
└── toptal
├── BatchApplicationTest.java
├── BatchTestConfiguration.java
├── BirthdayFilterProcessorTest.java
└── CustomerReportJobConfigTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
2 | hs_err_pid*
3 |
4 | # idea
5 | .idea/
6 | *.iml
7 |
8 | # eclipse
9 | .classpath
10 | .project
11 | .settings/
12 | .springBeans
13 |
14 | # maven
15 | target/
16 | database.xml
17 | output.txt
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Alexey Saenko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # spring-batch-article
2 |
3 | Project for the post [Spring Batch Tutorial: Batch Processing Made Easy with Spring](https://www.toptal.com/spring/spring-batch-tutorial) in the Toptal blog
4 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.toptal
7 | spring-batch-article
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | spring-batch-article
12 | Demo project for Spring Batch
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 1.5.2.RELEASE
18 |
19 |
20 |
21 |
22 | UTF-8
23 | UTF-8
24 | 1.8
25 |
26 |
27 |
28 |
29 | org.springframework.boot
30 | spring-boot-starter-batch
31 |
32 |
33 | org.projectlombok
34 | lombok
35 | 1.16.16
36 |
37 |
38 |
39 | com.h2database
40 | h2
41 | runtime
42 |
43 |
44 |
45 | org.springframework.boot
46 | spring-boot-starter-test
47 | test
48 |
49 |
50 | org.springframework.batch
51 | spring-batch-test
52 |
53 |
54 |
55 |
56 |
57 |
58 | org.springframework.boot
59 | spring-boot-maven-plugin
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/src/main/java/com/toptal/BatchApplication.java:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 Alexey Saenko
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package com.toptal;
25 |
26 | import java.beans.XMLEncoder;
27 | import java.io.FileNotFoundException;
28 | import java.io.FileOutputStream;
29 | import java.util.Calendar;
30 | import java.util.Collection;
31 | import java.util.GregorianCalendar;
32 | import java.util.LinkedList;
33 | import java.util.UUID;
34 | import lombok.extern.slf4j.Slf4j;
35 | import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
36 | import org.springframework.boot.SpringApplication;
37 | import org.springframework.boot.autoconfigure.SpringBootApplication;
38 | import org.springframework.scheduling.annotation.EnableScheduling;
39 |
40 | /**
41 | * Entry point of the application.
42 | *
43 | * @author Alexey Saenko (alexey.saenko@gmail.com)
44 | */
45 | @Slf4j
46 | @EnableScheduling
47 | @EnableBatchProcessing
48 | @SpringBootApplication
49 | public class BatchApplication {
50 |
51 | public static void main(String[] args) {
52 | prepareTestData(1000);
53 | SpringApplication.run(BatchApplication.class, args);
54 | }
55 |
56 | private static void prepareTestData(final int amount) {
57 | final int actualYear = new GregorianCalendar().get(Calendar.YEAR);
58 | final Collection customers = new LinkedList<>();
59 | for (int i = 1; i <= amount; i++) {
60 | final Calendar birthday = new GregorianCalendar();
61 | birthday.set(Calendar.YEAR, random(actualYear - 100, actualYear));
62 | birthday.set(Calendar.DAY_OF_YEAR, random(1, birthday.getActualMaximum(Calendar.DAY_OF_YEAR)));
63 | final Customer customer = new Customer();
64 | customer.setId(i);
65 | customer.setName(UUID.randomUUID().toString().replaceAll("[^a-z]", ""));
66 | customer.setBirthday(birthday);
67 | customer.setTransactions(random(0, 100));
68 | customers.add(customer);
69 | }
70 | try (final XMLEncoder encoder = new XMLEncoder(new FileOutputStream(CustomerReportJobConfig.XML_FILE))) {
71 | encoder.writeObject(customers);
72 | } catch (final FileNotFoundException e) {
73 | log.error(e.getMessage(), e);
74 | System.exit(-1);
75 | }
76 | }
77 |
78 | private static int random(final int start, final int end) {
79 | return start + (int) Math.round(Math.random() * (end - start));
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/com/toptal/BirthdayFilterProcessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 Alexey Saenko
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package com.toptal;
25 |
26 | import java.util.Calendar;
27 | import java.util.GregorianCalendar;
28 | import lombok.extern.slf4j.Slf4j;
29 | import org.springframework.batch.item.ItemProcessor;
30 |
31 | /**
32 | * Processor filters customers by being born in the actual month.
33 | *
34 | * @author Alexey Saenko (alexey.saenko@gmail.com)
35 | */
36 | @Slf4j
37 | public class BirthdayFilterProcessor implements ItemProcessor {
38 | @Override
39 | public Customer process(final Customer item) throws Exception {
40 | if (new GregorianCalendar().get(Calendar.MONTH) == item.getBirthday().get(Calendar.MONTH)) {
41 | log.info("Customer {} matched the birthday filter", item);
42 | return item;
43 | }
44 | return null;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/com/toptal/Customer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 Alexey Saenko
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package com.toptal;
25 |
26 | import java.io.Serializable;
27 | import java.util.Calendar;
28 | import lombok.Data;
29 |
30 | /**
31 | * Data class.
32 | *
33 | * @author Alexey Saenko (alexey.saenko@gmail.com)
34 | */
35 | @Data
36 | public class Customer implements Serializable {
37 |
38 | private int id;
39 | private String name;
40 | private Calendar birthday;
41 | private int transactions;
42 |
43 | @Override
44 | public String toString() {
45 | return String.format(
46 | "#%s, %s born on %3$tb %3$te, %3$tY, finished %4$s transactions",
47 | id,
48 | name,
49 | birthday.getTime(),
50 | transactions
51 | );
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/toptal/CustomerItemReader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 Alexey Saenko
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package com.toptal;
25 |
26 | import java.beans.XMLDecoder;
27 | import java.io.FileInputStream;
28 | import java.io.FileNotFoundException;
29 | import java.util.List;
30 | import lombok.extern.slf4j.Slf4j;
31 | import org.springframework.batch.item.ItemReader;
32 | import org.springframework.batch.item.support.IteratorItemReader;
33 |
34 | /**
35 | * Customer item reader.
36 | *
37 | * @author Alexey Saenko (alexey.saenko@gmail.com)
38 | */
39 | @Slf4j
40 | public class CustomerItemReader implements ItemReader {
41 |
42 | private final String filename;
43 |
44 | private ItemReader delegate;
45 |
46 | public CustomerItemReader(final String filename) {
47 | this.filename = filename;
48 | }
49 |
50 | @Override
51 | public Customer read() throws Exception {
52 | if (delegate == null) {
53 | log.info("Creating iterator item reader");
54 | delegate = new IteratorItemReader<>(customers());
55 | }
56 | log.info("Reading next customer");
57 | return delegate.read();
58 | }
59 |
60 | private List customers() throws FileNotFoundException {
61 | try (XMLDecoder decoder = new XMLDecoder(new FileInputStream(filename))) {
62 | return (List) decoder.readObject();
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/toptal/CustomerItemWriter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 Alexey Saenko
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package com.toptal;
25 |
26 | import java.io.Closeable;
27 | import java.io.FileNotFoundException;
28 | import java.io.FileOutputStream;
29 | import java.io.IOException;
30 | import java.io.OutputStream;
31 | import java.io.PrintWriter;
32 | import java.util.List;
33 | import javax.annotation.PreDestroy;
34 | import lombok.extern.slf4j.Slf4j;
35 | import org.springframework.batch.item.ItemWriter;
36 |
37 | /**
38 | * Customer item writer.
39 | *
40 | * @author Alexey Saenko (alexey.saenko@gmail.com)
41 | */
42 | @Slf4j
43 | public class CustomerItemWriter implements ItemWriter, Closeable {
44 | private final PrintWriter writer;
45 |
46 | public CustomerItemWriter() {
47 | OutputStream out;
48 | try {
49 | out = new FileOutputStream("output.txt");
50 | } catch (FileNotFoundException e) {
51 | out = System.out;
52 | }
53 | this.writer = new PrintWriter(out);
54 | }
55 |
56 | @Override
57 | public void write(final List extends Customer> items) throws Exception {
58 | for (Customer item : items) {
59 | writer.println(item.toString());
60 | }
61 | }
62 |
63 | @PreDestroy
64 | @Override
65 | public void close() throws IOException {
66 | writer.close();
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/com/toptal/CustomerReportJobConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 Alexey Saenko
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package com.toptal;
25 |
26 | import java.util.Arrays;
27 | import javax.annotation.PreDestroy;
28 | import lombok.extern.slf4j.Slf4j;
29 | import org.springframework.batch.core.Job;
30 | import org.springframework.batch.core.JobExecution;
31 | import org.springframework.batch.core.JobParametersBuilder;
32 | import org.springframework.batch.core.Step;
33 | import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
34 | import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
35 | import org.springframework.batch.core.configuration.annotation.StepScope;
36 | import org.springframework.batch.core.explore.JobExplorer;
37 | import org.springframework.batch.core.launch.JobLauncher;
38 | import org.springframework.batch.core.launch.NoSuchJobException;
39 | import org.springframework.batch.core.step.tasklet.Tasklet;
40 | import org.springframework.batch.item.ItemProcessor;
41 | import org.springframework.batch.item.ItemReader;
42 | import org.springframework.batch.item.ItemWriter;
43 | import org.springframework.batch.item.support.CompositeItemProcessor;
44 | import org.springframework.batch.repeat.RepeatStatus;
45 | import org.springframework.beans.factory.annotation.Autowired;
46 | import org.springframework.context.annotation.Bean;
47 | import org.springframework.context.annotation.Configuration;
48 | import org.springframework.scheduling.annotation.Scheduled;
49 |
50 | /**
51 | * Job configuration.
52 | *
53 | * @author Alexey Saenko (alexey.saenko@gmail.com)
54 | */
55 | @Slf4j
56 | @Configuration
57 | public class CustomerReportJobConfig {
58 |
59 | public static final String TASKLET_STEP = "taskletStep";
60 |
61 | public static final String XML_FILE = "database.xml";
62 |
63 | private static final String JOB_NAME = "customerReportJob";
64 |
65 | @Autowired
66 | private JobLauncher jobLauncher;
67 |
68 | @Autowired
69 | private JobBuilderFactory jobBuilders;
70 |
71 | @Autowired
72 | private StepBuilderFactory stepBuilders;
73 |
74 | @Autowired
75 | private JobExplorer jobs;
76 |
77 | @PreDestroy
78 | public void destroy() throws NoSuchJobException {
79 | jobs.getJobNames().forEach(name -> log.info("job name: {}", name));
80 | jobs.getJobInstances(JOB_NAME, 0, jobs.getJobInstanceCount(JOB_NAME)).forEach(
81 | jobInstance -> {
82 | log.info("job instance id {}", jobInstance.getInstanceId());
83 | }
84 | );
85 |
86 | }
87 |
88 | @Scheduled(fixedRate = 5000)
89 | public void run() throws Exception {
90 | JobExecution execution = jobLauncher.run(
91 | customerReportJob(),
92 | new JobParametersBuilder().addLong("uniqueness", System.nanoTime()).toJobParameters()
93 | );
94 | log.info("Exit status: {}", execution.getStatus());
95 | }
96 |
97 | @Bean
98 | public Job customerReportJob() {
99 | return jobBuilders.get(JOB_NAME)
100 | .start(taskletStep())
101 | .next(chunkStep())
102 | .build();
103 | }
104 |
105 | @Bean
106 | public Step taskletStep() {
107 | return stepBuilders.get(TASKLET_STEP)
108 | .tasklet(tasklet())
109 | .build();
110 | }
111 |
112 | @Bean
113 | public Step chunkStep() {
114 | return stepBuilders.get("chunkStep")
115 | .chunk(20)
116 | .reader(reader())
117 | .processor(processor())
118 | .writer(writer())
119 | .build();
120 | }
121 |
122 | @StepScope
123 | @Bean
124 | public ItemReader reader() {
125 | return new CustomerItemReader(XML_FILE);
126 | }
127 |
128 | @StepScope
129 | @Bean
130 | public ItemProcessor processor() {
131 | final CompositeItemProcessor processor = new CompositeItemProcessor<>();
132 | processor.setDelegates(Arrays.asList(birthdayFilterProcessor(), transactionValidatingProcessor()));
133 | return processor;
134 | }
135 |
136 | @StepScope
137 | @Bean
138 | public BirthdayFilterProcessor birthdayFilterProcessor() {
139 | return new BirthdayFilterProcessor();
140 | }
141 |
142 | @StepScope
143 | @Bean
144 | public TransactionValidatingProcessor transactionValidatingProcessor() {
145 | return new TransactionValidatingProcessor(5);
146 | }
147 |
148 | @StepScope
149 | @Bean
150 | public ItemWriter writer() {
151 | return new CustomerItemWriter();
152 | }
153 |
154 | @Bean
155 | public Tasklet tasklet() {
156 | return (contribution, chunkContext) -> {
157 | log.info("Executing tasklet step");
158 | return RepeatStatus.FINISHED;
159 | };
160 | }
161 |
162 | }
163 |
--------------------------------------------------------------------------------
/src/main/java/com/toptal/TransactionValidatingProcessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 Alexey Saenko
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package com.toptal;
25 |
26 | import lombok.extern.slf4j.Slf4j;
27 | import org.springframework.batch.item.validator.ValidatingItemProcessor;
28 | import org.springframework.batch.item.validator.ValidationException;
29 |
30 | /**
31 | * Processor filters customers by the amount of transactions.
32 | *
33 | * @author Alexey Saenko (alexey.saenko@gmail.com)
34 | */
35 | @Slf4j
36 | public class TransactionValidatingProcessor extends ValidatingItemProcessor {
37 | public TransactionValidatingProcessor(final int limit) {
38 | super(
39 | item -> {
40 | if (item.getTransactions() >= limit) {
41 | throw new ValidationException("Customer has more than " + limit + " transactions");
42 | }
43 | log.info("Customer {} matched the transaction filter", item);
44 | }
45 | );
46 | setFilter(true);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.batch.job.enabled=false
2 |
--------------------------------------------------------------------------------
/src/test/java/com/toptal/BatchApplicationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 Alexey Saenko
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package com.toptal;
25 |
26 | import org.junit.Test;
27 | import org.junit.runner.RunWith;
28 | import org.springframework.test.context.ContextConfiguration;
29 | import org.springframework.test.context.junit4.SpringRunner;
30 |
31 | /**
32 | * Tests for the application.
33 | *
34 | * @author Alexey Saenko (alexey.saenko@gmail.com)
35 | */
36 | @RunWith(SpringRunner.class)
37 | @ContextConfiguration(classes = {BatchApplication.class, BatchTestConfiguration.class})
38 | public class BatchApplicationTest {
39 |
40 | @Test
41 | public void contextLoads() {
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/com/toptal/BatchTestConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 Alexey Saenko
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package com.toptal;
25 |
26 | import org.springframework.batch.test.JobLauncherTestUtils;
27 | import org.springframework.context.annotation.Bean;
28 | import org.springframework.context.annotation.Configuration;
29 |
30 | /**
31 | * Test configuration.
32 | *
33 | * @author Alexey Saenko (alexey.saenko@gmail.com)
34 | */
35 | @Configuration
36 | public class BatchTestConfiguration {
37 | @Bean
38 | public JobLauncherTestUtils jobLauncherTestUtils() {
39 | return new JobLauncherTestUtils();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/java/com/toptal/BirthdayFilterProcessorTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 Alexey Saenko
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package com.toptal;
25 |
26 | import java.util.Calendar;
27 | import java.util.GregorianCalendar;
28 | import java.util.concurrent.Callable;
29 | import lombok.extern.slf4j.Slf4j;
30 | import org.junit.Assert;
31 | import org.junit.Test;
32 | import org.junit.runner.RunWith;
33 | import org.springframework.batch.core.StepExecution;
34 | import org.springframework.batch.test.MetaDataInstanceFactory;
35 | import org.springframework.batch.test.StepScopeTestExecutionListener;
36 | import org.springframework.batch.test.StepScopeTestUtils;
37 | import org.springframework.beans.factory.annotation.Autowired;
38 | import org.springframework.test.context.ContextConfiguration;
39 | import org.springframework.test.context.TestExecutionListeners;
40 | import org.springframework.test.context.junit4.SpringRunner;
41 | import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
42 |
43 | /**
44 | * Tests for {@link BirthdayFilterProcessor}.
45 | *
46 | * @author Alexey Saenko (alexey.saenko@gmail.com)
47 | */
48 | @Slf4j
49 | @RunWith(SpringRunner.class)
50 | @TestExecutionListeners({DependencyInjectionTestExecutionListener.class, StepScopeTestExecutionListener.class})
51 | @ContextConfiguration(classes = {BatchApplication.class, BatchTestConfiguration.class})
52 | public class BirthdayFilterProcessorTest {
53 |
54 | @Autowired
55 | private BirthdayFilterProcessor processor;
56 |
57 | public StepExecution getStepExecution() {
58 | log.info("getting step execution");
59 | return MetaDataInstanceFactory.createStepExecution();
60 | }
61 |
62 | @Test
63 | public void filter() throws Exception {
64 | final Customer customer = new Customer();
65 | customer.setId(1);
66 | customer.setName("name");
67 | customer.setBirthday(new GregorianCalendar());
68 | Assert.assertNotNull(processor.process(customer));
69 | }
70 |
71 | @Test
72 | public void filterId() throws Exception {
73 | final Customer customer = new Customer();
74 | customer.setId(1);
75 | customer.setName("name");
76 | customer.setBirthday(new GregorianCalendar());
77 | final int id = StepScopeTestUtils.doInStepScope(
78 | getStepExecution(),
79 | () -> processor.process(customer).getId()
80 | );
81 | Assert.assertEquals(1, id);
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/test/java/com/toptal/CustomerReportJobConfigTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 Alexey Saenko
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package com.toptal;
25 |
26 | import org.junit.Assert;
27 | import org.junit.Test;
28 | import org.junit.runner.RunWith;
29 | import org.springframework.batch.core.BatchStatus;
30 | import org.springframework.batch.core.JobExecution;
31 | import org.springframework.batch.test.JobLauncherTestUtils;
32 | import org.springframework.beans.factory.annotation.Autowired;
33 | import org.springframework.test.context.ContextConfiguration;
34 | import org.springframework.test.context.junit4.SpringRunner;
35 |
36 | /**
37 | * Tests for {@link CustomerReportJobConfig}.
38 | *
39 | * @author Alexey Saenko (alexey.saenko@gmail.com)
40 | */
41 | @RunWith(SpringRunner.class)
42 | @ContextConfiguration(classes = {BatchApplication.class, BatchTestConfiguration.class})
43 | public class CustomerReportJobConfigTest {
44 |
45 | @Autowired
46 | private JobLauncherTestUtils testUtils;
47 |
48 | @Autowired
49 | private CustomerReportJobConfig config;
50 |
51 | @Test
52 | public void testEntireJob() throws Exception {
53 | final JobExecution result = testUtils.getJobLauncher().run(config.customerReportJob(), testUtils.getUniqueJobParameters());
54 | Assert.assertNotNull(result);
55 | Assert.assertEquals(BatchStatus.COMPLETED, result.getStatus());
56 | }
57 |
58 | @Test
59 | public void testSpecificStep() {
60 | Assert.assertEquals(BatchStatus.COMPLETED, testUtils.launchStep(CustomerReportJobConfig.TASKLET_STEP).getStatus());
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------