在以编程方式管理的Quartz作业中注入实体管理器

时间:2015-02-03 03:31:58

标签: spring jpa quartz-scheduler

我有点奇怪的要求。

我有一个Spring托管应用程序uisng JPA Hibernate。我使用Quartz编写了一些作业类,但它们没有使用Spring框架进行管理/集成。它们是一种独立的java类,具有基于运行时参数的复杂逻辑和动态触发计划。所以我从LoginController以编程方式安排这些作业。 现在,当我需要在这些作业类中执行一些数据库事务时,就会出现问题。

如果我尝试做的话 @PersistenceContext 私有EntityManager entityManager 我得到一个明确的空引用,因为我无法将这些组件自动装配到非弹簧管理的Quartz作业中。

我必须使用的最后一种方法是将JDBC用于作业类中的数据库事务,但这会增加工作量。我的问题是否有任何可能的解决方案。我已经附加了java代码以使事情变得清晰。

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

1 个答案:

答案 0 :(得分:0)

由于您在应用程序中写道您正在使用Spring,因此我强烈建议您使用Spring SchedulerFactoryBean来创建Quartz调度程序实例。然后,您可以轻松访问Spring应用程序上下文(以及已由Spring管理的EntityManager),如下所示:

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

Spring应用程序上下文的示例。请注意applicationContextSchedulerContextKey属性的值,该属性包含工厂存储Spring应用程序上下文的Quartz作业应用程序上下文属性名称:

  <!--
    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>

如果您不想迁移代码以使用Spring SchedulerFactoryBean,则可以使用以下方法获得相同的结果:

    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)
            {
                ...
            }
        }

        ...
    }

或者,不是将Spring应用程序上下文存储在JobScheduler类的Quartz调度程序上下文中,而是可以在那里存储EntityManager实例。