Spring Batch Configuration - Jobs, Steps, Listeners - සිංහලෙන් ඉගෙනගමු! (SC Guide)

Spring Batch Configuration Demystified: Jobs, Steps, Listeners - සිංහලෙන් ඉගෙනගමු!
ආයුබෝවන්! ඩිවලොපර් කට්ටිය කොහොමද? ✋ අද අපි කතා කරන්න යන්නේ ඔයාලා ගොඩක් දෙනෙක්ට ප්රයෝජනවත් වෙන, ඒ වගේම ටිකක් සංකීර්ණ වෙන්න පුළුවන් මාතෘකාවක් ගැන - ඒ තමයි Spring Batch Configuration. ලංකාවේ ව්යාපාර වලදී, විශේෂයෙන් බැංකු, ටෙලිකොම්, රජයේ ආයතන වගේ තැන් වල දත්ත විශාල ප්රමාණයක් එකවර සකසන්න (process) සිද්ධ වෙන වෙලාවල් ගොඩක් තියෙනවා නේද? උදාහරණයක් විදිහට, මාසෙ අන්තිමට බිල්පත් හදනකොට, පාරිභෝගික දත්ත විශාල ප්රමාණයක් update කරනකොට, හෝ දත්ත ගබඩා දෙකක් අතර දත්ත synchronize කරනකොට වගේ අවස්ථාවලදී මේ වගේ Batch Processing Solutions ඉතාම වැදගත් වෙනවා.
අන්න ඒ වගේ අවස්ථාවලදී අපේ ජීවිතේ පහසු කරන්න Spring Framework එකේ තියෙන සුපිරිම මොඩියුලයක් තමයි Spring Batch කියන්නේ. මේකෙන් පුළුවන් data large volumes efficiently, robustly, and with high-performance processing කරන්න. ඒ කියන්නේ මොනවා හරි අවුලක් උනොත් නැවත පණගන්වන්න (restart) පුළුවන් විදිහට, log කරන්න පුළුවන් විදිහට, සහ error handling එක්ක batch jobs හදන්න මේක අපිට ලොකු සහයෝගයක් දෙනවා.
අද අපි බලමු කොහොමද Spring Batch එකේ Jobs, Steps, සහ Listeners හරි විදිහට configure කරගන්නේ කියලා. ඒ වගේම, අපේ batch job එකට custom listener එකක් කොහොමද එකතු කරන්නේ කියලා Practical විදිහට බලමු. එහෙනම්, අපි පටන් ගමුද? 🚀
1. Spring Batch කියන්නේ මොකක්ද? (What is Spring Batch?)
සරලවම කිව්වොත්, Spring Batch කියන්නේ enterprise-scale batch applications හදන්න උදව් කරන lightweight, comprehensive framework එකක්. මේකෙන් අපිට re-usable functions ගොඩක් ලැබෙනවා, ඒ නිසා අපිට boilerplate code ලියන්න ඕනේ නැහැ. ඒ වගේම, transaction management, chunk-based processing, restartability, skip and retry වගේ වැදගත් features මේකෙන් අපිට ලැබෙනවා.
Spring Batch වල මූලිකම සංකල්ප කීපයක් තියෙනවා. ඒවා තමයි:
- Job: මේක තමයි මුළු batch process එකම නියෝජනය කරන්නේ. Job එකක් කියන්නේ එකක් හෝ කීපයක් Steps වල එකතුවක්.
- Step: Job එකක් ඇතුළේ තියෙන ස්වාධීන, අනුපිළිවෙලකට ක්රියාත්මක වන වැඩ කොටසක් තමයි Step එකක් කියන්නේ. උදාහරණයක් විදිහට, data read කරන එක, process කරන එක, write කරන එක වගේ දේවල් Step එකක් වෙන්න පුළුවන්.
- ItemReader: දත්ත කියවන්න (read) පාවිච්චි කරන interface එක. (e.g., File, Database, API)
- ItemProcessor: කියවපු දත්ත modify කරන්න හෝ filter කරන්න පාවිච්චි කරන interface එක. (optional)
- ItemWriter: process කරපු දත්ත ලියන්න (write) පාවිච්චි කරන interface එක. (e.g., Database, File)
මේවා තමයි Batch Job එකක හරය. දැන් අපි බලමු මේවා කොහොමද configure කරන්නේ කියලා.
2. Job සහ Step Configuration (Configuring Jobs and Steps)
Spring Batch වල Jobs සහ Steps configure කරන්න පුළුවන් ප්රධාන ක්රම දෙකක් තියෙනවා: XML Configuration සහ Java Configuration. අද කාලේ ගොඩක් දුරට Java Configuration තමයි පාවිච්චි කරන්නේ, මොකද ඒක වඩාත් readable සහ maintainable නිසා. අපිත් Java config පාවිච්චි කරමු.
මුලින්ම, අපිට Spring Boot project එකක් අවශ්යයි. `spring-boot-starter-batch` dependency එක `pom.xml` එකට එකතු කරන්න ඕනේ:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
දැන් අපි බලමු කොහොමද සරල Job එකක් සහ Step එකක් configure කරන්නේ කියලා. අපි හිතමු අපිට CSV file එකකින් user data read කරලා, ඒවා Upper Case වලට convert කරලා, අලුත් CSV file එකකට ලියන්න ඕනේ කියලා.
පළමුව, Configuration Class එකක් හදමු:
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.item.file.builder.FlatFileItemWriterBuilder;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
@Configuration
@EnableBatchProcessing
public class BatchConfig {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
public BatchConfig(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
this.jobBuilderFactory = jobBuilderFactory;
this.stepBuilderFactory = stepBuilderFactory;
}
// 1. ItemReader: CSV එකෙන් දත්ත කියවන්න
@Bean
public ItemReader<User> csvUserReader() {
return new FlatFileItemReaderBuilder<User>()
.name("csvUserReader")
.resource(new ClassPathResource("users.csv")) // මේක resources folder එකේ තියෙන්න ඕනේ
.delimited()
.names(new String[]{"firstName", "lastName"})
.fieldSetMapper(new BeanWrapperFieldSetMapper<>() {{
setTargetType(User.class);
}})
.build();
}
// 2. ItemProcessor: දත්ත process කරන්න (Upper Case වලට හරවන්න)
@Bean
public ItemProcessor<User, User> userItemProcessor() {
return user -> {
user.setFirstName(user.getFirstName().toUpperCase());
user.setLastName(user.getLastName().toUpperCase());
return user;
};
}
// 3. ItemWriter: process කරපු දත්ත අලුත් CSV එකකට ලියන්න
@Bean
public ItemWriter<User> csvUserWriter() {
return new FlatFileItemWriterBuilder<User>()
.name("csvUserWriter")
.resource(new FileSystemResource("output_users.csv")) // output file එක
.delimited()
.names(new String[]{"firstName", "lastName"})
.build();
}
// Step Configuration
@Bean
public Step processUsersStep(ItemReader<User> csvUserReader,
ItemProcessor<User, User> userItemProcessor,
ItemWriter<User> csvUserWriter) {
return stepBuilderFactory.get("processUsersStep")
.<User, User>chunk(10) // එකවර process කරන records ගාන
.reader(csvUserReader)
.processor(userItemProcessor)
.writer(csvUserWriter)
.build();
}
// Job Configuration
@Bean
public Job importUserJob(Step processUsersStep) {
return jobBuilderFactory.get("importUserJob")
.incrementer(new RunIdIncrementer()) // Job එක run කරන හැම වෙලාවෙම අලුත් instance එකක් හදන්න
.start(processUsersStep) // පටන් ගන්න step එක
.build();
}
// User Class එක (POJO)
public static class User {
private String firstName;
private String lastName;
// Getters and Setters
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
@Override
public String toString() {
return "User{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
}
මේ Code එකේ තියෙන්නේ, `csvUserReader` කියන ItemReader එකෙන් `users.csv` file එක කියවලා, `userItemProcessor` එකෙන් ඒ දත්ත Upper Case වලට හරවලා, අන්තිමට `csvUserWriter` එකෙන් `output_users.csv` file එකට ලියන විදිහ. මේ හැමදේම `processUsersStep` කියන Step එක ඇතුලේ සිද්ධ වෙනවා. මේ Step එක `importUserJob` කියන Job එකට අයිති වෙනවා.
`users.csv` file එක `src/main/resources` එකේ හදන්න:
firstName,lastName
Kasun,Perera
Nimal,Silva
Chamari,Fernando
මේ Job එක run කරන්න, ඔබට Spring Boot Application එකේ `main` method එකෙන් `JobLauncher` එක පාවිච්චි කරන්න පුළුවන්, නැත්නම් Spring Batch Admin වගේ UI එකක් පාවිච්චි කරන්න පුළුවන්. සරලවම, Spring Boot application එක start කරලා, `localhost:8080` වගේ port එකකට ගහලා, job එක trigger කරන්න පුළුවන්. (ඒකට වෙන controller එකක් හදන්න ඕනේ, අපි අද ඒක ගැන ගැඹුරින් කතා කරන්නේ නෑ.)
3. Listenerලාගේ වැඩේ (The Role of Listeners)
අපේ Batch Job එක දුවනකොට මොනවද වෙන්නේ කියලා දැනගන්න එක හරිම වැදගත්. Job එක පටන් ගත්තද, ඉවර උනාද, error එකක් ආවද, මොන item එකද fail උනේ වගේ දේවල් log කරන්න, notifications යවන්න, හෝ cleanup කරන්න වගේ දේවල් වලට තමයි Listeners පාවිච්චි කරන්නේ.
Spring Batch වල ගොඩක් Listener interfaces තියෙනවා:
- JobExecutionListener: Job එක පටන් ගන්න කලින් (`beforeJob`) සහ ඉවර වුනාට පස්සේ (`afterJob`) ක්රියාත්මක වෙනවා.
- StepExecutionListener: Step එක පටන් ගන්න කලින් (`beforeStep`) සහ ඉවර වුනාට පස්සේ (`afterStep`) ක්රියාත්මක වෙනවා.
- ItemReadListener: Item එකක් read කරන්න කලින්, read කරපු ගමන්, හෝ read එක fail වුනොත් ක්රියාත්මක වෙනවා.
- ItemProcessListener: Item එකක් process කරන්න කලින්, process කරපු ගමන්, හෝ process එක fail වුනොත් ක්රියාත්මක වෙනවා.
- ItemWriteListener: Item set එකක් write කරන්න කලින්, write කරපු ගමන්, හෝ write එක fail වුනොත් ක්රියාත්මක වෙනවා.
මේවා පාවිච්චි කරලා අපිට Batch Process එකේ flow එක සහ status එක ගැන සම්පූර්ණ අවබෝධයක් ගන්න පුළුවන්. දැන් අපි බලමු කොහොමද අපේම Custom Listener එකක් හදලා Job එකට එකතු කරගන්නේ කියලා.
4. Custom Listener එකක් හදමු (Let's Create a Custom Listener)
අපේ අභ්යාසය විදිහට, අපි Job එක පටන් ගන්න කලින් සහ ඉවර වුනාට පස්සේ message එකක් print කරන Custom JobExecutionListener
එකක් හදමු. මේකෙන් අපිට Job එකේ lifecycle එක control කරන්න පුළුවන් වෙන්නේ කොහොමද කියලා තේරුම් ගන්න පුළුවන්.
මුලින්ම, Listener class එක හදමු:
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.stereotype.Component;
@Component // Spring Bean එකක් විදිහට register කරන්න
public class MyJobCompletionNotificationListener implements JobExecutionListener {
@Override
public void beforeJob(JobExecution jobExecution) {
System.out.println("--------------------------------------------------");
System.out.println("💪 Batch Job එක පටන් ගන්නයි යන්නේ! Job Name: " + jobExecution.getJobInstance().getJobName());
System.out.println("Job Id: " + jobExecution.getJobId());
System.out.println("Start Time: " + jobExecution.getStartTime());
System.out.println("--------------------------------------------------");
}
@Override
public void afterJob(JobExecution jobExecution) {
System.out.println("--------------------------------------------------");
if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
System.out.println("✅ Batch Job එක සාර්ථකව අවසන් විය! Job Name: " + jobExecution.getJobInstance().getJobName());
} else if (jobExecution.getStatus() == BatchStatus.FAILED) {
System.err.println("❌ Batch Job එක අසාර්ථක විය! Job Name: " + jobExecution.getJobInstance().getJobName());
System.err.println("Exit Status: " + jobExecution.getExitStatus().getExitCode() + " - " + jobExecution.getExitStatus().getExitDescription());
jobExecution.getAllFailureExceptions().forEach(e -> System.err.println("Error: " + e.getMessage()));
} else {
System.out.println("⚠️ Batch Job එකේ තත්වය: " + jobExecution.getStatus() + " Job Name: " + jobExecution.getJobInstance().getJobName());
}
System.out.println("End Time: " + jobExecution.getEndTime());
System.out.println("Duration: " + (jobExecution.getEndTime().getTime() - jobExecution.getStartTime().getTime()) + " ms");
System.out.println("--------------------------------------------------");
}
}
මේ `MyJobCompletionNotificationListener` class එක `JobExecutionListener` interface එක implement කරනවා. ඒකෙන් `beforeJob` සහ `afterJob` කියන methods දෙක override කරලා තියෙනවා. මේ methods දෙක ඇතුලේ Job එකේ ආරම්භය සහ අවසානය ගැන විස්තර print කරනවා.
දැන් මේ Listener එක අපේ Job එකට එකතු කරමු. `BatchConfig` class එකේ `importUserJob` Bean එකට මේ Listener එක inject කරන්න:
// Job Configuration - Listener එක එකතු කිරීම
@Bean
public Job importUserJob(Step processUsersStep, MyJobCompletionNotificationListener listener) {
return jobBuilderFactory.get("importUserJob")
.incrementer(new RunIdIncrementer())
.listener(listener) // මෙන්න මෙතනින් listener එක add කරනවා!
.start(processUsersStep)
.build();
}
දැන් ඔයාගේ Spring Boot Application එක run කරලා බලන්න. Console එකේ output එකේ Job එක පටන් ගන්න කලින් සහ ඉවර වුනාට පස්සේ අපේ custom messages print වෙන්න ඕනේ. ඒ කියන්නේ, ඔයා සාර්ථකවම custom listener එකක් ඔයාගේ Spring Batch Job එකට එකතු කරලා තියෙනවා!
Tip: ඔබට `StepExecutionListener` එකක් හදන්නත් පුළුවන්, ඒකත් මේ වගේම තමයි. `StepExecutionListener` interface එක implement කරලා `beforeStep` සහ `afterStep` methods override කරන්න.
5. වැඩිදුරටත් (Further Exploration)
අද අපි Spring Batch වල මූලික Configuration, Jobs, Steps, ItemReaders, ItemProcessors, ItemWriters සහ Custom Listeners ගැන කතා කළා. මේක Spring Batch කියන ලොකු ලෝකයේ පොඩි කොටසක් විතරයි. ඔබට තවත් දේවල් ගොඩක් ඉගෙන ගන්න පුළුවන්:
- Error Handling: Skippable exceptions, retry mechanisms.
- Job Parameters: Job එකට runtime values pass කරන හැටි.
- Partitioning: Job එකක් threads ගොඩක run කරන හැටි.
- Database Integration: JDBC, JPA ItemReaders/Writers.
- Testing: Spring Batch Jobs test කරන හැටි.
මේ හැම දේම Spring Batch Docs වල හොඳට විස්තර කරලා තියෙනවා. ඒ නිසා එතනින් වැඩිදුරටත් ඉගෙන ගන්න පුළුවන්.
අවසාන වශයෙන් (In Conclusion)
ඉතිං, අද අපි Spring Batch Configuration ගැන හොඳ අවබෝධයක් ගත්තා කියලා හිතනවා. ලොකු දත්ත ප්රමාණයක් handle කරනකොට, Spring Batch කියන්නේ දේව වරමක් වගේ. Jobs, Steps, සහ Listeners හරි විදිහට configure කරගන්න පුළුවන් නම්, ඔබට robust, scalable batch applications පහසුවෙන් හදන්න පුළුවන්. 🛠️
මේ Article එක ඔයාට ප්රයෝජනවත් උනාද? ඔයා මේක practice කරලා බැලුවද? මොනවා හරි ප්රශ්න තියෙනවා නම්, පහලින් comment කරන්න. 💬 අපි උදව් කරන්න ලෑස්තියි! තවත් මේ වගේ informative technical articles එක්ක ඉක්මනින්ම හමු වෙමු. එතකන් හැමෝටම ජය වේවා! ආයුබෝවන්! 👋