Spring Scheduler: toggle between using cron and running once on startup - java

I managed to get a simple app running with spring application context and managed to get it working on a schedule via #Scheduled:
#Scheduled(cron= "0 30 9 * * *, zone="Australia/Sydney")
Is there a way to get #Scheduled to run on startup?
What I had in mind is to have a toggle in application.properties e.g:
scheduler.triggernow=true
Where if it is set to true, the app ignores the spring cron schedule and runs now (runs once on startup), and if set to false then it will use the spring cron schedule above and runs every day at 9:30am
I went to https://start.spring.io to get a basic application to work with (maven project, java, spring boot 2.3.4)
I saw a YouTube video to help set up a scheduler:
In the main class i have
#SpringBootApplication
#EnableConfigurationProperties
public class Demo{
public static void main(String[] args){
ConfigurationApplicationContext ctx = new SpringApplicationBuilder(Demo.class)
.web(WebApplicationType.None)
.run(args)
}
}
In a new class I added:
#Configuration
#EnableScheduling
#ConditionalOnProperty(name="scheduling.enabled", matchIfMissing=true)
public class Jobsched{
#Scheduled(cron="0 30 9 * * *",zone="Australia/Sydney")
public void test(){
System.out.println("hello");
}
}
On its own the cron scheduler works. And I have a property that can disable it. I would only want to disable it if I want to run it once on startup (currently disabling it will mean it does nothing)
This is a nonprod application by the way. Plan is to deploy this with scheduler running. And if I want to run it locally then just disable the scheduler and have it run on startup

Use the #PostConstruct in your class component (or service ...) to execute the code in startup,
below example showing how to proceed :
#Component
public class MyClass {
#PostConstruct
private void methodToExecute() {
doScheduledWOrk(); // call here for startup execution
}
#Scheduled(cron= "0 30 9 * * *, zone="Australia/Sydney")
public void scheduleMethod() {
doScheduledWOrk(); // sample function
}
public void doScheduledWOrk() {
//doing some scheduled stuff here...
}
}

Related

Use Quartz Scheduler in IBM Domino Application

