001: /*
002: * Copyright 2006 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt. The Original Code is the Pentaho
007: * BI Platform. The Initial Developer is Pentaho Corporation.
008: *
009: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
011: * the license for the specific language governing your rights and limitations.
012: *
013: * Created Aug 1, 2005
014: * @author wseyler
015: */
016:
017: package org.pentaho.plugin.quartz;
018:
019: import java.io.IOException;
020: import java.text.ParseException;
021: import java.util.ArrayList;
022: import java.util.Iterator;
023: import java.util.List;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027: import org.pentaho.actionsequence.dom.actions.AbstractJobSchedulerAction;
028: import org.pentaho.actionsequence.dom.actions.DeleteScheduledJobAction;
029: import org.pentaho.actionsequence.dom.actions.ResumeScheduledJobAction;
030: import org.pentaho.actionsequence.dom.actions.StartScheduledJobAction;
031: import org.pentaho.actionsequence.dom.actions.SuspendScheduledJobAction;
032: import org.pentaho.core.session.IPentahoSession;
033: import org.pentaho.messages.Messages;
034: import org.pentaho.plugin.ComponentBase;
035: import org.quartz.CronTrigger;
036: import org.quartz.JobDataMap;
037: import org.quartz.JobDetail;
038: import org.quartz.Scheduler;
039: import org.quartz.SchedulerException;
040: import org.quartz.SimpleTrigger;
041: import org.quartz.Trigger;
042:
043: /**
044: * @author wseyler
045: *
046: * TODO To change the template for this generated type comment go to Window -
047: * Preferences - Java - Code Style - Code Templates
048: */
049: public class JobSchedulerComponent extends ComponentBase {
050:
051: /**
052: *
053: */
054: private static final long serialVersionUID = -1770772140985331431L;
055:
056: private static final String USER_STR = "username"; //$NON-NLS-1$
057: // Trigger misfire instructions
058: private static final String MISFIRE_POLICY = "misfirePolicy"; //$NON-NLS-1$
059:
060: private static final String INSTRUCTION_NOOP = "INSTRUCTION_NOOP"; //$NON-NLS-1$
061:
062: private static final String INSTRUCTION_RE_EXECUTE_JOB = "INSTRUCTION_RE_EXECUTE_JOB"; //$NON-NLS-1$
063:
064: private static final String INSTRUCTION_DELETE_TRIGGER = "INSTRUCTION_DELETE_TRIGGER"; //$NON-NLS-1$
065:
066: private static final String INSTRUCTION_SET_TRIGGER_COMPLETE = "INSTRUCTION_SET_TRIGGER_COMPLETE"; //$NON-NLS-1$
067:
068: private static final String INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE = "INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE"; //$NON-NLS-1$
069:
070: private static final String MISFIRE_INSTRUCTION_SMART_POLICY = "MISFIRE_INSTRUCTION_SMART_POLICY"; //$NON-NLS-1$
071:
072: // Simple Trigger misfire instructions
073: private static final String MISFIRE_INSTRUCTION_FIRE_NOW = "MISFIRE_INSTRUCTION_FIRE_NOW"; //$NON-NLS-1$
074:
075: private static final String MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT = "MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT"; //$NON-NLS-1$
076:
077: private static final String MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT = "MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT"; //$NON-NLS-1$
078:
079: private static final String MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT = "MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT"; //$NON-NLS-1$
080:
081: private static final String MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT = "MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT"; //$NON-NLS-1$
082:
083: // Cron Trigger misfire instructions
084: private static final String MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = "MISFIRE_INSTRUCTION_FIRE_ONCE_NOW"; //$NON-NLS-1$
085:
086: private static final String MISFIRE_INSTRUCTION_DO_NOTHING = "MISFIRE_INSTRUCTION_DO_NOTHING"; //$NON-NLS-1$
087:
088: private Scheduler sched = null;
089:
090: private List localInputNames = new ArrayList();
091:
092: /*
093: * (non-Javadoc)
094: *
095: * @see org.pentaho.component.IComponent#init()
096: */
097: public boolean init() {
098: localInputNames
099: .add(AbstractJobSchedulerAction.JOB_ACTION_ELEMENT);
100: localInputNames.add(StartScheduledJobAction.SOLUTION_ELEMENT);
101: localInputNames.add(StartScheduledJobAction.PATH_ELEMENT);
102: localInputNames.add(StartScheduledJobAction.ACTION_ELEMENT);
103: localInputNames
104: .add(StartScheduledJobAction.TRIGGER_TYPE_ELEMENT);
105: localInputNames
106: .add(StartScheduledJobAction.TRIGGER_NAME_ELEMENT);
107: localInputNames
108: .add(StartScheduledJobAction.REPEAT_INTERVAL_ELEMENT);
109: localInputNames
110: .add(StartScheduledJobAction.REPEAT_COUNT_ELEMENT);
111: localInputNames
112: .add(AbstractJobSchedulerAction.JOB_NAME_ELEMENT);
113: localInputNames
114: .add(StartScheduledJobAction.CRON_STRING_ELEMENT);
115: try {
116: sched = QuartzSystemListener.getSchedulerInstance();
117: } catch (Exception e) {
118: error(
119: Messages
120: .getErrorString("JobSchedulerComponent.ERROR_0001_NoScheduler"), e); //$NON-NLS-1$
121: return false;
122: }
123: return true;
124: }
125:
126: /*
127: * (non-Javadoc)
128: *
129: * @see org.pentaho.component.ComponentBase#validateAction()
130: */
131: protected boolean validateAction() {
132: return getActionDefinition() instanceof AbstractJobSchedulerAction;
133: }
134:
135: /*
136: * (non-Javadoc)
137: *
138: * @see org.pentaho.component.ComponentBase#validateSystemSettings()
139: */
140: protected boolean validateSystemSettings() {
141: return true;
142: }
143:
144: /*
145: * (non-Javadoc)
146: *
147: * @see org.pentaho.component.ComponentBase#done()
148: */
149: public void done() {
150: sched = null;
151: }
152:
153: /*
154: * (non-Javadoc)
155: *
156: * @see org.pentaho.component.ComponentBase#executeAction()
157: */
158: protected boolean executeAction() {
159:
160: AbstractJobSchedulerAction actionDefinition = (AbstractJobSchedulerAction) getActionDefinition();
161: if (actionDefinition instanceof StartScheduledJobAction) {
162: JobDetail jobDetail = createJobDetail((StartScheduledJobAction) actionDefinition);
163: Trigger trigger = createTrigger((StartScheduledJobAction) actionDefinition);
164:
165: if (trigger == null || jobDetail == null) {
166: error(Messages
167: .getErrorString("JobSchedulerComponent.ERROR_0002_UnableToCreateTriggerOrJob")); //$NON-NLS-1$
168: return false;
169: }
170: return startJob(jobDetail, trigger);
171: } else if (actionDefinition instanceof SuspendScheduledJobAction) {
172: return suspendJob(actionDefinition.getJobName()
173: .getStringValue(), Scheduler.DEFAULT_GROUP);
174: } else if (actionDefinition instanceof DeleteScheduledJobAction) {
175: return deleteJob(actionDefinition.getJobName()
176: .getStringValue(), Scheduler.DEFAULT_GROUP);
177: } else if (actionDefinition instanceof ResumeScheduledJobAction) {
178: return resumeJob(actionDefinition.getJobName()
179: .getStringValue(), Scheduler.DEFAULT_GROUP);
180: } else {
181: return false;
182: }
183: }
184:
185: /*
186: * (non-Javadoc)
187: *
188: * @see org.pentaho.core.system.PentahoBase#getLogger()
189: */
190: public Log getLogger() {
191: return LogFactory.getLog(JobSchedulerComponent.class);
192: }
193:
194: /**
195: * @return
196: */
197: private JobDetail createJobDetail(
198: StartScheduledJobAction actionDefinition) {
199: JobDetail jobDetail = new JobDetail(actionDefinition
200: .getJobName().getStringValue(),
201: Scheduler.DEFAULT_GROUP, QuartzExecute.class);
202:
203: JobDataMap jdm = jobDetail.getJobDataMap();
204: jdm.put(StartScheduledJobAction.SOLUTION_ELEMENT,
205: actionDefinition.getSolution().getStringValue());
206:
207: // Prevents a null path if the action is at the root of the solution.
208: jdm.put(StartScheduledJobAction.PATH_ELEMENT, actionDefinition
209: .getPath().getStringValue("")); //$NON-NLS-1$
210: jdm.put(StartScheduledJobAction.ACTION_ELEMENT,
211: actionDefinition.getAction().getStringValue()); //$NON-NLS-1$
212:
213: // Support scheduled actions in the portal and assigning the credential properly in the execute.
214: IPentahoSession session = this .getSession();
215: if ((session != null) && (session.isAuthenticated())) {
216: jdm.put(USER_STR, session.getName());
217: }
218: Iterator inputNamesIterator = getInputNames().iterator();
219: while (inputNamesIterator.hasNext()) {
220: String inputName = (String) inputNamesIterator.next();
221: if (!localInputNames.contains(inputName)) {
222: Object inputValue = getInputValue(inputName);
223: jobDetail.getJobDataMap().put(inputName, inputValue);
224: }
225: }
226:
227: return jobDetail;
228: }
229:
230: /**
231: * @return
232: */
233: private Trigger createTrigger(StartScheduledJobAction actionDef) {
234: Trigger trigger = null;
235:
236: String triggerType = actionDef.getTriggerType().getStringValue(
237: StartScheduledJobAction.SIMPLE_TRIGGER);
238: if (triggerType.equals(StartScheduledJobAction.SIMPLE_TRIGGER)) {
239: trigger = createSimpleTrigger(actionDef);
240: } else if (triggerType
241: .equals(StartScheduledJobAction.CRON_TRIGGER)) {
242: trigger = createCRONTrigger(actionDef);
243: }
244: addMisfireInstruction(trigger);
245: return trigger;
246: }
247:
248: private Trigger createSimpleTrigger(
249: StartScheduledJobAction actionDef) {
250: String triggerName = actionDef.getTriggerName().getStringValue(
251: StartScheduledJobAction.DEFAULT_STR);
252: // Convert it into milliseconds
253: int repeatInterval = actionDef.getRepeatInterval().getIntValue(
254: 0) * 1000;
255:
256: int repeatCount = actionDef.getRepeatCount().getIntValue(-1);
257: if (repeatCount == -1) {
258: repeatCount = SimpleTrigger.REPEAT_INDEFINITELY;
259: }
260:
261: Trigger trigger = new SimpleTrigger(triggerName,
262: Scheduler.DEFAULT_GROUP, repeatCount, repeatInterval);
263: return trigger;
264: }
265:
266: private Trigger createCRONTrigger(StartScheduledJobAction actionDef) {
267: String triggerName = actionDef.getTriggerName()
268: .getStringValue();
269: String cronExpression = actionDef.getCronString()
270: .getStringValue();
271: try {
272: Trigger trigger = new CronTrigger(triggerName,
273: Scheduler.DEFAULT_GROUP, cronExpression);
274: return trigger;
275: } catch (ParseException e) {
276: error(
277: Messages
278: .getErrorString(
279: "JobSchedulerComponent.ERROR_0003_UnableToParse", cronExpression), e); //$NON-NLS-1$
280: return null;
281: }
282: }
283:
284: /**
285: * @param trigger
286: */
287: private void addMisfireInstruction(Trigger trigger) {
288:
289: String misfirePolicy = MISFIRE_INSTRUCTION_SMART_POLICY;
290: if (isDefinedInput(MISFIRE_POLICY)) {
291: misfirePolicy = getInputStringValue(MISFIRE_POLICY);
292: }
293:
294: trigger
295: .setMisfireInstruction(Trigger.MISFIRE_INSTRUCTION_SMART_POLICY); // Default
296: if (misfirePolicy.equalsIgnoreCase(INSTRUCTION_RE_EXECUTE_JOB)) {
297: trigger
298: .setMisfireInstruction(Trigger.INSTRUCTION_RE_EXECUTE_JOB);
299: } else if (misfirePolicy.equals(INSTRUCTION_DELETE_TRIGGER)) {
300: trigger
301: .setMisfireInstruction(Trigger.INSTRUCTION_DELETE_TRIGGER);
302: } else if (misfirePolicy
303: .equals(INSTRUCTION_SET_TRIGGER_COMPLETE)) {
304: trigger
305: .setMisfireInstruction(Trigger.INSTRUCTION_SET_TRIGGER_COMPLETE);
306: } else if (misfirePolicy
307: .equals(INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE)) {
308: trigger
309: .setMisfireInstruction(Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE);
310: } else if (misfirePolicy
311: .equals(MISFIRE_INSTRUCTION_SMART_POLICY)) {
312: trigger
313: .setMisfireInstruction(Trigger.MISFIRE_INSTRUCTION_SMART_POLICY);
314: } else if (misfirePolicy.equals(INSTRUCTION_NOOP)) {
315: trigger.setMisfireInstruction(Trigger.INSTRUCTION_NOOP);
316: } else if (misfirePolicy.equals(MISFIRE_INSTRUCTION_FIRE_NOW)
317: && (trigger instanceof SimpleTrigger)) {
318: trigger
319: .setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
320: } else if (misfirePolicy
321: .equals(MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT)
322: && (trigger instanceof SimpleTrigger)) {
323: trigger
324: .setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT);
325: } else if (misfirePolicy
326: .equals(MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT)
327: && (trigger instanceof SimpleTrigger)) {
328: trigger
329: .setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT);
330: } else if (misfirePolicy
331: .equals(MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT)
332: && (trigger instanceof SimpleTrigger)) {
333: trigger
334: .setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT);
335: } else if (misfirePolicy
336: .equals(MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT)
337: && (trigger instanceof SimpleTrigger)) {
338: trigger
339: .setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT);
340: } else if (misfirePolicy
341: .equals(MISFIRE_INSTRUCTION_FIRE_ONCE_NOW)
342: && (trigger instanceof CronTrigger)) {
343: trigger
344: .setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW);
345: } else if (misfirePolicy.equals(MISFIRE_INSTRUCTION_DO_NOTHING)
346: && (trigger instanceof CronTrigger)) {
347: trigger
348: .setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
349: }
350: }
351:
352: public boolean startJob(JobDetail jobDetail, Trigger trigger) {
353: try { // If we got one already with the same name... overwrite it.
354: if (sched.getJobDetail(jobDetail.getName(),
355: Scheduler.DEFAULT_GROUP) != null) {
356: deleteJob(jobDetail.getName(), Scheduler.DEFAULT_GROUP);
357: }
358: sched.scheduleJob(jobDetail, trigger);
359: getFeedbackOutputStream()
360: .write(
361: Messages
362: .getString(
363: "JobSchedulerComponent.INFO_0001").getBytes()); //$NON-NLS-1$
364: } catch (SchedulerException e) {
365: error(e.getLocalizedMessage());
366: return false;
367: } catch (IOException e) {
368: error(e.getLocalizedMessage());
369: return false;
370: }
371: return true;
372: }
373:
374: public boolean suspendJob(String jobName, String groupName) {
375: try {
376: sched.pauseJob(jobName, groupName);
377: } catch (SchedulerException e) {
378: error(e.getLocalizedMessage());
379: return false;
380: }
381: return true;
382: }
383:
384: public boolean deleteJob(String jobName, String groupName) {
385: try {
386: sched.deleteJob(jobName, groupName);
387: } catch (SchedulerException e) {
388: error(e.getLocalizedMessage());
389: return false;
390: }
391: return true;
392: }
393:
394: public boolean resumeJob(String jobName, String groupName) {
395: try {
396: sched.resumeJob(jobName, groupName);
397: } catch (SchedulerException e) {
398: error(e.getLocalizedMessage());
399: return false;
400: }
401: return true;
402: }
403: }
|