/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program 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. * * Copyright 2005 - 2009 Pentaho Corporation. All rights reserved. * * * Created Aug 1, 2005 * @author wseyler */ package org.pentaho.platform.scheduler.action; import java.io.IOException; import java.io.OutputStream; import java.text.ParseException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.actionsequence.dom.actions.AbstractJobSchedulerAction; import org.pentaho.actionsequence.dom.actions.DeleteScheduledJobAction; import org.pentaho.actionsequence.dom.actions.ResumeScheduledJobAction; import org.pentaho.actionsequence.dom.actions.StartScheduledJobAction; import org.pentaho.actionsequence.dom.actions.SuspendScheduledJobAction; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.engine.services.solution.ComponentBase; import org.pentaho.platform.scheduler.QuartzExecute; import org.pentaho.platform.scheduler.QuartzSystemListener; import org.pentaho.platform.scheduler.messages.Messages; import org.quartz.CronTrigger; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SimpleTrigger; import org.quartz.Trigger; /** * @author wseyler * * TODO To change the template for this generated type comment go to Window - * Preferences - Java - Code Style - Code Templates */ public class JobSchedulerComponent extends ComponentBase { /** * */ private static final long serialVersionUID = -1770772140985331431L; private static final String USER_STR = "username"; //$NON-NLS-1$ // Trigger misfire instructions private static final String MISFIRE_POLICY = "misfirePolicy"; //$NON-NLS-1$ private static final String INSTRUCTION_NOOP = "INSTRUCTION_NOOP"; //$NON-NLS-1$ private static final String INSTRUCTION_RE_EXECUTE_JOB = "INSTRUCTION_RE_EXECUTE_JOB"; //$NON-NLS-1$ private static final String INSTRUCTION_DELETE_TRIGGER = "INSTRUCTION_DELETE_TRIGGER"; //$NON-NLS-1$ private static final String INSTRUCTION_SET_TRIGGER_COMPLETE = "INSTRUCTION_SET_TRIGGER_COMPLETE"; //$NON-NLS-1$ private static final String INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE = "INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE"; //$NON-NLS-1$ private static final String MISFIRE_INSTRUCTION_SMART_POLICY = "MISFIRE_INSTRUCTION_SMART_POLICY"; //$NON-NLS-1$ // Simple Trigger misfire instructions private static final String MISFIRE_INSTRUCTION_FIRE_NOW = "MISFIRE_INSTRUCTION_FIRE_NOW"; //$NON-NLS-1$ private static final String MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT = "MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT"; //$NON-NLS-1$ private static final String MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT = "MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT"; //$NON-NLS-1$ private static final String MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT = "MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT"; //$NON-NLS-1$ private static final String MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT = "MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT"; //$NON-NLS-1$ // Cron Trigger misfire instructions private static final String MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = "MISFIRE_INSTRUCTION_FIRE_ONCE_NOW"; //$NON-NLS-1$ private static final String MISFIRE_INSTRUCTION_DO_NOTHING = "MISFIRE_INSTRUCTION_DO_NOTHING"; //$NON-NLS-1$ private Scheduler sched = null; private final List localInputNames = new ArrayList(); /* * (non-Javadoc) * * @see org.pentaho.component.IComponent#init() */ @Override public boolean init() { localInputNames.add(AbstractJobSchedulerAction.JOB_ACTION_ELEMENT); localInputNames.add(StartScheduledJobAction.SOLUTION_ELEMENT); localInputNames.add(StartScheduledJobAction.PATH_ELEMENT); localInputNames.add(StartScheduledJobAction.ACTION_ELEMENT); localInputNames.add(StartScheduledJobAction.TRIGGER_TYPE_ELEMENT); localInputNames.add(StartScheduledJobAction.TRIGGER_NAME_ELEMENT); localInputNames.add(StartScheduledJobAction.REPEAT_INTERVAL_ELEMENT); localInputNames.add(StartScheduledJobAction.REPEAT_COUNT_ELEMENT); localInputNames.add(AbstractJobSchedulerAction.JOB_NAME_ELEMENT); localInputNames.add(StartScheduledJobAction.CRON_STRING_ELEMENT); try { sched = QuartzSystemListener.getSchedulerInstance(); } catch (Exception e) { error(Messages.getInstance().getErrorString("JobSchedulerComponent.ERROR_0001_NoScheduler"), e); //$NON-NLS-1$ return false; } return true; } /* * (non-Javadoc) * * @see org.pentaho.component.ComponentBase#validateAction() */ @Override protected boolean validateAction() { return getActionDefinition() instanceof AbstractJobSchedulerAction; } /* * (non-Javadoc) * * @see org.pentaho.component.ComponentBase#validateSystemSettings() */ @Override protected boolean validateSystemSettings() { return true; } /* * (non-Javadoc) * * @see org.pentaho.component.ComponentBase#done() */ @Override public void done() { sched = null; } /* * (non-Javadoc) * * @see org.pentaho.component.ComponentBase#executeAction() */ @Override protected boolean executeAction() { AbstractJobSchedulerAction actionDefinition = (AbstractJobSchedulerAction) getActionDefinition(); if (actionDefinition instanceof StartScheduledJobAction) { JobDetail jobDetail = createJobDetail((StartScheduledJobAction) actionDefinition); Trigger trigger = createTrigger((StartScheduledJobAction) actionDefinition); if ((trigger == null) || (jobDetail == null)) { error(Messages.getInstance().getErrorString("JobSchedulerComponent.ERROR_0002_UnableToCreateTriggerOrJob")); //$NON-NLS-1$ return false; } return startJob(jobDetail, trigger); } else if (actionDefinition instanceof SuspendScheduledJobAction) { return suspendJob(actionDefinition.getJobName().getStringValue(), Scheduler.DEFAULT_GROUP); } else if (actionDefinition instanceof DeleteScheduledJobAction) { return deleteJob(actionDefinition.getJobName().getStringValue(), Scheduler.DEFAULT_GROUP); } else if (actionDefinition instanceof ResumeScheduledJobAction) { return resumeJob(actionDefinition.getJobName().getStringValue(), Scheduler.DEFAULT_GROUP); } else { return false; } } /* * (non-Javadoc) * * @see org.pentaho.core.system.PentahoBase#getLogger() */ @Override public Log getLogger() { return LogFactory.getLog(JobSchedulerComponent.class); } /** * @return */ private JobDetail createJobDetail(final StartScheduledJobAction actionDefinition) { JobDetail jobDetail = new JobDetail(actionDefinition.getJobName().getStringValue(), Scheduler.DEFAULT_GROUP, QuartzExecute.class); JobDataMap jdm = jobDetail.getJobDataMap(); jdm.put(StartScheduledJobAction.SOLUTION_ELEMENT, actionDefinition.getSolution().getStringValue()); // Prevents a null path if the action is at the root of the solution. jdm.put(StartScheduledJobAction.PATH_ELEMENT, actionDefinition.getPath().getStringValue("")); //$NON-NLS-1$ jdm.put(StartScheduledJobAction.ACTION_ELEMENT, actionDefinition.getAction().getStringValue()); // Support scheduled actions in the portal and assigning the credential properly in the execute. IPentahoSession session = this.getSession(); if ((session != null) && (session.isAuthenticated())) { jdm.put(JobSchedulerComponent.USER_STR, session.getName()); } Iterator inputNamesIterator = getInputNames().iterator(); while (inputNamesIterator.hasNext()) { String inputName = (String) inputNamesIterator.next(); if (!localInputNames.contains(inputName)) { Object inputValue = getInputValue(inputName); jobDetail.getJobDataMap().put(inputName, inputValue); } } return jobDetail; } /** * @return */ private Trigger createTrigger(final StartScheduledJobAction actionDef) { Trigger trigger = null; String triggerType = actionDef.getTriggerType().getStringValue(StartScheduledJobAction.SIMPLE_TRIGGER); if (triggerType.equals(StartScheduledJobAction.SIMPLE_TRIGGER)) { trigger = createSimpleTrigger(actionDef); } else if (triggerType.equals(StartScheduledJobAction.CRON_TRIGGER)) { trigger = createCRONTrigger(actionDef); } addMisfireInstruction(trigger); return trigger; } private Trigger createSimpleTrigger(final StartScheduledJobAction actionDef) { String triggerName = actionDef.getTriggerName().getStringValue(StartScheduledJobAction.DEFAULT_STR); // Convert it into milliseconds int repeatInterval = actionDef.getRepeatInterval().getIntValue(0) * 1000; int repeatCount = actionDef.getRepeatCount().getIntValue(-1); if (repeatCount == -1) { repeatCount = SimpleTrigger.REPEAT_INDEFINITELY; } Trigger trigger = new SimpleTrigger(triggerName, Scheduler.DEFAULT_GROUP, repeatCount, repeatInterval); return trigger; } private Trigger createCRONTrigger(final StartScheduledJobAction actionDef) { String triggerName = actionDef.getTriggerName().getStringValue(); String cronExpression = actionDef.getCronString().getStringValue(); try { Trigger trigger = new CronTrigger(triggerName, Scheduler.DEFAULT_GROUP, cronExpression); return trigger; } catch (ParseException e) { error(Messages.getInstance().getErrorString("JobSchedulerComponent.ERROR_0003_UnableToParse", cronExpression), e); //$NON-NLS-1$ return null; } } /** * @param trigger */ private void addMisfireInstruction(final Trigger trigger) { String misfirePolicy = JobSchedulerComponent.MISFIRE_INSTRUCTION_SMART_POLICY; if (isDefinedInput(JobSchedulerComponent.MISFIRE_POLICY)) { misfirePolicy = getInputStringValue(JobSchedulerComponent.MISFIRE_POLICY); } trigger.setMisfireInstruction(Trigger.MISFIRE_INSTRUCTION_SMART_POLICY); // Default if (misfirePolicy.equalsIgnoreCase(JobSchedulerComponent.INSTRUCTION_RE_EXECUTE_JOB)) { trigger.setMisfireInstruction(Trigger.INSTRUCTION_RE_EXECUTE_JOB); } else if (misfirePolicy.equals(JobSchedulerComponent.INSTRUCTION_DELETE_TRIGGER)) { trigger.setMisfireInstruction(Trigger.INSTRUCTION_DELETE_TRIGGER); } else if (misfirePolicy.equals(JobSchedulerComponent.INSTRUCTION_SET_TRIGGER_COMPLETE)) { trigger.setMisfireInstruction(Trigger.INSTRUCTION_SET_TRIGGER_COMPLETE); } else if (misfirePolicy.equals(JobSchedulerComponent.INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE)) { trigger.setMisfireInstruction(Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE); } else if (misfirePolicy.equals(JobSchedulerComponent.MISFIRE_INSTRUCTION_SMART_POLICY)) { trigger.setMisfireInstruction(Trigger.MISFIRE_INSTRUCTION_SMART_POLICY); } else if (misfirePolicy.equals(JobSchedulerComponent.INSTRUCTION_NOOP)) { trigger.setMisfireInstruction(Trigger.INSTRUCTION_NOOP); } else if (misfirePolicy.equals(JobSchedulerComponent.MISFIRE_INSTRUCTION_FIRE_NOW) && (trigger instanceof SimpleTrigger)) { trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW); } else if (misfirePolicy.equals(JobSchedulerComponent.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT) && (trigger instanceof SimpleTrigger)) { trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT); } else if (misfirePolicy.equals(JobSchedulerComponent.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT) && (trigger instanceof SimpleTrigger)) { trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT); } else if (misfirePolicy .equals(JobSchedulerComponent.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT) && (trigger instanceof SimpleTrigger)) { trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT); } else if (misfirePolicy .equals(JobSchedulerComponent.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT) && (trigger instanceof SimpleTrigger)) { trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT); } else if (misfirePolicy.equals(JobSchedulerComponent.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) && (trigger instanceof CronTrigger)) { trigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW); } else if (misfirePolicy.equals(JobSchedulerComponent.MISFIRE_INSTRUCTION_DO_NOTHING) && (trigger instanceof CronTrigger)) { trigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING); } } public boolean startJob(final JobDetail jobDetail, final Trigger trigger) { try { // If we got one already with the same name... overwrite it. if (sched.getJobDetail(jobDetail.getName(), Scheduler.DEFAULT_GROUP) != null) { deleteJob(jobDetail.getName(), Scheduler.DEFAULT_GROUP); } sched.scheduleJob(jobDetail, trigger); OutputStream feedbackOutputStream = getFeedbackOutputStream(); if (feedbackOutputStream != null) { feedbackOutputStream.write(Messages.getInstance().getString("JobSchedulerComponent.INFO_0001").getBytes()); //$NON-NLS-1$ } } catch (SchedulerException e) { error(e.getLocalizedMessage()); return false; } catch (IOException e) { error(e.getLocalizedMessage()); return false; } return true; } public boolean suspendJob(final String jobName, final String groupName) { try { sched.pauseJob(jobName, groupName); } catch (SchedulerException e) { error(e.getLocalizedMessage()); return false; } return true; } public boolean deleteJob(final String jobName, final String groupName) { try { sched.deleteJob(jobName, groupName); } catch (SchedulerException e) { error(e.getLocalizedMessage()); return false; } return true; } public boolean resumeJob(final String jobName, final String groupName) { try { sched.resumeJob(jobName, groupName); } catch (SchedulerException e) { error(e.getLocalizedMessage()); return false; } return true; } }