I have a batch file which is running through windows task scheduler and it will he execute the jar file every 10 hours repeately.
Now, I'm trying to run the same batch file using spring boot batch process as a task scheduler. but i didn't get solution for this.
How can i resolve the this issue?
In your schedule method, try like this:
Runtime.getRuntime().exec("file.bat");
Did you try this https://spring.io/guides/gs/scheduling-tasks/
Spring Boot scheduler can schedule your tasks and accepts CRON like expression to scheduling recurring tasks.
You can use Java ProcessBuilder API to trigger your batch (.bat) file
You can implement simple Spring scheduling tasks as following
#Component
public class RunCommandScheduledTask {
private final ProcessBuilder builder;
private final Logger logger; // Slf4j in this example
// inject location/path of the bat file
#Inject
public RunCommandScheduledTask(#Value("${bat.file.path.property}") String pathToBatFile) {
this.builder = new ProcessBuilder(pathToBatFile);
this.logger = LoggerFactory.getLogger("RunCommandScheduledTask");
}
// cron to execute each 10h
#Scheduled(cron = "0 */10 * * *")
public void runExternalCommand() {
try {
final Process process = builder.start();
if (logger.isDebugEnabled()) {
// pipe command output and proxy it into log
try (BufferedReader out = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.ISO_8859_1))) {
String str = null;
for (;;) {
str = out.readLine();
if (null == str)
break;
logger.debug(str);
}
}
}
process.waitFor();
} catch (IOException exc) {
logger.error("Can not execute external command", exc);
} catch (InterruptedException exc) {
Thread.currentThread().interrupt();
}
}
}
.....
#SpringBootApplication
#EnableScheduling
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class);
}
}
Related
I have a spring boot java service I have to schedule to run on a particular time. I have enabled the #Enablescheduling and #Scheduled annotation and given the cron expression.
It's working fine. The scheduler is running at the expected time. But my concern is I should control the cron expression somewhere from outside my jar file. I have tried using it in property file but when packaging my property file also getting included in that.
Sample code:
#PostMapping(path = "getoktatodynamodb")
#Scheduled(cron = "0 0/5 0 * * ?")
#ApiOperation("Sync data to DynamoDB")
public FinalResponse getdatatodynamodb() {
FinalResponse finalResponse = new FinalResponse();
try {
LOGGER.info("Sync data to DynamoDB starts - " + new Date());
finalResponse = dynamodbuserService.dynamoDbSync();
} catch (MyRestTemplateException ex) {
LOGGER.error(ex.getMessage());
finalResponse.setResponseMessage(ex.getMessage());
finalResponse.setStatusCode(ex.getStatusCode().value());
} catch (Exception execption) {
LOGGER.error(execption.getMessage());
finalResponse.setResponseMessage(execption.getMessage());
finalResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
} finally {
LOGGER.info("Sync data DynamoDB Ends - " + new Date());
}
return finalResponse;
}
The main intention is scheduler should be in our control whenever we need to change the time it should be configurable. No code change and restarting the scheduler for minor changes.
How should we achieve this also we would like to schedule this in linux ec2 instance? in case if we have better suggestion to achieve this kindly share it.
You can implement SchedulingConfigurer:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/SchedulingConfigurer.html
This DZone article shows a really good example: https://dzone.com/articles/schedulers-in-java-and-spring which I'm showing here in case the article doesn't stay permanent.
#Configuration
#EnableScheduling
public class ScheduledConfiguration implements SchedulingConfigurer {
TaskScheduler taskScheduler;
private ScheduledFuture<?> job1;
private ScheduledFuture<?> job2;
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler =new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(10);// Set the pool of threads
threadPoolTaskScheduler.setThreadNamePrefix("scheduler-thread");
threadPoolTaskScheduler.initialize();
job1(threadPoolTaskScheduler);// Assign the job1 to the scheduler
// Assign the job1 to the scheduler
this.taskScheduler=threadPoolTaskScheduler;// this will be used in later part of the article during refreshing the cron expression dynamically
taskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
private void job1(TaskScheduler scheduler) {
job1 = scheduler.schedule(new Runnable() {
#Override
public void run() {
System.out.println(Thread.currentThread().getName() + " The Task1 executed at " + new Date());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, new Trigger() {
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
String cronExp = "0/5 * * * * ?";// Can be pulled from a db .
return new CronTrigger(cronExp).nextExecutionTime(triggerContext);
}
});
}
private void job2(TaskScheduler scheduler){
job2=scheduler.schedule(new Runnable(){
#Override
public void run() {
System.out.println(Thread.currentThread().getName()+" The Task2 executed at "+ new Date());
}
}, new Trigger(){
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
String cronExp="0/1 * * * * ?";//Can be pulled from a db . This will run every minute
return new CronTrigger(cronExp).nextExecutionTime(triggerContext);
}
});
}
}
I am currently trying to create a scheduled task, that runs my Jar file every minute. the command line code to create the task looks like this:
schtasks /Create /tn Testname /sc Minute /tr C:\Users\MyUser\Desktop\Program.jar
no matter how long I wait, nothing happens. The Scheduled Task GUI shows that the result of my task is (0x1)
Howwever if I run C:\Users\MyUser\Desktop\Program.jar in the CMD everything works fine
What am I doing wrong?
I was able to create the scheduled task via Java, I dont know if this will help somebody in the future, but just in case I am gonna post my code anyway:
private void createScheduledTask(String taskName) {
List<String> commandList = new ArrayList<String>();
commandList.add("schtasks.exe");
commandList.add("/Create");
commandList.add("/tn");
commandList.add(taskName);
commandList.add("/sc");
commandList.add("Minute");
commandList.add("/tr");
commandList.add("java -jar C:\\Users\\MyUser\\Desktop\\Program.jar REPLACE_WITH_ARGUMENT");
try {
int returnValue = executeCMDStatement(commandList);
if(returnValue == 0){
//everything should have worked out
} else {
//there was an error
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static int executeCMDStatement(List<String> commandList) throws InterruptedException, IOException {
ProcessBuilder builder = new ProcessBuilder(commandList);
Process process = builder.start();
process.waitFor(3, TimeUnit.SECONDS);
return process.exitValue();
}
I tried to find it, but without results. I'd like to have object having path to bash script and cron expression specifying when to run it. It's SpringBoot project. I see it like this:
public class TestScript {
private String cronExpression;
private String pathToFile;
public void execute() {
// either it's #Scheduled or execute another way
}
}
Is it possible to do? Please guide me even a little if you can.
Ok, I managed to make my custom service that dynamically creates jobs:
#Service
public class DynamicJob {
public void schedule(TestScript testScript) {
try {
JobDetail job = JobBuilder.newJob(TestScript.class)
.withIdentity(testScript.getName(), "default group")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(testScript.getName().concat(" trigger"), "groupAll")
.withSchedule(CronScheduleBuilder.cronSchedule(testScript.getCronExpression()))
.build();
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);
} catch (Exception e) {
e.printStackTrace();
}
}
}
TestScript class implements org.quartz.Job and I use quartz library version 2.2.1
I am new to Spring Batch framework and quartz scheduler. My task is to schedule a new Spring Batch job dynamically using quartz scheduler. All new spring batch job's entries are in my database with trigger expression. Problem is that for every new spring batch job coming from database , we need to wrap it in quartz's scheduler job. so means as many spring batch job will be there that many batch job class should be there to wrap them and run by quartz scheduler.
quartz is storing all the job's and trigger entry into its own data base tables. which i have configure in config file. This job will always be quartz's job not spring batch job.
here is my main method, here i will write my data base connection code to find out new springbatch job name and trigger expression and will bind them with quartz schedular
public static void main(String[] args) {
try {
ApplicationContext context = new ClassPathXmlApplicationContext("quartz-context.xml");
JobLauncher launcher=(JobLauncher) context.getBean("jobLauncher");
JobLocator locator= (JobLocator) context.getBean("jobRegistry");
Scheduler schedulerFactoryBean=(Scheduler) context.getBean("quartzSchedulerFactoryBean");
JobDetail job = newJob(SpringBatchJob.class).withIdentity("myJob001", "group1").build();
Trigger trigger1 =newTrigger().withIdentity("myTrigger001", "group1").startNow().withSchedule(simpleSchedule().withIntervalInSeconds(10).repeatForever()).build();
schedulerFactoryBean.scheduleJob(job, trigger1);
schedulerFactoryBean.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
Here we can see that we have jobDetail which is quartz job and whose execute method is use to run spring batch job.
springBatchjob.java
public class SpringBatchJob implements Job {
private String jobName;
private String batchJob;
private JobLocator jobLocator;
private JobLauncher jobLauncher;
private File contentDirectory;
private String directoryPath = "inputFiles";
public void init(){
contentDirectory = new File(directoryPath);
}
boolean fileFound = false;
public void performJob(String str) {}
public String getJobName() {
return jobName;
}
public void setBatchJob(String batchJob) {
this.batchJob = batchJob;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public void setJobLocator(JobLocator jobLocator) {
this.jobLocator = jobLocator;
}
public void setJobLauncher(JobLauncher jobLauncher) {
this.jobLauncher = jobLauncher;
}
#Override
public void execute(JobExecutionContext arg0) throws org.quartz.JobExecutionException {
JobParameter jb= new JobParameter(5L);
Map<String, JobParameter> map= new HashMap<>();
map.put(jobName,jb);
ApplicationContext context = new ClassPathXmlApplicationContext("quartz-context.xml");
JobLauncher launcher=(JobLauncher) context.getBean("jobLauncher");
JobLocator locator= (JobLocator) context.getBean("jobRegistry");
setJobLauncher(launcher);
setJobLocator(locator);
setJobName("helloWorldJob");
// TODO Auto-generated method stub
JobExecution result = null;
try {
result = jobLauncher.run(jobLocator.getJob(jobName), new JobParameters(map));
} catch (JobExecutionAlreadyRunningException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobRestartException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobInstanceAlreadyCompleteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobParametersInvalidException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchJobException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("ExamResult Job completetion details : "+result.toString());
}
here in setJobName method i am hard coding my spring batch job name ,
But in my project we are having nearly 800 jobs, so acc to approarch we need to make 800 wrapper classes.
Please help me , that how to resolve this solution by making generic class.
I sincerely hope you aren't really using that class, it will eventually eat all your memory as you are recreating your application over and over.
Spring already has support for quartz and especially the construction of jobs in the form of the SpringBeanJobFactory which you can use to your advantage especially if you extend it with some auto wiring capabilities.
public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private ApplicationContext context;
#Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object object = super.createJobInstance(bundle);
context.getAutowireCapableBeanFactory().autowireBean(object);
return object;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.context=applicationContext;
}
}
Then rewrite your job class as something like this
public class SpringBatchJob implements Job {
private final Logger logger = LoggerFactory.getLogger(SpringBatchJob.class);
private String jobName;
#Autowired
private JobLocator jobLocator;
#Autowired
private JobLauncher jobLauncher;
#Override
public void execute(JobExecutionContext context) throws org.quartz.JobExecutionException {
JobDataMap JobDataMap = context.getMergedJobDataMap();
JobParametersBuilder builder = new JobParametersBuilder();
for (Map.Entry<String, Object) param : jobDataMap.entrySet()) {
String key = param.getKey();
Object val = param.getValue();
builder.addString(key, String.valueOf(val)); // Or make it smarter by doing type detection.
}
Job jobToLaunch = jobLocator.getJob(jobName);
JobExecution result;
try {
result = jobLauncher.run(jobToLaunch, builder.to);
} catch (JobExecutionException e) {
throw new org.quartz.JobExecutionException("Exception execution job '"+this.jobName+"'", e);
} finally {
logger.info("{} Job completetion details ", this.jobName, result);
}
}
}
Now you need to configure the SchedulerFactoryBean to use the AutowiringSpringBeanJobFactory by setting the jobFactory property. When you done then you just need to configure the quartz jobs appropriately.
For the sample you posted the following will launch the helloWorldJob when it is triggered to launch.
public static void main(String[] args) {
try {
ApplicationContext context = new ClassPathXmlApplicationContext("quartz-context.xml");
JobLauncher launcher=(JobLauncher) context.getBean("jobLauncher");
JobLocator locator= (JobLocator) context.getBean("jobRegistry");
Scheduler schedulerFactoryBean=(Scheduler) context.getBean("quartzSchedulerFactoryBean");
JobDetail job = newJob(SpringBatchJob.class)
.withIdentity("myJob001", "group1")
.usingJobData("jobName", "helloWorldJob")
.build();
Trigger trigger1 =newTrigger().withIdentity("myTrigger001", "group1")
.startNow()
.withSchedule(simpleSchedule().withIntervalInSeconds(10).repeatForever())
.build();
schedulerFactoryBean.scheduleJob(job, trigger1);
schedulerFactoryBean.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
Notice the .usingJobData("jobName", "helloWorldJob"), the SpringBeanJobFactory will try to satisfy all setter methods on the SpringBatchJob class. This has a setJobName which will get injected with helloWorldJob when launched. The functionality is extended by the AutowiringSpringBeanJobFactory to also auto wire the needed infrastructure beans from Spring Batch.
If you need to pass additional properties to the Spring Batch job (like keys to use or other information, just add another usingJobData("your-property", your-value). The modified SpringBatchJob will map all quarts job parameters to Spring Batch parameters before launching the job.
Note: This was typed from the top of my head, I haven't actually tested this, but it should at least give you an idea on how to do this.
I am using Quartz CronScheduler to execute a job every 15 minutes. Every time the job executes it checks the DB for any change in the cron expression and updates the job scheduler for the next run. I have implemented the above in the following way:
#Service
public class QuartzSchedulerService {
private static final Logger LOG = LoggerFactory.getLogger(QuartzSchedulerService.class);
private Scheduler scheduler;
#PostConstruct
private void init() {
try {
scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
} catch (Exception e) {
LOG.error("Unable to start quartz scheduler", e);
}
}
#PreDestroy
private void destroy() {
try {
scheduler.shutdown();
} catch (Exception e) {
LOG.error("Unable to shutdown quartz scheduler", e);
}
}
public void registerCronJob(Class<? extends Job> jobClass, String cronExpression) {
try {
String jobName = jobClass.getSimpleName();
CronScheduleBuilder cronBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName).build();
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName).withSchedule(cronBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
LOG.info("Registered Cron Job:" + jobName + " " + jobDetail.getKey());
} catch (Exception e) {
LOG.error("Unable to register cron job", e);
}
}
public void updateCronSchedule(Class<? extends Job> jobClass, String cronExpression) {
try {
String jobName = jobClass.getSimpleName();
CronScheduleBuilder cronBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
CronTrigger newCronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName).withSchedule(cronBuilder).build();
scheduler.rescheduleJob(TriggerKey.triggerKey(jobName), newCronTrigger);
LOG.info("Updated Cron Job:" + jobName + " " + newCronTrigger.getJobKey());
LOG.info("Jobs executed: " + scheduler.getMetaData().getNumberOfJobsExecuted());
} catch (Exception e) {
LOG.error("Unable to reschedule cron job", e);
}
}
}
The class which implements the Job interface is as belows:
#Component
#DisallowConcurrentExecution
public class PropertiesReloadJob implements Job {
private static final Logger LOG = LoggerFactory.getLogger(PropertiesReloadJob.class);
#Autowired
private QuartzSchedulerService schedulerService;
#Autowired
private PropertiesService propertiesService;
public void loadAtStartup() {
load();
LOG.info("--- Registerting PropertiesReload Cron Job ---");
schedulerService.registerCronJob(PropertiesReloadJob.class, propertiesService.getCacheReloadCronExpression());
}
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
load();
LOG.info("--- Updating Pro Cron Job ---");
schedulerService.updateCronSchedule(PropertiesReloadJob.class, propertiesService.getCacheReloadCronExpression());
}
public void load(){
// Load properties from DB
}
The loadAtStartup() method is called during context initializtion and then after every 15 minutes the execute() method is called.
The cron expression used is: 0 0/15 * 1/1 * ? *
Now, the problem is as follows:
Lets say that the job starts at 3:00:00, it will execute as many times it can till 3:00:01, rather than executing only once.
Next the job will start at 3:15:00 and again will run as many times it can till 3:15:01.
The number of times the job executes is different every time.
I am not sure what is causing this behaviour. I have tested the cron expression with cronmaker.
Can somebody point out the error here ?