I'm very new to Quartz, but know 3 simple things which you have to have in order to make it work.
These are jobs, triggers and scheduler.
Now, in our domino application we have to use it for refreshing a token.
I've created 3 basic classes for it.
The job:
public class RefreshEGRZTokenJob implements Job
{
public void execute(JobExecutionContext arg0) throws JobExecutionException
{
System.out.println("stub for refreshing a token");
}
}
The trigger and something like main:
public class RefreshEGRZTokenExecutor
{
private static String REFRESH_TOKEN_JOB = "refreshTokenJob";
public static void executeAndScheduleRefreshToken(int timeInSeconds) throws SchedulerException
{
JobDetail job = JobBuilder.newJob(RefreshEGRZTokenJob.class)
.withIdentity(REFRESH_TOKEN_JOB).build();
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity(REFRESH_TOKEN_JOB)
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(timeInSeconds).repeatForever())
.build();
QuartzScheduler.getInstance().scheduleJob(job, trigger);
}
public static void pauseScheduler() throws SchedulerException
{
QuartzScheduler.getInstance().standby();
}
}
And the scheduler:
public final class QuartzScheduler
{
private static Scheduler quartzSchedulerInstance;
public static Scheduler getInstance() throws SchedulerException
{
if (quartzSchedulerInstance == null)
{
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
quartzSchedulerInstance = scheduler;
}
return quartzSchedulerInstance;
}
}
The call I make is from a button (in production it'll execute shortly after the user authorized)
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:
ru.lanit.egrz.scheduler.RefreshEGRZTokenExecutor.executeAndScheduleRefreshToken(30);
}]]>
</xp:this.action>
</xp:eventHandler>
Well, quartz scheduler is initilized and the job is set but doesn't execute the job (I know this because if I press the same button twice, it'll give me an exeption that the job already exists).
I guess Domino's JVM doesn't let the scheduler run indefinitely.
The reason why I don't use standard IBM's agent is simple - it doesn't allow to use Java code in Code section. You have to either import and duplicate everything you have so far or to compile it into jar and import. But if you decide to change anything in your sources you'll have to recompile the entire jar (with new source code) and re-import that.
Has anybody integrated Domino JVM and Quartz?
If so, please tell me the best practices and how to make it work.
Thanks in advance.
I have created a plugin, you can find it here: https://github.com/hasselbach/domino-quartz
The feature project and the updatesite project are missing.
You have to install the plugin on the server and in the DDE and activate it in your XPages application.
You have a set of hurdles to overcome here:
Your scheduler needs to run "forever", so you need to run it from where it doesn't die. That place is an OSGi plugin. The place for your scheduler would be the activator class. something along those lines:
import java.util.logging.Logger;
import org.eclipse.core.runtime.Plugin;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator extends Plugin implements BundleActivator {
#Override
public void start(final BundleContext context) throws Exception {
// Here you start your scheduler
}
}
You need to call it from somewhere (and that shouldn't be SSJS). So you would create a managed bean that can access the plugins activator. Put the bean code into the plug-in. Once you have defined it you can call the bean method in your button
Your scheduler runs a token refresh. What is supposed to happen with the refreshed token?
Having said all this. You probably can get away with a much simpler solution (unless your token needs millisecond precision):
Create a managed bean for the session context (Each bean is individually instantiated per user).
In the bean have a method you will call from your button (or elsewhere) that kicks of a new thread. That thread sleeps for a while before you execute again. Check for a property of being shut down, so you can terminate gracefully.
Hope that helps

how to run two different spring boot instances?

I have a need to run two separate spring boot instances, the first instance is responsible for some code-generation, while the second is serves as an API.
Here is the process:
1. Run the generation spring boot app
2. After the generation is shutdown, run the API spring boot app
// Api.java
#Log4j2
#EnableEurekaClient
#SpringBootApplication
public class Api {
public static void main(String[] args) {
SpringApplication.run(Api.class, args);
}
}
// Generator.java
#Log4j2
#EnableEurekaClient
#SpringBootApplication
public class Generator implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Generator.class, args);
Api.run(args); // run the API instance
}
#Override
public void run(String... args) throws Exception {
// do generation here
}
}
Exact error:
Error creating bean with name 'nested exception is javax.management.InstanceAlreadyExistsException: org.springframework.boot:type=Admin,name=SpringApplication
The error I'm currently getting is that there is already a spring boot instance, and hence can not call Api.run(args). I'd like to be able to run both of these one after another. If there is a better way I'm all ears. At the end of the day it needs to be a single runnable jar. My main class is configured to point to the Generation, and theoretically it should generate the code then run the Api.
I use something like this to run 2 services - User Service and Project Service:
SpringApplicationBuilder uws = new SpringApplicationBuilder(UserWebApplication.class)
.properties("server.port=8081",
"server.contextPath=/UserService");
uws.run();
SpringApplicationBuilder pws = new SpringApplicationBuilder(ProjectWebApplication.class)
.properties("server.port=8082",
"server.contextPath=/ProjectService");
pws.run();
They use different ports and context paths.

How to do some preprocessing before starting Spring Boot Scheduler?

I want to have some basic preprocessing code that need to be run only once before starting the scheduler everytime. How can we achieve the same in Spring Boot ?
Are you looking for this? There are other options as well. But please elaborate the question.
#Component
public class Cache {
...
#PostConstruct
public void initializeCache() {
...
}
#Scheduled(fixedRate = 60L * 1000L)
public void refreshCache() {
...
}
}
Credits: Will a method annotated with #PostConstruct be guaranteed to execute prior to a method with #Scheduled within the same bean?
If you want to run code only once you could wait until Spring is ready and then run the code. To achieve this you could listen for the event like this:
#EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() {
System.out.println("run your code here");
}
You can put that code in the application class to see the results.

