Schedule task dynamically, in Springboot application - java

In a spring-boot application, I need to create a scheduled task that executes an exposed method (in the application domain).
Through the graphical interface, the user can configure the execution time and periodicity.
I already have this part.
The problem is to know if using quartz or similar library is possible to program the task and to be reprogrammed if the user modifies the configuration.
Please can you give me documentation about it so I can configure it this way.

you can use #Scheduled annotation of spring. You can annotate any method on the spring component by specifying the delaystring ,i.e. the interval it should be running after. This can be configured in the properties file. To specify interval you can use "fixedRate" or "fixedDelay".
fixedRate- Executes the new run even if previous run of the job is still in progress.
fixedDelay-controls the next execution time when the last execution finishes.
This had helped me in the past.
https://spring.io/guides/gs/scheduling-tasks
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/annotation/Scheduled.html
1.You can create the JOB class containing the task to be done:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
#Component
public class MyScheduledTask {
private static final SimpleDateFormat currentTime =
new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
#Scheduled(fixedRate = 2000) //interval in millisconds
public void doSomeTask() {
System.out.println("doSome task exceuted at "
+ currentTime.format(new Date()));
}
}
2.Main Spring boot class
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
#SpringBootApplication
#EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(new Object[] { Application.class }, args);
}
}
Hope this helps you a bit.

Related

Can i get the time in milliseconds when the scheduler started in spring boot application

I have a method which will be running at an interval of 2 hrs and is mentioned in cron of Scheduler in Spring boot application.
Now my question is can I get the time in milliseconds when the scheduler ran?
I tried to get the time but not able to fetch it when the scheduler ran.
The Spring guide of scheduler give a similar example , see the link : https://spring.io/guides/gs/scheduling-tasks/ .
You can log the time to a logFile but as Thomas mention the exact time of execution may be after some millisonconds (related with the machine and the thread scheduler and the time needed by the thread to do the task of log or print to the outputStream).
import java.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
#Component
public class ScheduledTasks {
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
#Scheduled(fixedRate = 1000)
public void reportCurrentTime() {
log.info("The time is now {}", LocalDateTime.now());
// System.out.println(LocalDateTime.now());
}
}

How to create a background job with sling commons scheduler in AEM as a Cloud?

I tried 2 ways to do a periodical background job.
1:sling job. This was executed on both local SDK and AEM cloud, but executed many times when the specified time come.
2:commons scheduler. This is good on a local SDK, but this did nothing on AEM cloud environment.
I want to know how can it be executed with second way(Sling Commons Scheduler).
Here is my sample code
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Component(service = Runnable.class, property = { Scheduler.PROPERTY_SCHEDULER_EXPRESSION + "=0 30 * * * ? " })
public class Myjob implements Runnable {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Override
public void run() {
logger.info("this message is from myjob");
}
}
This code above is good on local SDK. On AEM cloud, doesn't work. Please help me.

Setting "spring.config.name" property programmatically does not work

According to this blog post from December 2017, it is possible to change the name used to search for Spring Boot configuration files programmatically like this:
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class)
.properties("spring.config.name:conf")
.build()
.run(args);
}
}
I have tried this using Spring Boot version 1.5.9-RELEASE, but this does not work. Setting spring.config.name as an argument does work:
mvn spring-boot:run -Dspring.config.name=conf
However, I do not have control over the arguments passed to my Spring Boot application when it is started so this is not an option.
Is it no longer possible to set spring.config.name programmatically, am I missing something, or is this a bug?
Doesn't directly answer the question, but I eventually discovered a workaround. Setting the spring.config.name by adding it to the arguments does work:
package com.ups.cep;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
List<String> arguments = new ArrayList<>(Arrays.asList(args));
arguments.add("-Dspring.config.name=conf");
SpringApplication.run(Application.class, arguments.toArray(new String[arguments.size()]));
}
}
Your example works for me (Spring Boot 2.4.3) so it could be bug at that time.
A similar SO answer also uses SpringApplicationBuilder.properties(String... defaultProperties) to define configuration name.
new SpringApplicationBuilder(Application.class)
.properties("spring.config.name:conf")
.build()
.run(args);
I checked both spring.config.name=conf and spring.config.name:conf and they work.

Spring Scheduled task does not start on application startup

