[Spring Batch] When #Scheduled annotation is used, it is executed dynamically. Can I change it to run statically?
Because you mention #Scheduled, I imagine your batch is executed from a web application. If you want to run it out of the box, you can :
use Spring boot to start your batch by launching a spring boot application : I advise you to follow this tuto and replace the example batch by your own : https://spring.io/guides/gs/batch-processing/
start your batch manually from a classic java application which create a spring context at start (so spring boot does the job really better)
start your batch as a unit test (for integration purpose) : you can folow this tuto (which also use spring boot) :https://www.baeldung.com/spring-batch-testing-job
Good luck
I think I misunderstood your question.
If you want to run from static method there is one way to do this. You can make StaticJobInitializer component like this
#Component
public class StaticJobInitializer {
private JobRegistry jobRegistry;
private JobLauncher jobLauncher;
public StaticJobInitializer(JobRegistry jobRegistry, JobLauncher jobLauncher) {
this.jobRegistry = jobRegistry;
this.jobLauncher = jobLauncher;
}
#PostConstruct
public void init() {
StaticJobRun.setJobRegistry(jobRegistry);
StaticJobRun.setJobLauncher(jobLauncher);
}
}
public final class StaticJobRun {
private static JobRegistry jobRegistry;
private static JobLauncher jobLauncher;
public static JobRegistry getJobRegistry() {
return jobRegistry;
}
public static JobLauncher getJobLauncher() {
return jobLauncher;
}
public static void setJobLauncher(JobLauncher jobLauncher) {
StaticJobRun.jobLauncher = jobLauncher;
}
public static void setJobRegistry(JobRegistry jobRegistry) {
StaticJobRun.jobRegistry = jobRegistry;
}
}
and than your start job static method should be like this:
public static void startJob(){
Job job = StaticJobRun.getJobRegistry().getJob("job_name");
JobParameters jobParameters = new JobParametersBuilder()
.toJobParameters();
StaticJobRun.getJobLauncher().run(job, new JobParameters());
}
Related
There is spring-application used with spring-batch and spring-quartz
There is #Service class that launch spring-batch and named MyService.
#Service
#EnableBatchProcessing
#EnableScheduling
public class MyService {
#Autowired
JobLauncher jobLauncher;
#Autowired
Job processExportJob;
public void helloMethod() throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
JobParameters jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis())
.toJobParameters();
jobLauncher.run(processExportJob, jobParameters);
}
}
There is spring-quarts job and config that try to inject this serrvice and launch method helloMethod()
If job contains only logger, there is no problem and works okey.
Then, I try to inject my service in one of field. After every application launch, first time, job contains this field, but in next time there is null in this field.
I tried to just create my service by new:
MyService service = new MyService();
But in service all autowired fields are null after first successful launch
Application is deployed on webshere 8.5.5.13(cluster with 2 nodes), oracle11g and spring4. Java8.
Then, using the #Autowired annotation I inject the service into the job it this job works exactly once. The injected field is null during all the subsequent job executions.
Moreover, if I create the service outside spring:
MyService service = new MyService();
#DisallowConcurrentExecution
#PersistJobDataAfterExecution
public class MyJob implements Job {
#Autowired
private MyService service;
private static final String MESSAGE = "===================================QUARTZ TACT===================================";
private Logger logger = Logger.getLogger(getClass());
#Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.log(Level.INFO, MESSAGE);
try {
service.helloMethod();
} catch (Exception e) {
e.printStackTrace();
logger.log(Level.ERROR, " Failed..");
logger.log(Level.ERROR, Arrays.toString(e.getStackTrace()));
}
}
}
From different place is heplful to use this:
ApplicationContext springContext =
WebApplicationContextUtils.getWebApplicationContext(ContextLoaderListener.getCurrentWebApplicationContext().getServletContext());
Object bean = springContext.getBean("myService");
It solved my issue
I am using quartz in my apache isis project for scheduling. I have a class MyJob which implements org.quartz.Job and it has method execute which is called when scheduler triggers at given time.
My problem is, I have a class DemoService and it has a method showDemo() which I want to call from the execute method.
But when the scheduler runs, it throws Null Pointer Exception at demoService.showDemo().
I have not been able to inject any service in that class. It always gives NPE. How can I inject a service into the MyJob class?
Here is the code:-
public class MyJob implements Job {
#Inject
DemoService demoService;
public MyJob() {
}
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
demoService.showDemo();
}
}
The easiest approach is to put the logic you want to run in a subclass of AbstractIsisSessionTemplate, and then instantiate and execute it from your quartz job.
This technique is used by the Incode Platform's quartz job to run background commands, see here; the quartz module shows this from the quartz perspective (which I think you already have figured out).
HTH
Dan
Try this NullPointerException while deploying Quartz in Spring Boot
You need to use SpringBeanJobFactory to create Job with Spring's autowired beans.
class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
#Override
public Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job); //the magic is done here
return job;
}
}
And then when you do
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
scheduler = schedFact.getScheduler();
AutowiringSpringBeanJobFactory autowiringSpringBeanJobFactory = new AutowiringSpringBeanJobFactory();
autowiringSpringBeanJobFactory.setApplicationContext(applicationContext);
scheduler.setJobFactory(autowiringSpringBeanJobFactory);
All,
I have a configuration similar to the following.
#Configuration
#EnableBatchProcessing
public class MyConfiguration {
#Bean
protected Job myJob() { return <the-job>; }
#Bean
protected JobParameters params() { return <parameters>; }
}
I had assumed that this would use the JobParameters bean which I specified, but it does not. How do I use both #EnableBatchProcessing and JobParameters?
Thanks!
#EnableBatchProcessing provides you with some useful beans like the JobLauncher (further info). To inject your specified job parameters, pass them to the method actually launching your job.
#Autowired
JobLauncher jobLauncher;
public void startExecution() {
jobLauncher.run(job, jobParemeters);
}
If I understand correctly, you know how to access your jobParameters in your step configuration like this
#Bean
#StepScope
public SomeProcessor processor(#Value("#{jobParameters['someParameter']}") String param) {
...
}
#EnableBatchProcessing will run available jobs on startup by default. Setting
spring.batch.job.enabled=false
in your application.properties will disable this feature.
I'm using spring boot. I have a batch job which I've implemented with these classes :
My main class is :
#SpringBootApplication
#ComponentScan("com.batch")
#PropertySource("classpath:application.properties")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
My scheduler is :
#Component
#EnableScheduling
public class JobScheduler {
#Scheduled(fixedRate = 10000)
public void runJob() {
SpringApplication.run(MyBatchConfig.class);
}
}
and my batch configuration class is :
#Configuration
#EnableBatchProcessing
public class MyBatchConfig {
#Value("${database.driver}")
private String databaseDriver;
#Value("${database.url}")
private String databaseUrl;
#Value("${database.username}")
private String databaseUsername;
#Value("${database.password}")
private String databasePassword;
#Bean
public Job myJob(JobBuilderFactory jobs, Step s) {
Job job = jobs.get("myJob")
.incrementer(new RunIdIncrementer())
.flow(s)
.end()
.build();
return job;
}
#Bean
public Step myStep(StepBuilderFactory stepBuilderFactory, ItemReader<Account> reader,
ItemWriter<Person> writer, ItemProcessor<Account, Person> processor) {
TaskletStep step = stepBuilderFactory.get("myStep")
.<Account, Person>chunk(1)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
step.setAllowStartIfComplete(true);
return step;
} ...
now, my problem is :
the scheduler works and it repeats every ten seconds,
but the job executes only on application startup(reader, processor and writer just execute once in startup) and it seems that
SpringApplication.run(MyBatchConfig.class);
has no effect on re-running the job.
what should I do?
Thanks in advance
This is what I can think of,
1.You put this property in application.properties so your batch job doesn't start automatically by call of SpringApplication.run(...) from mainmethod.
spring.batch.job.enabled=false
This will simply initialize your Spring Batch configuration and not actually start job.
2.Put annotation #EnableScheduling on your Spring Boot Batch Job starting class i.e. on Application class in your code.
3.Remove #EnableScheduling annotation from JobScheduler class and call , jobLauncher.run(job, jobParameters) from runJob() instead of calling SpringApplication.run(MyBatchConfig.class).
JobLauncher & Job beans can be auto wired to your scheduler class since context is already initialized.
Hope it helps !!
You need to create JobLauncher bean and use that in scheduler for starting new job instances.
I am trying to test my spring batch application with JUnit.
I have setup the project and it is working fine when running using the normal java main method. But when i try to write JUnit test and trying to execute using SpringJUnit4ClassRunner, it is not executing the step that belongs to the Job. Following is the JUnit test case i have written.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = CASConfig.class)
public class DailyDataPreparationStepTest {
private static final Logger LOGGER = LoggerFactory.getLogger(DailyDataPreparationStepTest.class);
#Autowired
private ApplicationContext applicationContext;
#Autowired
private Environment environment;
#Autowired
private JobLauncher jobLauncher;
#Autowired
private JobRepository jobRepository;
#Autowired
private Job dailyDataPrepJob;
#Autowired
private DailyDataPreparationStep dailyDataPreparationStep;
//
private JobLauncherTestUtils jobLauncherTestUtils;
static{
System.setProperty("env", "test");
}
#Before
public void setUp() throws Exception {
jobLauncherTestUtils = new JobLauncherTestUtils();
jobLauncherTestUtils.setJobLauncher(jobLauncher);
jobLauncherTestUtils.setJobRepository(jobRepository);
jobLauncherTestUtils.setJob(dailyDataPrepJob);
}
#After
public void tearDown() throws Exception {
}
#Test
public void testJobExecution(){
try {
jobLauncherTestUtils.launchJob(this.jobParameters("1", "System", DateUtil.getCurrentDateInYYYYMMDD(), "log.log"));
} catch (Exception e) {
e.printStackTrace();
}
}
private JobParameters jobParameters(String jobId, String userId, String executionDate, String jobLogFileName){
return new JobParametersBuilder()
.addString(Constants.BatchJobParameter.PARAM_EXECUTION_DATE, executionDate)
.addString(Constants.BatchJobParameter.PARAM_JOBID, jobId)
.addString(Constants.BatchJobParameter.PARAM_JOBLOGFILENAME, jobLogFileName)
.addString(Constants.BatchJobParameter.PARAM_USERID, userId)
.addDate(Constants.BatchJobParameter.PARAM_TIMESTAMP, Calendar.getInstance().getTime())
.toJobParameters();
}
}
Following code snipt shows the Job configuration
#Bean(name = "dailyDataPrepJob")
public Job dailyDataPreparationJob() throws Exception{
return jobBuilderFactory.get("dailyDataPrepJob")
.start(dailyDataPrepStep)
.incrementer(new RunIdIncrementer())
.listener(jobExecutionListener)
.build();
}
Can anyone give some idea what's going on while executing the job via SpringJUnit4ClassRunner?