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
Related
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...
}
}
I need to perform some work when the spring application is ready, something similar to #Scheduled but I want it to perform only once.
I found some ways to do it, such as using #PostConstruct on a bean, using #EventListener or InitializingBean, however, all of these ways does not match my need. If during the execution of this logic something goes wrong, I want to ignore it so the application starts anyway. But using these methods the application crashes.
Of course, I can surround the logic with try-catch and it will work. But, is there any more elegant way?
We faced a similar issue with our microservices , in order to run code just after startup we added a Component.
ApplicationStartup implements ApplicationListener<ApplicationReadyEvent>
Within the application to make a call to the services just after application startup, this worked for us.
#Component
public class ApplicationStartup implements ApplicationListener<ApplicationReadyEvent> {
#Autowired
YourService yourService;
#Override
public void onApplicationEvent(final ApplicationReadyEvent event) {
System.out.println("ApplicationReadyEvent: application is up");
try {
// some code to call yourservice with property driven or constant inputs
} catch (Exception e) {
e.printStackTrace();
}
}
}
When you use #PostConstruct for implementing a logic, the application is not ready yet, so it kind of contradicts your requirement. spring initializes the beans one by one (with respect to the dependencies between them.
After all it builds up the application context.
When the application context is fully initialized, spring indeed allows listeners to be run. So The listeners is a way to go - when the listener is invoked the application is ready.
In both cases (PostConstruct, EventListener) as long as you're not using try/catch block the application context will fail, because it waits till all the listeners will be done.
You can use #Async if you don't want the application context to wait for listeners execution. In this case the exception handling will be done by the task executor. See here
Personally I don't see any issue with try/catch approach
You can use #PostConstruct (as you said) but you must wrap your business in try catch and ignore it when it throws an exception.
Sample Code
#PostConstruct
void init() {
try {
//Your business
}
catch (Exception e) {
//Do nothing Or you can just log
}
I am creating a service which watch for a file and as soon as file is available it loads it to db. Once this job is done I want to shutdown the app gracefully.
But when I use context.close it throws exception (though the app shuts down) but I want to shut it down without causing any exception.
#profile("non-test")
class Manager implements ApplicationContextAware{
#PostConstruct
public void init()
{
//watch for file and as soon as file is available invokde trigger()
}
public void trigger()
{
//load to db
shutodown()
}
public void shutdown()
{
SpringApplication.exit(context);
}
}
when i call shutdown it throws below exception.
AnnotationConfigApplicationContext has already been closed
I want the app to shutdown gracefully without any exception.
You problem is most probably presence of Spring dependency that makes your app long lived. Such dependency is for example spring-boot-starter-web. This starter includes Servlet container as dependency ans Spring will by default be running forever.
If non of such dependencies is on classpath, your Spring Boot app would shut down automatically (without any special effort to kill it). I would suggest to take a look at some of Spring guides for batch job.
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.
I'm needing to make a service that schedules jobs that are basically get requests that hit some servlet. I tried to do this w/ a servlet context listener based on this post, Running a background Java program in Tomcat, but the web.xml changes that were defined are causing 404 errors on the Tomcat server. Does anyone have any other suggestions on how to accomplish this?
One idea I have at this point is to define a runnable servlet
public class Service extends HttpServlet implements Runnable {
//Does stuff
init() {
new Thread(this);
}
}
Is this a reasonable approach?
you can look into using quartz scheduler for jobs :
http://quartz-scheduler.org/
for instance (not specific to your task):
import java.util.Map;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class SchedulerJob implements Job
{
public void execute(JobExecutionContext context)
throws JobExecutionException {
Map dataMap = context.getJobDetail().getJobDataMap();
SchedulerTask task = (SchedulerTask)dataMap.get("schedulerTask");
task.printSchedulerMessage();
}
}
Another option (for a quick turaround) would be to just use a cron job or windows task manager depending on your OS.