简体   繁体   中英

Injecting Entity Manager in a programatically managed Quartz job

I have a somewhat strange kind of reqirement.

I have a Spring managed application uisng JPA Hibernate. I have written few job classes using Quartz but they are not managed/integrated with Spring framework. They are kind of independent java classes with complex logic and dynamic triggering schedules based on run time parameters. So I schedule these jobs programatically from the LoginController. Now the problem comes when I need to do some database transactions in these job classes.

If I try to do @PersistenceContext private EntityManager entityManager I get a null reference which is clear because I am not able to autowire these components into non spring managed Quartz jobs.

The last resort that I would have to use is to use JDBC for my database transactions in the job classes but that increases the work manifold. Is there any possible solution to my problem. I have attached the java code to make things clear.

JobScheduler.java

public class JobScheduler extends Object
{
    private static final Logger logger = LoggerFactory
            .getLogger(JobScheduler.class);

    private static final JobScheduler s_instance = new JobScheduler();
    private static boolean s_isSchedulerStarted = false;
    private static Scheduler s_scheduler = null;

    static
    {
        try
        {
            s_scheduler = new StdSchedulerFactory().getScheduler();
        } catch (SchedulerException e)
        {
            logger.debug(e.getMessage().toString());
        }
    }

    public static JobScheduler getInstance()
    {
        if (!s_isSchedulerStarted)
        {
            try
            {
                s_scheduler.start();
                s_isSchedulerStarted = true;
            } catch (SchedulerException e)
            {
                logger.debug(e.getMessage());
                e.printStackTrace();
            }
        }
        return s_instance;
    }

    public Scheduler getScheduler()
    {
        return s_scheduler;
    }

    public void scheduleMonitoring() throws ApplicationException
    {
        try
        {
            Class<? extends Job> jobClass = ScheduleMonitoringJob.class;
            JobDetail job = JobBuilder.newJob(jobClass).build();
            Trigger trigger = ScheduleMonitoringJob.getTriggerWithSchedule();
            s_scheduler.scheduleJob(job, trigger);

        } catch (SchedulerException e)
        {
            logger.debug(e.getMessage());
            throw new ApplicationException(e);
        }
    }

}

ScheduleMonitoringJob.java

public class ScheduleMonitoringJob implements InterruptableJob
{
    private static final Logger logger = LoggerFactory
            .getLogger(ScheduleMonitoringJob.class);

    @PersistenceContext
    private EntityManager entityManager; //THIS COMES AS NULL 

    /*
     * (non-Javadoc)
     * 
     * @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
     */
    @Override
    public void execute(JobExecutionContext context)
            throws JobExecutionException
    {
        List<KpiDefinition> kpisToBeMonitored = getNewOrChangedKPIs();

        for (KpiDefinition kpiDef : kpisToBeMonitored)
        {
            KpiType kpiType = kpiDef.getKpiTypeBean();
            Class<? extends MonitorJob> jobClass = null;

            if (kpiType.getName()
                    .equalsIgnoreCase(KpiType.TYPE_DB_CONNECTIVITY))
            {
                jobClass = DBConnectionMonitorJob.class;
            } else if (kpiType.getName().equalsIgnoreCase(
                    KpiType.TYPE_FTP_SERVER_AVAILABILITY))
            {
                jobClass = FTPServerMonitorJob.class;
            } else if (kpiType.getName().equalsIgnoreCase(
                    KpiType.TYPE_SOAP_SERVICE_AVAILABILITY))
            {
                jobClass = SOAPServiceMonitorJob.class;
            } else
            {
                jobClass = EngineEventSQLMonitorJob.class;
            }

            JobDetail job = JobBuilder.newJob(jobClass).build();

            job.getJobDataMap().put("kpiDefId", kpiDef.getKpiDefId());

            Trigger trigger = MonitorJob.getTriggerWithSchedule(kpiDef);

            try
            {
                JobScheduler.getInstance().getScheduler()
                        .scheduleJob(job, trigger);
            } catch (SchedulerException e)
            {
                logger.debug(e.getMessage());
                throw new JobExecutionException(e);
            }

            kpiDef.setKpiStatus(KpiDefinition.KPI_STATUS_PROCESSING_PROCESSED);

        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.quartz.InterruptableJob#interrupt()
     */
    @Override
    public void interrupt() throws UnableToInterruptJobException
    {
        // TODO Auto-generated method stub

    }

    public static Trigger getTriggerWithSchedule()
    {
        SimpleTrigger trigger = (SimpleTrigger) newTrigger().withSchedule(
                SimpleScheduleBuilder.repeatMinutelyForever(10)).build();

        return trigger;

    }

    public List<KpiDefinition> getNewOrChangedKPIs()
    {
        String[] statusCodes = { KpiDefinition.KPI_STATUS_NEW,
                KpiDefinition.KPI_STATUS_CHANGED };
        Query query = entityManager
                .createQuery("select kpiDef from KpiDefinition kpiDef where kpiDef.kpiStatus in (:statusCodes)");
        query.setParameter("statusCodes", statusCodes);
        return query.getResultList();
    }
}

Since you write that you are using Spring in your application, I strongly recommend that you use the Spring SchedulerFactoryBean to create the Quartz scheduler instance. Then you can easily access the Spring application context (and the EntityManager that is already managed by Spring) like so:

public class ScheduleMonitoringJob implements InterruptableJob
{
    private static final String SCHEDULER_CONTEXT_APPLICATION_CONTEXT_KEY = "applicationContext";

    ...

    /**
     * Returns the {@link ApplicationContext} instance extracted from the scheduler context, or null if not found.
     *
     * @return the {@link ApplicationContext} instance.
     */
    protected EntityManager getEntityManager(JobExecutionContext context)
    {
      try
      {
        ApplicationContext springCtx = (ApplicationContext) context.getScheduler().getContext().get( SCHEDULER_CONTEXT_APPLICATION_CONTEXT_KEY );

        return springCtx.getBean(EntityManager.class);
      }
      catch ( SchedulerException e )
      {
        if ( log.isErrorEnabled() )
          log.error( "Error obtaining Spring application context.", e );

        return null;
      }
    }
}

Example of the Spring application context. Please note the value of the applicationContextSchedulerContextKey property that contains the name of the Quartz job application context property name into which the factory stores the Spring application context):

  <!--
    Quartz scheduler.
  -->
  <bean id="scheduler"
        class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

    <property name="schedulerName" value="MyScheduler"/>

    ...

    <!--
      Name of the Quartz scheduler context property where the factory stores the Spring context.
    -->
    <property name="applicationContextSchedulerContextKey" value="applicationContext"/>
  </bean>

If you do not want to migrate your code to use the Spring SchedulerFactoryBean, you can achieve the same result with:

    public class JobScheduler extends Object
    {       
        private static final JobScheduler s_instance = new JobScheduler();
        private static boolean s_isSchedulerStarted = false;
        private static Scheduler s_scheduler = null;

        static
        {
            try
            {
                s_scheduler = new StdSchedulerFactory().getScheduler();
                s_scheduler.getContext().put("applicationContext", YOUR_SPRING_APPLICATION_CONTEXT);
            } 
            catch (SchedulerException e)
            {
                ...
            }
        }

        ...
    }

Alternatively, instead of storing the Spring application context in the Quartz scheduler context in your JobScheduler class, you can store the EntityManager instance there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM