01-19-2009 05:16 AM
<bean id="fromTraiterToArchiverScheduler" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>org.alfresco.module.msc.action.executer.MscArchiverExecuter</value>
</property>
</bean>
<bean id="fromTraiterToArchiverEveryOneHundredDays" class="org.alfresco.util.CronTriggerBean">
<property name="jobDetail">
<ref bean="fromTraiterToArchiverScheduler" />
</property>
<property name="scheduler">
<ref bean="schedulerFactory" />
</property>
<property name="cronExpression">
<value>0/10 * * * * ?</value>
</property>
</bean>Spring InvervalJobs and scheduling in Liferay 5
If you have a Liferay portlet that requires some scheduling you can easily use Liferay's built-in Scheduler to add an IntervalJob to the job list, like this. However, what if your IntervalJob is a Spring bean and has dependencies on other Spring beans in the portlet? Unfortunately, at the time of this writing (Liferay 5.1.2), the hot deploy code invokes the Scheduler configuration and execution before the context is initialized–which means you're up a creek when Spring is setup to be initialized with the context (happens to be my case).
An alternative approach is to extend com.liferay.portal.job.JobSchedulerImpl with a Spring singleton and configure the jobs via Spring. While this is very flexible, the singleton is now operating outside the Liferay Quartz realm and therefore will not be subject to the lifecycle of the portlet. That is to say, when you redeploy the portlet the jobs stay scheduled. A more annoying aspect to this is that if you try to shutdown Liferay it appears to hang. Sure the log says that Coyote is stopped, but that's not the case and the process appears to be waiting on a thread. This in turn requires manually killing every time. During development this is such a pain. My guess, without significant research into the bowels of the Liferay Quartz integration, is that the Spring singleton hasn't been properly disposed of.
One solution to this situation is to extend org.springframework.web.context.ContextLoaderListener with something like this:
public class SpringSchedulerContextLoaderListener extends org.springframework.web.context.ContextLoaderListener{
private static final Logger logger = Logger.getLogger(SpringSchedulerContextLoaderListener.class);
public void contextInitialized(ServletContextEvent event) {
super.contextInitialized(event);
}
public void contextDestroyed(ServletContextEvent event) {
JobScheduler j = (JobScheduler) StaticApplicationContextHolder.getApplicationContext().getBean("jobScheduler");
j.shutdown();
super.contextDestroyed(event);
}
}
This will ensure that the Spring singleton JobScheduler will unschedule the registered IntervalJobs when the context is destroyed. You're good to go once this entry replaces org.springframework.web.context.ContextLoaderListener in web.xml.
There may be a more efficient way to do this, but for now this works.
01-21-2009 03:30 AM
01-21-2009 09:15 AM
<bean id="MscArchiverExecuter"
class="org.alfresco.module.msc.action.executer.MscArchiverExecuter" parent="action-executer">
<property name="nodeService">
<ref bean="NodeService" />
</property>
<property name="fileFolderService">
<ref bean="FileFolderService" />
</property>
<property name="transactionService">
<ref bean="TransactionService" />
</property>
<property name="mscSearchService">
<ref bean="MscSearchService" />
</property>
<property name="mscNodeService">
<ref bean="MscNodeService" />
</property>
</bean>
<bean id="fromTraiterToArchiverAction" class="org.alfresco.repo.action.scheduled.SimpleTemplateActionDefinition">
<property name="actionName">
<value>MscArchiverExecuter</value>
</property>
<property name="parameterTemplates">
<map>
<entry>
<key>
<value>destination-folder</value>
</key>
<value>${selectSingleNode('workspace://SpacesStore', 'lucene', 'PATH:"/app:company_home/cm:Archiver"' )}</value>
</entry>
<entry>
<key>
<value>assoc-type</value>
</key>
<value>${node.primaryParentAssoc.typeQName}</value>
</entry>
<entry>
<key>
<value>assoc-name</value>
</key>
<value>${node.primaryParentAssoc.QName}</value>
</entry>
<entry>
<key>
<value>deep-copy</value>
</key>
<value>false</value>
</entry>
</map>
</property>
<property name="templateActionModelFactory">
<ref bean="templateActionModelFactory"/>
</property>
<property name="dictionaryService">
<ref bean="DictionaryService"/>
</property>
<property name="actionService">
<ref bean="ActionService"/>
</property>
<property name="templateService">
<ref bean="TemplateService"/>
</property>
</bean>
<bean id="fromTraiterToArchiverEveryOneHundredDays" class="org.alfresco.repo.action.scheduled.CronScheduledQueryBasedTemplateActionDefinition">
<property name="transactionMode">
<value>UNTIL_FIRST_FAILURE</value>
</property>
<property name="compensatingActionMode">
<value>IGNORE</value>
</property>
<property name="searchService">
<ref bean="SearchService"/>
</property>
<property name="templateService">
<ref bean="TemplateService"/>
</property>
<property name="queryLanguage">
<value>lucene</value>
</property>
<property name="stores">
<list>
<value>workspace://SpacesStore</value>
</list>
</property>
<property name="queryTemplate">
<value>+PATH:"/app:company_home/cm:Traiter/*"</value>
</property>
<property name="cronExpression">
<value>0/10 * * * * ?</value>
</property>
<property name="jobName">
<value>jobC</value>
</property>
<property name="jobGroup">
<value>jobGroup</value>
</property>
<property name="triggerName">
<value>triggerC</value>
</property>
<property name="triggerGroup">
<value>triggerGroup</value>
</property>
<property name="scheduler">
<ref bean="schedulerFactory"/>
</property>
<property name="actionService">
<ref bean="ActionService"/>
</property>
<property name="templateActionModelFactory">
<ref bean="templateActionModelFactory"/>
</property>
<property name="templateActionDefinition">
<ref bean="fromTraiterToArchiverAction"/>
</property>
<property name="transactionService">
<ref bean="TransactionService"/>
</property>
<property name="runAsUser">
<value>System</value>
</property>
</bean>public class MscArchiverExecuter extends ActionExecuterAbstractBase {
public static final String WHO = "automate";
public static final String NAME = "MscArchiverExecuter";
public static final String PARAM_DESTINATION_FOLDER = "destination-folder";
public static final String PARAM_ASSOC_TYPE_QNAME = "assoc-type";
public static final String PARAM_ASSOC_QNAME = "assoc-name";
public static final String PARAM_DEEP_COPY = "deep-copy";
public static final String PARAM_OVERWRITE_COPY = "overwrite-copy";
private static Log log = LogFactory
.getLog( MscArchiverExecuter.class );
private FileFolderService fileFolderService;
private NodeService nodeService;
private MscSearchService mscSearchService;
private TransactionService transactionService;
private MscNodeService mscNodeService;
@Override
protected void addParameterDefinitions(List<ParameterDefinition> pParamList) {
pParamList.add( new ParameterDefinitionImpl( PARAM_DESTINATION_FOLDER,
DataTypeDefinition.NODE_REF, true,
getParamDisplayLabel( PARAM_DESTINATION_FOLDER ) ) );
pParamList.add( new ParameterDefinitionImpl( PARAM_ASSOC_TYPE_QNAME,
DataTypeDefinition.QNAME, true,
getParamDisplayLabel( PARAM_ASSOC_TYPE_QNAME ) ) );
pParamList.add( new ParameterDefinitionImpl( PARAM_ASSOC_QNAME,
DataTypeDefinition.QNAME, true,
getParamDisplayLabel( PARAM_ASSOC_QNAME ) ) );
pParamList.add( new ParameterDefinitionImpl( PARAM_DEEP_COPY,
DataTypeDefinition.BOOLEAN, false,
getParamDisplayLabel( PARAM_DEEP_COPY ) ) );
pParamList.add( new ParameterDefinitionImpl( PARAM_OVERWRITE_COPY,
DataTypeDefinition.BOOLEAN, false,
getParamDisplayLabel( PARAM_OVERWRITE_COPY ) ) );
}
@Override
protected void executeImpl(Action pAction, NodeRef pActionedUponNodeRef) {
//process
}
//add bean setters spring injection
}
Tags
Find what you came for
We want to make your experience in Hyland Connect as valuable as possible, so we put together some helpful links.