Search code examples
javajava-8quartz-schedulerguicedropwizard

Dropwizard quartz jobs won't work with Guicey


I'm using dropwizard 1.0.5, dropwizard-guicey 4.0.1 (https://github.com/xvik/dropwizard-guicey), spinscale quartz implementation 3.0.0 (https://github.com/spinscale/dropwizard-jobs).

I want to be able to inject my dependencies into the job instances that quartz/spinscale creates. To do this, I tried to load in the following library https://github.com/spinscale/dropwizard-jobs/tree/master/dropwizard-jobs-guice

The problem with this is, when the Guice dropwizard jobs bundle is created, the Guice injector is not yet initialized, so I get an NPE.

Example: MyApplication.java

  @Override
  public void initialize(Bootstrap<MyConfiguration> bootstrap) {

    // Initialize Guice for dependency injection

    GuiceBundle guiceBundle = GuiceBundle.builder()
            .bindConfigurationInterfaces()
            .enableAutoConfig(getClass().getPackage().getName())
            .modules(new MyModule(bootstrap.getMetricRegistry()))
            .build();
    bootstrap.addBundle(guiceBundle);

    bootstrap.addBundle(new GuiceJobsBundle(guiceBundle.getInjector()));

Exception:

Exception in thread "main" java.lang.NullPointerException: Guice not initialized
    at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:226)

To fix this, I tried creating my own version of the GuiceJobsBundle that takes in the GuiceBundle and doesn't set the injector until bundle.run() is called. But still, I get an NPE.

MyApplication

  @Override
  public void initialize(Bootstrap<MyConfiguration> bootstrap) {
    // Substitute environment variable references in the configuration file
    bootstrap.setConfigurationSourceProvider(new SubstitutingSourceProvider(
        bootstrap.getConfigurationSourceProvider(),
        new EnvironmentVariableSubstitutor(false, true)
    ));

    // Initialize Guice for dependency injection

    GuiceBundle guiceBundle = GuiceBundle.builder()
            .bindConfigurationInterfaces()
            .enableAutoConfig(getClass().getPackage().getName())
            .modules(new MyModule(bootstrap.getMetricRegistry()))
            .build();
    bootstrap.addBundle(guiceBundle);

    bootstrap.addBundle(new GuiceyJobsBundle(guiceBundle));
  }

GuiceyJobsBundle

public class GuiceyJobsBundle extends JobsBundle {

    private GuiceBundle guiceBundle;

    public GuiceyJobsBundle(GuiceBundle guiceBundle) {
        this.guiceBundle = guiceBundle;
    }

    @Override
    public void run(Environment environment) {
        JobManager jobManager = new GuiceJobManager(guiceBundle.getInjector());
        environment.lifecycle().manage(jobManager);
    }
}

When is a bundle's run() method called? And has anybody found a working solution for this?


Solution

  • You were almost right about the last fix, you just forget to bind configuration jobManager.configure(configuration) (that's why NPE was thrown).

    But you can do integration without bundles at all. I see you already use classpath scan, so just drop these extensions somewhere:

    @Singleton
    public class JobsManager extends GuiceJobManager {
    
        @Inject
        public JobsManager(Injector injector, JobsAppConfiguration configuration) {
            super(injector);
            configure(configuration);
        }
    }
    

    This will be recognized as Manged bean and will start/stop quartz context. Internally it searches for jobs in registered guice bindngs (using dropwizard-jobs logic).

    public class JobsInstaller implements FeatureInstaller<Job>, TypeInstaller<Job> {
    
        private final Reporter reporter = new Reporter(JobsInstaller.class, "jobs =");
    
        @Override
        public boolean matches(Class<?> type) {
            return FeatureUtils.is(type, Job.class);
        }
    
        @Override
        public void install(Environment environment, Class<Job> type) {
            // here we can also look for class annotations and show more info in console
            // (omitted for simplicity)
            reporter.line("(%s)", type.getName());
        }
    
        @Override
        public void report() {
            reporter.report();
        }
    }
    

    This installer simply binds all jobs to guice context so JobsManager could install them (just to avoid manual jobs binding).

    I add compete example to guicey-examples repo: https://github.com/xvik/dropwizard-guicey-examples/tree/master/dropwizard-jobs