Package ch.entwine.weblounge.common.impl.scheduler

Source Code of ch.entwine.weblounge.common.impl.scheduler.QuartzJob

/*
*  Weblounge: Web Content Management System
*  Copyright (c) 2003 - 2011 The Weblounge Team
*  http://entwinemedia.com/weblounge
*
*  This program 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
*  of the License, or (at your option) any later version.
*
*  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.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program; if not, write to the Free Software Foundation
*  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package ch.entwine.weblounge.common.impl.scheduler;

import ch.entwine.weblounge.common.Customizable;
import ch.entwine.weblounge.common.impl.util.config.OptionsHelper;
import ch.entwine.weblounge.common.impl.util.xml.XPathHelper;
import ch.entwine.weblounge.common.scheduler.Job;
import ch.entwine.weblounge.common.scheduler.JobTrigger;
import ch.entwine.weblounge.common.scheduler.JobWorker;
import ch.entwine.weblounge.common.site.Environment;

import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Node;

import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;

/**
* Base implementation for jobs.
*/
public final class QuartzJob implements Job, Customizable {

  /** The job identifier */
  protected String identifier = null;

  /** The job name */
  protected String name = null;

  /** The actual job implementation */
  protected Class<? extends JobWorker> worker = null;

  /** Job trigger */
  protected JobTrigger trigger = null;

  /** The options */
  protected OptionsHelper options = null;

  /** Job context map */
  protected Dictionary<String, Object> ctx = null;

  /** The environment */
  protected Environment environment = Environment.Production;

  /**
   * Creates a new job.
   *
   * @param identifier
   *          job identifier
   * @param worker
   *          the job implementation
   * @param trigger
   *          the job trigger
   */
  public QuartzJob(String identifier, Class<? extends JobWorker> worker,
      JobTrigger trigger) {
    this(identifier, worker, null, trigger);
  }