I have a #Scheduled task in my application which is setup using CRON and run every 4 hours. The problem I face is that the CRON job does not start immediately after application startup but it starts only 4 hours after the application startup.
I tried to use a #PostConstruct method inside the task to invoke it, but that results in an error due to an uninitialized Spring context.
Please tell me how I can make the Scheduled task run immediately on application deployment and then on every 4 hours after the deployment.
EDIT:
I would not use a #PostConstruct since my scheduled method depends on other Beans , which are not initialized when this PostConstruct method runs for some reason.
By 0 */4 * * * you specify "At minute 0 past every 4th hour (0:00, 4:00, 8:00 etc.)", which is not at startup time and then every 4 hours as I think you want.
You can specify initial delay and rate by:
#Scheduled(initialDelay=0, fixedRate=4*60*60*1000)
If you are worried about hard-coded values, you can still provide config value:
#Scheduled(initialDelay=0, fixedRateString = "${some.config.string}")
I had #EnableScheduling in my app config. I had
#Scheduled(fixedDelay=5000) in my scheduled task class. Even then it didn't work.
I added #Service to my scheduled task class and everything worked fine.
I was having the same situation. I need to run cron scheduler every 1st day of the month and also when the application starts.
This is how I achieved using ApplicationListener.
import org.springframework.context.ApplicationListener;
import org.springframework.boot.context.event.ApplicationReadyEvent;
#Component
public class ApplicationReadyListner implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// callYourTask();
}
}
I'm not sure if you have tried this but you can use #Scheduled with an initialDelay
For fixed-delay and fixed-rate tasks, an initial delay may be specified indicating the number of milliseconds to wait before the first execution of the method.
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html#scheduling-annotation-support-scheduled
#Scheduled(initialDelay=0, fixedRate=5000)
public void doSomething() {
// something that should execute periodically
}
I assume this will execute your scheduled method when the application is run.
If is not possible to use initialDelay attribute with the cron rule and your intention is execute this Job after every restart, you can implement a CommandLineRunner on your application class:
#SpringBootApplication
public class MyApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Autowired
private TaskService taskService;
#Override
public void run(final String... args) throws Exception {
taskService.run();
}
}
It's not the best strategy, but works.
Just specify the method you want to run in the init_method attribute of the bean.
Java config: #Bean(init_method="methodWhichStartsTask")
XML config: <bean id="someId" class="com.example.Clazz" init-method="methodWhichStartsTask"/>
This will invoke the method just after the bean is properly initialized and if the method is scheduled, then it will be called afterwards every 4 hours.
Instead of cron inside #Scheduled, use fixedRate like below. By default initialDelay is -1, which will start immediately or you can set 0.
#Scheduled(fixedRate=4*60*60*1000)
public void test() {
System.out.println("helloworld");
}
user #EnableScheduling annotation on your class which contains you1 scheduled method:
see spring document:
To enable support for #Scheduled and #Async annotations add
#EnableScheduling and #EnableAsync to one of your #Configuration
classes:
#Configuration
#EnableAsync
#EnableScheduling
public class AppConfig {
}
link : https://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html
#EnableScheduling annotation should be added in the main class for the Spring application.
Example :
#SpringBootApplication
#EnableScheduling
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
and above the method where the actual scheduling needed we need to add
#Scheduled(initialDelay = 1000, fixedDelay = 4*60*60*1000)
Example:
#Scheduled(initialDelay = 1000, fixedDelay = 4*60*60*1000)
public void schleduleWork() {
/* Code Here */
}
For the above scheduler the first hit will be at 1000 milli second and subsequest hits will be at the interval of 4 hrs

Camel read properties file

How do I configure the use of a properties file using Java DSL and the Main object?
According to this page I should be able to call something like:
main.setPropertyPlaceholderLocations("example.properties");
However that simply doesn't work. It seems that option wasn't added until Camel 2.18 and I'm running 2.17.1.
What was the original way to set a properties file to use when letting the application run in a standalone form?
Some backstory:
I'm trying to convert from Spring to Java DSL. During that conversion I was attempting to have my Camel application run on its own. I know that is achieved using main.run();.
I had things "functioning" when using the CamelContext, but that cannot run on its own. So I know using the following will work in that case:
PropertiesComponent pc = new PropertiesComponent();
pc.setLocation("classpath:/myProperties.properties");
context.addComponent("properties", pc);
Is there some way I can tell the main to use that setup? Or is there something else needed?
You can use the following snippet:
PropertiesComponent pc = new PropertiesComponent();
pc.setLocation("classpath:/myProperties.properties");
main.getCamelContexts().get(0).addComponent("properties", pc);
Also, if you are using camel-spring, you could use org.apache.camel.spring.Main class, it should use the property placeholder from your application context.
Since you are mentioning you are in the process to move from Spring XML to Java Config here's a minimum application that is using properties and injecting it into a Camel route (it's really properties management in Spring injected into our Camel route bean):
my.properties:
something=hey!
Main class:
package camelspringjavaconfig;
import org.apache.camel.spring.javaconfig.CamelConfiguration;
import org.apache.camel.spring.javaconfig.Main;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#ComponentScan("camelspringjavaconfig")
#PropertySource("classpath:my.properties")
public class MyApplication extends CamelConfiguration {
public static void main(String... args) throws Exception {
Main main = new Main();
main.setConfigClass(MyApplication.class); // <-- passing to the Camel Main the class serving as our #Configuration context
main.run(); // <-- never teminates
}
}
MyRoute class:
package camelspringjavaconfig;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public class MyRoute extends RouteBuilder {
#Autowired
Environment env; //<-- we are wiring the Spring Env
#Override
public void configure() throws Exception {
System.out.println(env.getProperty("something")); //<-- so that we can extract our property
from("file://target/inbox")
.to("file://target/outbox");
}
}

Categories