Unable to inject service in Apache isis quartz - java

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);

Related

Can I change Spring Batch configuration to run statically?

[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());
}

How to pause a scheduled task started using springboot's #Scheduled annotation?

I have a simple scheduled task that was created using the #Scheduled annotation. Something like this -
public void doSomething() {
// My code
}
I also have a blue-green upgrade scenario where this scheduled task may potentially run in both the blue and green clusters. And given this scheduled task modifies the DB, there is a possibility that one of the nodes might overwrite the data from the other node - or worst case race conditions.
I want to pause all the scheduled tasks on the green cluster until it is ready to accept traffic. I already have the code wired to listen to changes to the application state.
I explored a few options -
Simply adding a boolean at the start of the task.
Adding a decorator to the ThreadPoolTaskScheduler.
The problem with these approaches is they skip the task for that particular schedule and not pause it. So, if the task is skipped, say at T, and the application becomes immediately, the application will have to wait T+30mins to refresh data.
Is there a way in springboot to pause schedulers and resume immediately?
It looks like natively spring does not have the ability to pause scheduled tasks. I tend to use Quartz Scheduler when running schedule tasks with spring.
In order to do that we have to do a couple configurations.
First we have to setup our context aware component:
#Component
public final class ApplicationContextHolder extends SpringBeanJobFactory implements ApplicationContextAware {
private static ApplicationContext context;
private transient AutowireCapableBeanFactory beanFactory;
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
if (context == null) {
beanFactory = ctx.getAutowireCapableBeanFactory();
context = ctx;
}
}
#Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
/**
* Get the current application context
* #return the current context
*/
public static ApplicationContext getContext() {
return context;
}
}
Then our configuration:
#Configuration
public class QuartzSchedulerConfig {
#Autowired
private ApplicationContext applicationContext;
/**
* Create the job factory bean
* #return Job factory bean
*/
#Bean
public JobFactory jobFactory() {
ApplicationContextHolder jobFactory = new ApplicationContextHolder();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
/**
* Create the Scheduler Factory bean
* #return scheduler factory object
*/
#Bean
public SchedulerFactoryBean schedulerFactory() {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setAutoStartup(true);
factory.setSchedulerName("Scheduler");
factory.setOverwriteExistingJobs(true);
factory.setJobFactory(jobFactory());
return factory;
}
}
Then our actual service:
#Service
public class SchedulerService {
#Autowired
private SchedulerFactoryBean schedulerFactory;
private Scheduler scheduler;
private ScheduledExecutorService executor;
/**
* Initialize the scheduler service
*/
#PostConstruct
private void init() {
scheduler = schedulerFactory.getScheduler();
executor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
}
...//Other Scheduling tasks can go here
public void pauseJob(String triggerName, String groupName) {
TriggerKey tk = new TriggerKey(triggerName, groupName);
scheduler.pauseJob(tk);
}
}
Quartz Scheduling gives a lot of flexibility when it comes to scheduling tasks
http://www.quartz-scheduler.org/overview/
Customize the task scheduler to wait until it finishes the job and allow spring boot to do the graceful shutdown
#Bean
TaskSchedulerCustomizer taskSchedulerCustomizer() {
return taskScheduler -> {
taskScheduler.setAwaitTerminationSeconds(60);
taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
};
}
another opinion
Use Shedlock to lock the scheduler for the given amount of time(upgradable time). It also solves the scheduler running on all/multiple instances instead of one instance.

There is null in #Autowired field when try to inject in spring-quartz job?

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

How to inject dependency in a Quartz job using Dagger 2

I am using Dagger 2 as my DI framework and I am providing a singleton class instance with it.
I also use Quartz Scheduler to schedule jobs. Is there any way to inject the singleton class into the Quartz job?
Dagger 2 module:
#Module
public class MyModule {
#Provides
#Singleton
Messager provideMessager() {
return new CustomMessager();
}
}
Dagger 2 component:
#Component(modules = MyModule.class)
#Singleton
public interface MyComponent {
Messager messager();
}
Quartz Job:
public class MyJob implements Job {
// #Inject
Messager messager;
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
messager.sendMessage("Hello.");
}
}
EDIT
I have created a MyJobScheduler class that calls the Quartz Job:
public class MyJobScheduler {
public void scheduleJob() {
JobDetail myJob = JobBuilder.newJob(MyJob.class)
.withIdentity("myJobId", "Group1")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTriggerId", "Group1")
.startNow()
.build();
Scheduler scheduler = new org.quartz.impl.StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(myJob, trigger);
}
}
EDIT 2
So I managed to configure it to work, but I don't know if this is the correct approach.
First I created a DependencyResolver class, which I use as a singleton:
public class DependencyResolver {
private static DependencyResolver _instance = null;
private static MyComponent _myComponent;
public static MyComponent getMyComponent() {
return _myComponent;
}
protected DependencyResolver() {
// Exists only to defeat instantiation.
}
public static void initialize() {
_myComponent = DaggerMyComponent.builder().build();
}
}
Then I called the initialize method in the main method:
DependencyResolver.initialize();
MyComponent myComponent = DependencyResolver.getMyComponent();
And I used the DependencyResolver in MyJob class to get the Messager singleton instance.
public class MyJob implements Job {
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
MyComponent myComponent = DependencyResolver.getMyComponent();
Messager messager = myComponent.messager();
messager.sendMessage("Hello.");
}
}
Is this the correct way to solve this issue? Any input will be greatly appreciated.
Your EDIT 2 DependencyResolver approach kind of defeats the whole reason to use Dagger to inject the dependencies, because your job gets the dependency from a singleton provider. :-) It completely bypasses the benefit of Dagger, so you might as well just have a singleton on the source dependency itself, like: Messager messager = CustomMessager.getInstance() or something like that.
One reason to use Dependency Injection, is to assist with unit testing, and in this case you're losing the ability to mock your Messager implementation in a unit test.
The proper way to use dependency injection with Quartz jobs is mentioned in the API doc for JobFactory: "This interface may be of use to those wishing to have their application produce Job instances via some special mechanism, such as to give the opertunity for dependency injection."
The trick is to create your own job factory that extends SimpleJobFactory and then you have an opportunity to initialize/inject the job's dependencies, something like this:
public class MyJobFactory extends SimpleJobFactory {
private final MyComponent component;
#Inject
public MyJobFactory(MyComponent component) {
this.component = component;
}
#Override
public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
final Job job = super.newJob(bundle, scheduler);
if (job instanceof MyJob) {
component.inject((MyJob) job);
}
return job;
}
}
And then you tell the scheduler to use your job factory:
scheduler.setJobFactory(myJobFactory);
See the full code here on GitHub

