All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencms.scheduler.CmsScheduleManager Maven / Gradle / Ivy

Go to download

OpenCms is an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.

There is a newer version: 17.0
Show newest version
/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software GmbH & Co. KG, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.scheduler;

import org.opencms.file.CmsObject;
import org.opencms.i18n.CmsMessageContainer;
import org.opencms.main.CmsIllegalArgumentException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.security.CmsRole;
import org.opencms.security.CmsRoleViolationException;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.apache.commons.logging.Log;

import org.quartz.CronScheduleBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.JobDetailImpl;
import org.quartz.impl.StdSchedulerFactory;

/**
 * Manages the OpenCms scheduled jobs.

* * Please see the documentation of the class {@link org.opencms.scheduler.CmsScheduledJobInfo} * for a full description of the OpenCms scheduling capabilities.

* * The OpenCms scheduler implementation internally uses the * Quartz scheduler from * the OpenSymphony project.

* * This manager class implements the org.quartz.Job interface * and wraps all calls to the {@link org.opencms.scheduler.I_CmsScheduledJob} implementing * classes.

* * @since 6.0.0 * * @see org.opencms.scheduler.CmsScheduledJobInfo */ public class CmsScheduleManager implements Job { /** Key for the scheduled job description in the job data map. */ public static final String SCHEDULER_JOB_INFO = "org.opencms.scheduler.CmsScheduledJobInfo"; /** The log object for this class. */ private static final Log LOG = CmsLog.getLog(CmsScheduleManager.class); /** The Admin context used for creation of users for the individual jobs. */ private CmsObject m_adminCms; /** The list of job entries from the configuration. */ private List m_configuredJobs; /** The list of scheduled jobs. */ private List m_jobs; /** The initialized scheduler. */ private Scheduler m_scheduler; /** * Default constructor for the scheduler manager, * used only when a new job is scheduled.

*/ public CmsScheduleManager() { // important: this constructor is always called when a new job is // generated, so it _must_ remain empty } /** * Used by the configuration to create a new Scheduler during system startup.

* * @param configuredJobs the jobs from the configuration */ public CmsScheduleManager(List configuredJobs) { m_configuredJobs = configuredJobs; int size = 0; if (m_configuredJobs != null) { size = m_configuredJobs.size(); } if (CmsLog.INIT.isInfoEnabled()) { CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SCHEDULER_CREATED_1, new Integer(size))); } } /** * Implementation of the Quartz job interface.

* * The architecture is that this scheduler manager generates * a new (empty) instance of itself for every OpenCms job scheduled with Quartz. * When the Quartz job is executed, the configured * implementation of {@link I_CmsScheduledJob} will be called from this method.

* * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) */ public void execute(JobExecutionContext context) { JobDataMap jobData = context.getJobDetail().getJobDataMap(); CmsScheduledJobInfo jobInfo = (CmsScheduledJobInfo)jobData.get(SCHEDULER_JOB_INFO); if (jobInfo == null) { LOG.error( Messages.get().getBundle().key( Messages.LOG_INVALID_JOB_1, ((JobDetailImpl)context.getJobDetail()).getFullName())); // can not continue return; } // update the execution times in job info jobInfo.setPreviousFireTime(context.getFireTime()); jobInfo.setNextFireTime(context.getNextFireTime()); executeJob(jobInfo); } /** * Given a job ID, this directly executes the corresponding job.

* * @param jobId the job id */ public void executeDirectly(String jobId) { final CmsScheduledJobInfo jobInfo = (CmsScheduledJobInfo)getJob(jobId).clone(); if (jobInfo == null) { LOG.error(Messages.get().getBundle().key(Messages.LOG_INVALID_JOB_1, "null")); return; } Thread thread = new Thread() { /** * @see java.lang.Thread#run() */ @Override public void run() { executeJob(jobInfo); } }; thread.start(); } /** * Returns the currently scheduled job description identified by the given id. * * @param id the job id * * @return a job or null if not found */ public CmsScheduledJobInfo getJob(String id) { Iterator it = m_jobs.iterator(); while (it.hasNext()) { CmsScheduledJobInfo job = it.next(); if (job.getId().equals(id)) { return job; } } // not found return null; } /** * Returns the currently scheduled job descriptions in an unmodifiable list.

* * The objects in the List are of type {@link CmsScheduledJobInfo}.

* * @return the currently scheduled job descriptions in an unmodifiable list */ public List getJobs() { return Collections.unmodifiableList(m_jobs); } /** * Initializes the OpenCms scheduler.

* * @param adminCms an OpenCms context object that must have been initialized with "Admin" permissions * * @throws CmsRoleViolationException if the user has insufficient role permissions */ public synchronized void initialize(CmsObject adminCms) throws CmsRoleViolationException { if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_1_CORE_OBJECT) { // simple unit tests will have runlevel 1 and no CmsObject OpenCms.getRoleManager().checkRole(adminCms, CmsRole.WORKPLACE_MANAGER); } // the list of job entries m_jobs = new ArrayList(); // save the admin cms m_adminCms = adminCms; // Quartz scheduler settings Properties properties = new Properties(); properties.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, "OpenCmsScheduler"); properties.put(StdSchedulerFactory.PROP_SCHED_THREAD_NAME, "OpenCms: Scheduler"); properties.put(StdSchedulerFactory.PROP_SCHED_RMI_EXPORT, CmsStringUtil.FALSE); properties.put(StdSchedulerFactory.PROP_SCHED_RMI_PROXY, CmsStringUtil.FALSE); properties.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, CmsSchedulerThreadPool.class.getName()); properties.put(StdSchedulerFactory.PROP_JOB_STORE_CLASS, "org.quartz.simpl.RAMJobStore"); // this will be required in quartz versions from 1.6, but constants are not supported in earlier versions properties.put("org.quartz.scheduler.jmx.export", CmsStringUtil.FALSE); properties.put("org.quartz.scheduler.jmx.proxy", CmsStringUtil.FALSE); try { // initialize the Quartz scheduler SchedulerFactory schedulerFactory = new StdSchedulerFactory(properties); m_scheduler = schedulerFactory.getScheduler(); } catch (Exception e) { LOG.error(Messages.get().getBundle().key(Messages.LOG_NO_SCHEDULER_0), e); // can not continue m_scheduler = null; return; } if (CmsLog.INIT.isInfoEnabled()) { CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SCHEDULER_INITIALIZED_0)); } if (m_configuredJobs != null) { // add all jobs from the system configuration for (int i = 0; i < m_configuredJobs.size(); i++) { try { CmsScheduledJobInfo job = m_configuredJobs.get(i); scheduleJob(adminCms, job); } catch (CmsSchedulerException e) { // ignore this job, but keep scheduling the other jobs // note: the log is has already been written } } } try { // start the scheduler m_scheduler.start(); } catch (Exception e) { LOG.error(Messages.get().getBundle().key(Messages.LOG_CANNOT_START_SCHEDULER_0), e); // can not continue m_scheduler = null; return; } if (CmsLog.INIT.isInfoEnabled()) { CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SCHEDULER_STARTED_0)); CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SCHEDULER_CONFIG_FINISHED_0)); } } /** * Adds a new job to the scheduler.

* * @param cms an OpenCms context object that must have been initialized with "Admin" permissions * @param jobInfo the job info describing the job to schedule * * @throws CmsRoleViolationException if the user has insufficient role permissions * @throws CmsSchedulerException if the job could not be scheduled for any reason */ public synchronized void scheduleJob(CmsObject cms, CmsScheduledJobInfo jobInfo) throws CmsRoleViolationException, CmsSchedulerException { if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_1_CORE_OBJECT) { // simple unit tests will have runlevel 1 and no CmsObject OpenCms.getRoleManager().checkRole(cms, CmsRole.WORKPLACE_MANAGER); } if ((jobInfo == null) || (jobInfo.getClassName() == null)) { // prevent NPE CmsMessageContainer message = Messages.get().container(Messages.ERR_INVALID_JOB_CONFIGURATION_0); LOG.error(message.key()); // can not continue throw new CmsSchedulerException(message); } if (m_scheduler == null) { CmsMessageContainer message = Messages.get().container(Messages.ERR_NO_SCHEDULER_1, jobInfo.getJobName()); LOG.error(message.key()); // can not continue throw new CmsSchedulerException(message); } Class jobClass; try { jobClass = Class.forName(jobInfo.getClassName()); if (!I_CmsScheduledJob.class.isAssignableFrom(jobClass)) { // class does not implement required interface CmsMessageContainer message = Messages.get().container( Messages.ERR_JOB_CLASS_BAD_INTERFACE_2, jobInfo.getClassName(), I_CmsScheduledJob.class.getName()); LOG.error(message.key()); if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_2_INITIALIZING) { throw new CmsIllegalArgumentException(message); } else { jobInfo.setActive(false); } } } catch (ClassNotFoundException e) { // class does not exist CmsMessageContainer message = Messages.get().container( Messages.ERR_JOB_CLASS_NOT_FOUND_1, jobInfo.getClassName()); LOG.error(message.key()); if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_2_INITIALIZING) { throw new CmsIllegalArgumentException(message); } else { jobInfo.setActive(false); } } String jobId = jobInfo.getId(); boolean idCreated = false; if (jobId == null) { // generate a new job id CmsUUID jobUUID = new CmsUUID(); jobId = "OpenCmsJob_".concat(jobUUID.toString()); jobInfo.setId(jobId); idCreated = true; } // generate Quartz job trigger Trigger trigger; try { CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobInfo.getCronExpression()); TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger(); triggerBuilder.withSchedule(scheduleBuilder); triggerBuilder.withIdentity(jobId, Scheduler.DEFAULT_GROUP); trigger = triggerBuilder.build(); } catch (Exception e) { if (idCreated) { jobInfo.setId(null); } CmsMessageContainer message = Messages.get().container( Messages.ERR_BAD_CRON_EXPRESSION_2, jobInfo.getJobName(), jobInfo.getCronExpression()); LOG.error(message.key()); // can not continue throw new CmsSchedulerException(message); } CmsScheduledJobInfo oldJob = null; if (!idCreated) { // this job is already scheduled, remove the currently scheduled instance and keep the id // important: since the new job may have errors, it's required to make sure the old job is only unscheduled // if the new job info is o.k. oldJob = unscheduleJob(cms, jobId); if (oldJob == null) { CmsMessageContainer message = Messages.get().container( Messages.ERR_JOB_WITH_ID_DOES_NOT_EXIST_1, jobId); LOG.warn(message.key()); // can not continue throw new CmsSchedulerException(message); } // open the job configuration (in case it has been frozen) jobInfo.setFrozen(false); } // only schedule jobs when they are marked as active if (jobInfo.isActive()) { // generate Quartz job detail JobDetailImpl jobDetail = (JobDetailImpl)JobBuilder.newJob(CmsScheduleManager.class).build(); jobDetail.setName(jobInfo.getId()); jobDetail.setGroup(Scheduler.DEFAULT_GROUP); // add the trigger to the job info jobInfo.setTrigger(trigger); // now set the job data JobDataMap jobData = new JobDataMap(); jobData.put(CmsScheduleManager.SCHEDULER_JOB_INFO, jobInfo); jobDetail.setJobDataMap(jobData); // finally add the job to the Quartz scheduler try { m_scheduler.scheduleJob(jobDetail, trigger); if (LOG.isInfoEnabled()) { LOG.info( Messages.get().getBundle().key( Messages.LOG_JOB_SCHEDULED_4, new Object[] { new Integer(m_jobs.size()), jobInfo.getJobName(), jobInfo.getClassName(), jobInfo.getContextInfo().getUserName()})); Date nextExecution = jobInfo.getExecutionTimeNext(); if (nextExecution != null) { LOG.info( Messages.get().getBundle().key( Messages.LOG_JOB_NEXT_EXECUTION_2, jobInfo.getJobName(), nextExecution)); } } } catch (Exception e) { if (LOG.isDebugEnabled()) { LOG.debug(e.getMessage(), e); } if (idCreated) { jobInfo.setId(null); } CmsMessageContainer message = Messages.get().container( Messages.ERR_COULD_NOT_SCHEDULE_JOB_2, jobInfo.getJobName(), jobInfo.getClassName()); if (oldJob != null) { // make sure an old job is re-scheduled jobDetail = (JobDetailImpl)JobBuilder.newJob(CmsScheduleManager.class).build(); jobDetail.setName(oldJob.getId()); jobDetail.setGroup(Scheduler.DEFAULT_GROUP); jobDetail.setJobDataMap(jobData); try { m_scheduler.scheduleJob(jobDetail, oldJob.getTrigger()); m_jobs.add(oldJob); } catch (SchedulerException e2) { if (LOG.isDebugEnabled()) { LOG.debug(e2.getMessage(), e2); } // unable to re-schedule original job - not much we can do about this... message = Messages.get().container( Messages.ERR_COULD_NOT_RESCHEDULE_JOB_2, jobInfo.getJobName(), jobInfo.getClassName()); } } if (LOG.isWarnEnabled()) { LOG.warn(message.key()); } throw new CmsSchedulerException(message); } } // freeze the scheduled job configuration jobInfo.initConfiguration(); // add the job to the list of configured jobs m_jobs.add(jobInfo); } /** * Shuts down this instance of the OpenCms scheduler manager.

*/ public synchronized void shutDown() { m_adminCms = null; if (CmsLog.INIT.isInfoEnabled()) { CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SHUTDOWN_1, this.getClass().getName())); } if (m_scheduler != null) { try { m_scheduler.shutdown(); } catch (SchedulerException e) { LOG.error(Messages.get().getBundle().key(Messages.LOG_SHUTDOWN_ERROR_0)); } } m_scheduler = null; } /** * Removes a currently scheduled job from the scheduler.

* * @param cms an OpenCms context object that must have been initialized with "Admin" permissions * @param jobId the id of the job to unschedule, obtained with {@link CmsScheduledJobInfo#getId()} * * @return the {@link CmsScheduledJobInfo} of the sucessfully unscheduled job, * or null if the job could not be unscheduled * * @throws CmsRoleViolationException if the user has insufficient role permissions */ public synchronized CmsScheduledJobInfo unscheduleJob(CmsObject cms, String jobId) throws CmsRoleViolationException { if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_1_CORE_OBJECT) { // simple unit tests will have runlevel 1 and no CmsObject OpenCms.getRoleManager().checkRole(cms, CmsRole.WORKPLACE_MANAGER); } CmsScheduledJobInfo jobInfo = null; if (m_jobs.size() > 0) { // try to remove the job from the OpenCms list of jobs for (int i = (m_jobs.size() - 1); i >= 0; i--) { CmsScheduledJobInfo job = m_jobs.get(i); if (jobId.equals(job.getId())) { m_jobs.remove(i); if (jobInfo != null) { LOG.error(Messages.get().getBundle().key(Messages.LOG_MULTIPLE_JOBS_FOUND_1, jobId)); } jobInfo = job; } } } if ((jobInfo != null) && jobInfo.isActive()) { // job currently active, remove it from the Quartz scheduler try { // try to remove the job from Quartz m_scheduler.unscheduleJob(new TriggerKey(jobId, Scheduler.DEFAULT_GROUP)); if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_UNSCHEDULED_JOB_1, jobId)); } } catch (SchedulerException e) { if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_UNSCHEDULING_ERROR_1, jobId)); } } } return jobInfo; } /** * Executes the given job.

* * @param jobInfo the job info bean */ protected void executeJob(CmsScheduledJobInfo jobInfo) { if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_JOB_STARTING_1, jobInfo.getJobName())); } I_CmsScheduledJob job = jobInfo.getJobInstance(); if (job != null) { // launch the job try { CmsObject cms = null; // update the request time in the job info to the current time jobInfo.updateContextRequestTime(); // some simple test cases might run below this runlevel if (OpenCms.getRunLevel() >= OpenCms.RUNLEVEL_3_SHELL_ACCESS) { // generate a CmsObject for the job context // must access the scheduler manager instance from the OpenCms singleton // to get the initialized CmsObject cms = OpenCms.initCmsObject(OpenCms.getScheduleManager().getAdminCms(), jobInfo.getContextInfo()); } String result = job.launch(cms, jobInfo.getParameters()); if (CmsStringUtil.isNotEmpty(result) && LOG.isInfoEnabled()) { LOG.info( Messages.get().getBundle().key(Messages.LOG_JOB_EXECUTION_OK_2, jobInfo.getJobName(), result)); } } catch (Throwable t) { LOG.error(Messages.get().getBundle().key(Messages.LOG_JOB_EXECUTION_ERROR_1, jobInfo.getJobName()), t); } } if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_JOB_EXECUTED_1, jobInfo.getJobName())); Date nextExecution = jobInfo.getExecutionTimeNext(); if (nextExecution != null) { LOG.info( Messages.get().getBundle().key( Messages.LOG_JOB_NEXT_EXECUTION_2, jobInfo.getJobName(), nextExecution)); } } } /** * Returns the {@link CmsObject} this Scheduler Manager was initialized with.

* * @return the {@link CmsObject} this Scheduler Manager was initialized with */ private synchronized CmsObject getAdminCms() { return m_adminCms; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy