我有点奇怪的要求。
我有一个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();
}
}
答案 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实例。