Lookup with InitialContext lookup() and #Inject of Singleton

I have an #Singleton bean that I use to store state that is to be shared with other beans/threads. The state that is to be shared is maintained in a HashMap. Other beans that require the services of this Singleton simply do an #Inject and invoke the methods.
Recently, we had to introduce Quartz Scheduler since we had some asynchronous jobs to be done. In one quartz job I lookup the Singleton using IntialContext lookup(), and use methods provided by the Singleton. Once this job executes, the other beans that use #Inject (for the Singleton) no longer get information maintained in the HashMap.
#Singleton(mappedName = "ClientSessionMgr")
public class ClientSessionMgr {
private final Map<String, ClientSession> clientSessions = new HashMap<String, ClientSession>();
/* Methods that manage the clientSessions data */
}
One amongst a few beans that Inject the above Singleton.
#Stateless(mappedName = "NwdCredProfRepo")
public class NwdCredProfRepo {
#Inject private ClientSessionMgr csm;
/* Bean Methods that invoke some methods of the singleton */
}
The Quartz Job that does a lookup of the singleton and then uses the methods provided by the same.
public class ClientSyncProbe implements Job {
private ClientSessionMgr csm;
#Override
public void execute(JobExecutionContext jec) throws JobExecutionException {
InitialContent ic = new InitialContext();
csm = (ClientSessionMgr) ic.lookup("java:app/SVWeb/ClientSessionMgr");
/* Some tasks, and finally */
ic.close()
}
}
I am guessing that I am missing a pattern principle. Any help?

Categories