EJB #Timeout method is getting called multiple times during hot re deployment in weblogic server

We have the following simple app: one #Singleton bean with #Timeout method and one servlet which starts timer. After first deploy we see that method is called once in 2 seconds - which is expected. Then after hot re-deploy we see that method is called twice within 2 seconds. After a few redeploys method is called multiple times during the same 2 seconds. Restarting the server doesn't help. See the code below:
import javax.ejb.*;
#Remote(TimerRemote.class)
#Singleton
public class TimerBean implements TimerRemote {
#Resource
private SessionContext context;
public void startTimer() {
context.getTimerService().createTimer(2000,2000,null);
}
#Timeout
public void timeoutCallback(javax.ejb.Timer timer) {
System.out.println("timeoutCallback is called: " + timer);
}
}
#Timeout method should be called after given interval time. Currently method is getting called multiple times within a second.
The timer is persistent per default and is not cancelled at all.
Please refer to the official Java EE6 Tutorial: Using the Timer Service.
Prefer using #Schedule and set persistent=false if you don't need the timer to be persistent. Or try a programmatic approach controlling the lifecycle of the timer on your own.

How to run spring boot application both as a web application as well as a command line application?

Currently, I am trying to use the CommandLineRunner along with the ConfigurableApplicationContext to run a spring boot application both as a web application by default and as a standalone command line application on demand (via command line parameters of some sort). I am struggling figuring out how to solely run this as a console application when program arguments are supplied. Please any suggestions would help.
Main class - SpringApplication
CommandLineRunner
I had the same requirement. This is how I was able to achieve it:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplicationBuilder app = new SpringApplicationBuilder(Application.class);
if (args.length == 0) { // This can be any condition you want
app.web(WebApplicationType.SERVLET);
} else {
app.web(WebApplicationType.NONE);
}
app.run(args);
}
}
And this is a console application runner.
#Component
#ConditionalOnNotWebApplication
public class ConsoleApplication implements CommandLineRunner {
#Override
public void run(String... args) {
System.out.println("************************** CONSOLE APP *********************************");
}
}
When you build your bootJar, you can run application as Web app using
java -jar app.jar and as a command line app using java -jar app.jar anything #based on the condition you specified.
Hope this helps.
EDIT:
A better way to achieve this is changing Application.java as following and keeping ConsoleApplication.java as shown above.
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
and then running your bootJar with java -jar -Dspring.main.web-application-type=NONE app.jar will run the application as console application. And not passing any spring.main.web-application-type will run as a web application.
The CommandLineRunner interface provides a useful way of picking up command line arguments once the application has started, but it won’t help change the nature of the application. As you’ve probably discovered, the application will probably not exit since it thinks it needs to handle incoming web requests.
The approach you’ve taken in your main method looks sensible to me. You need to tell Spring Boot that it isn’t a web application, and therefor shouldn’t hang around listening for incoming requests once it’s been started.
I’d do something like this:
public static void main(String[] args) {
SpringApplication application = new SpringApplication(AutoDbServiceApplication.class);
application.setWeb(ObjectUtils.isEmpty(args);
application.run(args);
}
That should start the application in the correct mode. Then you can use a CommandLineRunner bean in the same way as you currently do. You might also want to look at ApplicationRunner which has a slightly better API:
#Component
public class AutoDbApplicationRunner implements ApplicationRunner {
public void run(ApplicationArguments args) {
if (ObjectUtils.isEmpty(args.getSourceArgs)) {
return; // Regular web application
}
// Do something with the args.
if (args.containsOption(“foo”)) {
// …
}
}
}
If you really don’t want the AutoDbApplicationRunner bean to even be created you could look at setting a profile in the main method that you could use later (see SpringApplication.setAdditionalProfiles).

Categories

Resources