  /**
   * Creates a new job with an initial job context. That context will be passed
   * every time the {@link #execute(Dictionary)} method is triggered.
   *
   * @param identifier
   *          job identifier
   * @param worker
   *          the job implementation
   * @param context
   *          the job context
   * @param trigger
   *          the job trigger
   */
  public QuartzJob(String identifier, Class<? extends JobWorker> worker,
      Dictionary<String, Object> context, JobTrigger trigger) {
    if (identifier == null)
      throw new IllegalArgumentException("Job identifier must not be null");
    if (worker == null)
      throw new IllegalArgumentException("Worker must not be null");
    if (trigger == null)
      throw new IllegalArgumentException("Trigger must not be null");
    this.identifier = identifier;
    this.worker = worker;
    this.trigger = trigger;
    this.ctx = context;
    if (this.ctx == null)
      ctx = new Hashtable<String, Object>();
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.scheduler.Job#setEnvironment(ch.entwine.weblounge.common.site.Environment)
   */
  public void setEnvironment(Environment environment) {
    options.setEnvironment(environment);
    ctx = new Hashtable<String, Object>();
    for (String name : options.getOptionNames()) {
      String[] values = options.getOptionValues(name);
      if (values.length == 1)
        ctx.put(name, values[0]);
      else
        ctx.put(name, values);
    }
  }

  /**
   * Sets the job identifier.
   *
   * @param identifier
   *          the job identifier
   * @throws IllegalArgumentException
   *           if <code>identifier</code> is <code>null</code>
   */
  public void setIdentifier(String identifier) {
    if (identifier == null)
      throw new IllegalArgumentException("Job identifier must not be null");
    this.identifier = identifier;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.scheduler.Job#getIdentifier()
   */
  public String getIdentifier() {
    return identifier;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.scheduler.Job#setName(java.lang.String)
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.scheduler.Job#getName()
   */
  public String getName() {
    return name;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.scheduler.Job#setWorker(java.lang.Class)
   */
  public void setWorker(Class<JobWorker> job) {
    if (worker == null)
      throw new IllegalArgumentException("Job implementation must not be null");
    this.worker = job;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.scheduler.Job#getWorker()
   */
  public Class<? extends JobWorker> getWorker() {
    return worker;
  }

  /**
   * Returns the job context.
   *
   * @return the context
   */
  public Dictionary<String, Object> getContext() {
    return ctx;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.scheduler.Job#setTrigger(ch.entwine.weblounge.common.scheduler.JobTrigger)
   */
  public void setTrigger(JobTrigger trigger) {
    if (trigger == null)
      throw new IllegalArgumentException("Job trigger must not be null");
    this.trigger = trigger;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.scheduler.Job#getTrigger()
   */
  public JobTrigger getTrigger() {
    return trigger;
  }

  /**
   * Resets the job, which means that there will be no more evidence that the
   * job has been run already. This is especially useful in the case where a
   * site is stopped and restarted for maintenance.
   */
  public void reset() {
    trigger.reset();
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.site.Action#setOption(java.lang.String,
   *      java.lang.String)
   */
  public void setOption(String key, String value) {
    options.setOption(key, value);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#setOption(java.lang.String,
   *      java.lang.String, ch.entwine.weblounge.common.site.Environment)
   */
  public void setOption(String name, String value, Environment environment) {
    options.setOption(name, value, environment);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#getOptionValue(java.lang.String)
   */
  public String getOptionValue(String name) {
    return options.getOptionValue(name);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#getOptionValue(java.lang.String,
   *      java.lang.String)
   */
  public String getOptionValue(String name, String defaultValue) {
    return options.getOptionValue(name, defaultValue);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#getOptionValues(java.lang.String)
   */
  public String[] getOptionValues(String name) {
    return options.getOptionValues(name);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#getOptions()
   */
  public Map<String, Map<Environment, List<String>>> getOptions() {
    return options.getOptions();
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#hasOption(java.lang.String)
   */
  public boolean hasOption(String name) {
    return options.hasOption(name);
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#getOptionNames()
   */
  public String[] getOptionNames() {
    return options.getOptionNames();
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.Customizable#removeOption(java.lang.String)
   */
  public void removeOption(String name) {
    options.removeOption(name);
  }

  /**
   * Returns the string representation of this job, which is equal to the value
   * returned by <code>getName()</code>.
   *
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    StringBuffer buf = new StringBuffer(identifier);
    buf.append(" [schedule=");
    buf.append(trigger);
    buf.append("; class=");
    buf.append(worker.getClass().getName());
    buf.append("]");
    return buf.toString();
  }

  /**
   * Initializes this job from an XML node that was generated using
   * {@link #toXml()}.
   * <p>
   * To speed things up, you might consider using the second signature that uses
   * an existing <code>XPath</code> instance instead of creating a new one.
   *
   * @param context
   *          the job node
   * @throws IllegalStateException
   *           if the job cannot be parsed
   * @see #fromXml(Node, XPath)
   * @see #toXml()
   */
  public static Job fromXml(Node context) throws IllegalStateException {
    XPath xpath = XPathFactory.newInstance().newXPath();
    return fromXml(context, xpath);
  }

  /**
   * Initializes this job from an XML node that was generated using
   * {@link #toXml()}.
   *
   * @param context
   *          the job node
   * @param xpathProcessor
   *          xpath processor to use
   * @throws IllegalStateException
   *           if the job cannot be parsed
   * @see #toXml()
   */
  @SuppressWarnings("unchecked")
  public static Job fromXml(Node config, XPath xPathProcessor)
      throws IllegalStateException {

    CronJobTrigger jobTrigger = null;
    Dictionary<String, Object> ctx = new Hashtable<String, Object>();
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

    // Main attributes
    String identifier = XPathHelper.valueOf(config, "@id", xPathProcessor);

    // Implementation class
    String className = XPathHelper.valueOf(config, "m:class", xPathProcessor);
    Class<? extends JobWorker> c;
    try {
      c = (Class<? extends JobWorker>) classLoader.loadClass(className);
      c.newInstance(); // Create an instance just to make sure we catch any errors right here
    } catch (ClassNotFoundException e) {
      throw new IllegalStateException("Implementation " + className + " for job '" + identifier + "' not found", e);
    } catch (InstantiationException e) {
      throw new IllegalStateException("Error instantiating impelementation " + className + " for job '" + identifier + "'", e);
    } catch (IllegalAccessException e) {
      throw new IllegalStateException("Access violation instantiating implementation " + className + " for job '" + identifier + "'", e);
    } catch (Throwable t) {
      throw new IllegalStateException("Error loading implementation " + className + " for job '" + identifier + "'", t);
    }

    // Read execution schedule
    String schedule = XPathHelper.valueOf(config, "m:schedule", xPathProcessor);
    if (schedule == null)
      throw new IllegalStateException("No schedule has been defined for job '" + identifier + "'");
    jobTrigger = new CronJobTrigger(schedule);

    // Read options
    Node nodes = XPathHelper.select(config, "m:options", xPathProcessor);
    OptionsHelper options = OptionsHelper.fromXml(nodes, xPathProcessor);
    for (Map.Entry<String, Map<Environment, List<String>>> entry : options.getOptions().entrySet()) {
      String key = entry.getKey();
      Map<Environment, List<String>> environments = entry.getValue();
      for (Environment environment : environments.keySet()) {
        List<String> values = environments.get(environment);
        if (values.size() == 1)
          ctx.put(key, values.get(0));
        else
          ctx.put(key, values.toArray(new String[values.size()]));
      }
    }

    // Did we find something?

    QuartzJob job = new QuartzJob(identifier, c, ctx, jobTrigger);
    job.options = options;

    // name
    String name = XPathHelper.valueOf(config, "m:name", xPathProcessor);
    job.setName(name);

    return job;
  }

  /**
   * {@inheritDoc}
   *
   * @see ch.entwine.weblounge.common.scheduler.Job#toXml()
   */
  public String toXml() {
    StringBuffer b = new StringBuffer();
    b.append("<job id=\"");
    b.append(identifier);
    b.append("\">");

    // Names
    if (StringUtils.isNotBlank(name)) {
      b.append("<name><![CDATA[");
      b.append(name);
      b.append("]]></name>");
    }

    // Class
    b.append("<class>");
    b.append(worker.getName());
    b.append("</class>");

    // Schedule
    if (!(trigger instanceof CronJobTrigger))
      throw new IllegalStateException("Cannot serialize job trigger of type " + trigger.getClass().getName());
    b.append("<schedule>");
    b.append(((CronJobTrigger) trigger).getCronExpression());
    b.append("</schedule>");

    // Options
    Enumeration<String> e = ctx.keys();
    if (e.hasMoreElements()) {
      b.append("<options>");
      while (e.hasMoreElements()) {
        String key = e.nextElement();
        b.append("<option>");
        b.append("<name>");
        b.append(key);
        b.append("</name>");
        Object value = ctx.get(key);
        if (value instanceof String[]) {
          String[] values = (String[]) ctx.get(key);
          for (String v : values) {
            b.append("<value><![CDATA[");
            b.append(v);
            b.append("]]></value>");
          }
        } else {
          b.append("<value><![CDATA[");
          b.append(value);
          b.append("]]></value>");
        }
        b.append("</option>");
      }
      b.append("</options>");
    }

    b.append("</job>");
    return b.toString();
  }

}
TOP

Related Classes of ch.entwine.weblounge.common.impl.scheduler.QuartzJob

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.