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
Related
I'm using Kinesis Client Library (KCL) and Spring boot. To use KCL, I have to implement a class (I named it RecordProcessor) for interface IRecordProcessor. And KCL will call this class and process records from kinesis. But when I tried to use dependency injection, I found it was not succeeded.
Here's the snippet for RecordProcessor:
#Component
public class RecordProcessor implements IRecordProcessor {
#Autowired
private SingleRecordProcessor singleRecordProcessor;
#Override
public void initialize(String shardId) {
...
}
#Override
public void processRecords(List<Record> records, IRecordProcessorCheckpointer checkpointer) {
...
}
}
I use Class SingleRecordProcessor to process single each record from kinesis. And this is my SingleRecordProcessor class snippet:
#Component
public class SingleRecordProcessor {
private Parser parser;
private Map<String, Table> tables;
public SingleRecordProcessor() {
}
#Autowired
private void setParser(Parser parser) {
this.parser = parser;
}
#Autowired
private void setTables(Map<String, Table> tables) {
this.tables = tables;
}
public void process(String record) {
...
}
}
I want to let spring framework automatically inject the SingleRecordProcessor instance into the class and use it. But I found that the field singleRecordProcessor is null.
Any idea why the dependency injection is failed? Or is it impossible to inject dependencies into a class which is called by other framework (in this case it's KCL)? Any suggestions will be appreciated! Really need some help please!!
[UPDATE]:
Sorry for not expressing the error clearly. The error was NullPointerException. I tried to inject singleRecordProcessor and call method process() on it. I think the injection was not successful so the instance singleRecordProcessor is null and there comes the NullPointerException.
More information is as follows:
I have a major class called Application
#SpringBootApplication
public class Application{
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.addListeners(new ApplicationPidFileWriter("./app.pid"));
ConfigurableApplicationContext ctx = application.run(args);
}
}
And I have the MainProcessor class which will call KCL.
#Service
public final class MainProcessor {
#EventListener(ApplicationReadyEvent.class)
public static void startConsumer() throws Exception {
init();
IRecordProcessorFactory recordProcessorFactory = new RecordProcessorFactory();
Worker worker = new Worker(recordProcessorFactory, kinesisClientLibConfiguration);
...
worker.run(); // this line will call KCL library and eventually call ProcessorRecord class.
}
}
[UPDATE2]
RecordProcessorFactory only has one method like this
#Component
public class RecordProcessorFactory implements IRecordProcessorFactory {
#Autowired
RecordProcessor recordProcessor;
#Override
public IRecordProcessor createProcessor() {
return recordProcessor;
}
}
It creates a new RecordProcessor instance for KCL to use it.
You should autowire an instance of this into your MainProcessor:
#Component
public class RecordProcessorFactory {
#Lookup IRecordProcessor createProcessor() { return null; }
}
Spring will instantiate a RecordProcessorFactory for you, and replace the implementation of createProcessor() in it with one that will return a new IRecordProcessor each time it's called. Both the factory and the processors will be Spring beans - which is what you want.
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);
I have assigned a dedicated thread pool for database operations via play-akka configuration file. For now I am injecting the services that require this thread pool with the actor system and accessing the execution context.
public class ServiceA{
final Executor executionContext;
#Inject
public ServiceA(ActorSystem system) {
this.executionContext = system.dispatchers().lookup("akka.actor.db-context");
}
But this makes it hard to test ServiceA. What I would like to do instead is just inject the Executor directly like this:
public class ServiceA{
final Executor executionContext;
#Inject
public ServiceA(Executor dbExecutionCtx) {
this.executionContext = dbExecutionCtx;
}
How do I achieve this? I have tried creating a guice module to inject the Executor but it errors out complaining that there is no started application and doesn't have access to ActorSystem when its doing it's binding the classes.
I use a pattern where I can get EC anywhere I want. I create an ActorSystem in a Singleton and get it injected into my services.
I have a design with ActorSystems, Dispatchers and a lot more for monitoring. Have a look at this and see if you can integrate it.
So if MyActorSystem is injected to your class you can access the EC from it. Have a look at MyDispatcher and the use of EC:
#Singleton
public class MyActorSystem implements IMyActorSystem{
ActorSystem system;
public MyActorSystem() {
system = ActorSystem.create();
}
public ActorRef create() {
final ActorRef actor = system.actorOf(
Props.create(MyWorker.class).withDispatcher("my-disp")
);
return actor;
}
public void shutdown(){
system.shutdown();
}
public ExecutionContextExecutor getDispatcher(){
return system.dispatcher();
}
}
public class MyDispatcher implements IMyDispatcher {
MyActorSystem system;
#Inject public MyDispatcher(MyActorSystem system) {
this.system = system;
}
public CompletableFuture<Object> dispatch(final Object story) {
List<CompletableFuture<Object>> futureList = new ArrayList<>();
final ActorRef actor = system.create();
final CompletableFuture<Object> completed = FutureConverter
.fromScalaFuture(Patterns.ask(actor, story, 50000)).executeOn(system.getDispatcher())
.thenApply(i -> (Object) i);
return completed;
}
public ExecutionContextExecutor getDispatcher(){
return system.getDispatcher();
}
}
Play as of 2.6 exposes DI bindings for its default dispatcher like this:
bind[ExecutionContextExecutor].toProvider[ExecutionContextProvider],
bind[ExecutionContext].to[ExecutionContextExecutor],
bind[Executor].to[ExecutionContextExecutor],
where ExecutionContextProvider is
#Singleton
class ExecutionContextProvider #Inject() (actorSystem: ActorSystem) extends Provider[ExecutionContextExecutor] {
def get = actorSystem.dispatcher
}
It looks like Play does not expose other named dispatchers in DI bindings, so you can do it yourself the same way but using #Named bindings. It is also possible to write a Play DI module which would dynamically expose all named akka dispatchers as corresponding named bindings.
The application I have been working on has been getting more and more complicated, and it's gotten to the point where I have been running into the same problems over and over again with concurrency. It no longer made any sense to solve the same problems and not have any regression tests.
That's when I found ThreadWeaver. It was really nice for some simple concurrency cases I cooked up, but I started to get frustrated when trying to do some more complicated cases with my production code. Specifically, when injecting components using Guice.
I've had a bit of a hard time understanding the implications of the way ThreadWeaver runs tests, and looked for any mention of Guice or DI in the wiki documents, but with no luck.
Is Guice compatible with ThreadWeaver?
Here is my test
#Test
public void concurrency_test() {
AnnotatedTestRunner runner = new AnnotatedTestRunner();
runner.runTests(OPYLWeaverImpl.class, OPYLSurrogateTranscodingService.class);
}
Here is my test implementation
public class OPYLWeaverImpl extends WeaverFixtureBase {
#Inject private TaskExecutor taskExecutor;
#Inject private Serializer serializer;
#Inject private CountingObjectFileMarshaller liveFileMarshaller;
#Inject private GraphModel graphModel;
#Inject private CountingModelUpdaterService updaterService;
#Inject private BabelCompiler babelCompiler;
#Inject private EventBus eventBus;
OPYLSurrogateTranscodingService service;
private Path testPath;
#ThreadedBefore
public void before() {
service = new OPYLSurrogateTranscodingService(eventBus, taskExecutor, serializer, liveFileMarshaller,
() -> new OPYLSurrogateTranscodingService.Importer(graphModel, babelCompiler, updaterService, eventBus),
() -> new OPYLSurrogateTranscodingService.Validator(eventBus, babelCompiler),
() -> new OPYLSurrogateTranscodingService.Exporter(graphModel, updaterService));
}
#ThreadedMain
public void mainThread() {
testPath = FilePathOf.OASIS.resolve("Samples/fake-powershell-unit-test.opyl");
service.applyToExistingGraphModel(testPath);
}
#ThreadedSecondary
public void secondaryThread() {
}
#ThreadedAfter
public void after() {
}
And the WeaverFixtureBase
public class WeaverFixtureBase {
#Inject protected CountingEventBus eventBus;
#Before public final void setupComponents() {
Injector injector = Guice.createInjector(new WeaverTestingEnvironmentModule(CommonSerializationBootstrapper.class));
injector.getMembersInjector((Class) this.getClass()).injectMembers(this);
}
private class WeaverTestingEnvironmentModule extends AbstractModule {
private final Class<? extends SerializationBootstrapper> serializationBootstrapper;
public WeaverTestingEnvironmentModule(Class<? extends SerializationBootstrapper> serializationConfiguration) {
serializationBootstrapper = serializationConfiguration;
}
#Override protected void configure() {
bind(TaskExecutor.class).to(FakeSerialTaskExecutor.class);
bind(SerializationBootstrapper.class).to(serializationBootstrapper);
bind(ModelUpdaterService.class).toInstance(new CountingModelUpdaterService());
bindFactory(StaticSerializationConfiguration.Factory.class);
CountingEventBus localEventBus = new CountingEventBus();
bind(Key.get(EventBus.class, Bindings.GlobalEventBus.class)).toInstance(localEventBus);
bind(Key.get(EventBus.class, Bindings.LocalEventBus.class)).toInstance(localEventBus);
bind(CountingEventBus.class).toInstance(localEventBus);
bind(EventBus.class).toInstance(localEventBus);
}
#Provides
#Singleton
public GraphModel getGraphModel(EventBus eventBus, Serializer serializer) {
return MockitoUtilities.createMockAsInterceptorTo(new GraphModel(eventBus, serializer));
}
}
But when the classloader loads OPYLWeaverImpl, none of the Guice stuff goes off and I get a big pile of nulls.
I feel like this is one of those "missing-something-really-simple" kind of scenarios. Sorry if it is!
The above comment is right. Thread-weaver is fully agnostic of JUnit. Thread weaver is its own runner that executes a test case respecting its own annotations. You must not use any JUnit-specific annotation within a Thread Weaver test.
Other than that, Thread Weaver does not need any compatibility for a specific framework. It manipulates Java byte code and loads that manipulated code using aeperate class loaders.
Finally, a Thread Weaver test without any secondary test does not make any sense. Thread weaver works by interleaving seperate execution paths. Without a second thread, Thread Weaver only steps through a single thread without adding any value.
I have a code which runs at a regular interval.
Below is code for that
#EnableScheduling
#Component
public class Cer {
#Autowired
private A a;
#Autowired
private CacheManager cacheManager;
#Scheduled(fixedDelayString = "${xvc}")
public void getData() {
getCat();
getB();
return;
}
}
I want to change#Scheduled(fixedDelayString = "${xvc}") this based on a boolean say runOnce if runOnce is true #scheduled should run once only say on code startup.
Any advice how to achieve this.
Thanks in advance.
I would place the functionality that you want to schedule in a component:
#Component
public class ServiceToSchedule {
public void methodThatWillBeScheduled() {
// whatever
return;
}
}
And have two additional components that will be instantiated depending on a Profile. One of them schedules the task, and the other one just executes it once.
#Profile("!scheduled")
#Component
public class OnceExecutor {
#Autowired
private ServiceToSchedule service;
#PostConstruct
public void executeOnce() {
// will just be execute once
service.methodThatWillBeScheduled();
}
}
#Profile("scheduled")
#Component
#EnableScheduling
public class ScheduledExecutor {
#Autowired
private ServiceToSchedule service;
#Scheduled(fixedRate = whateverYouDecide)
public void schedule() {
// will be scheduled
service.methodThatWillBeScheduled();
}
}
Depending on which profile is active, your method will be executed just once (scheduled profile is not active), or will be scheduled (scheduled profile is active).
You set the spring profiles by (for example) starting your service with:
-Dspring.profiles.active=scheduled