org.anodyneos.jse.cron.CronDaemon.java Source code

Java tutorial

Introduction

Here is the source code for org.anodyneos.jse.cron.CronDaemon.java

Source

/*
 * Copyright (c) 2002 John Vasileff
 *
 * Permission  is  hereby  granted,  free of  charge,  to  any  person
 * obtaining  a copy  of  this software  and associated  documentation
 * files   (the  "Software"),   to  deal   in  the   Software  without
 * restriction, including without limitation  the rights to use, copy,
 * modify, merge, publish, distribute,  sublicense, and/or sell copies
 * of the  Software, and  to permit  persons to  whom the  Software is
 * furnished to do so, subject to the following conditions:
 *
 * The  above copyright  notice and  this permission  notice shall  be
 * included in all copies or substantial portions of the Software.
 *
 * THE  SOFTWARE  IS  PROVIDED  "AS   IS",  WITHOUT  WARRANTY  OF  ANY
 * KIND,  EXPRESS  OR  IMPLIED,  INCLUDING  BUT  NOT  LIMITED  TO  THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES  OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT,  TORT OR OTHERWISE, ARISING FROM, OUT  OF OR IN
 * CONNECTION WITH  THE SOFTWARE OR THE  USE OR OTHER DEALINGS  IN THE
 * SOFTWARE.
 */

package org.anodyneos.jse.cron;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.TimeZone;
import java.util.UUID;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventLocator;
import javax.xml.bind.util.ValidationEventCollector;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.anodyneos.jse.JseDateAwareJob;
import org.anodyneos.jse.JseException;
import org.anodyneos.jse.JseTimerService;
import org.anodyneos.jse.cron.config.Config;
import org.anodyneos.jse.cron.config.Job;
import org.anodyneos.jse.cron.config.Property;
import org.anodyneos.jse.cron.config.Schedule;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.xml.sax.InputSource;

public class CronDaemon {

    private static final Log log = LogFactory.getLog(CronDaemon.class);

    private ArrayList<JseTimerService> timerServices = new ArrayList<JseTimerService>();

    public CronDaemon(InputSource source) throws JseException {

        Schedule schedule;

        // parse source
        try {

            JAXBContext jc = JAXBContext.newInstance("org.anodyneos.jse.cron.config");
            Unmarshaller u = jc.createUnmarshaller();
            //Schedule
            Source schemaSource = new StreamSource(Thread.currentThread().getContextClassLoader()
                    .getResourceAsStream("org/anodyneos/jse/cron/cron.xsd"));

            SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            Schema schema = sf.newSchema(schemaSource);
            u.setSchema(schema);
            ValidationEventCollector vec = new ValidationEventCollector();
            u.setEventHandler(vec);

            JAXBElement<?> rootElement;
            try {
                rootElement = ((JAXBElement<?>) u.unmarshal(source));
            } catch (UnmarshalException ex) {
                if (!vec.hasEvents()) {
                    throw ex;
                } else {
                    for (ValidationEvent ve : vec.getEvents()) {
                        ValidationEventLocator vel = ve.getLocator();
                        log.error("Line:Col[" + vel.getLineNumber() + ":" + vel.getColumnNumber() + "]:"
                                + ve.getMessage());
                    }
                    throw new JseException("Validation failed for source publicId='" + source.getPublicId()
                            + "'; systemId='" + source.getSystemId() + "';");
                }
            }

            schedule = (Schedule) rootElement.getValue();

            if (vec.hasEvents()) {
                for (ValidationEvent ve : vec.getEvents()) {
                    ValidationEventLocator vel = ve.getLocator();
                    log.warn("Line:Col[" + vel.getLineNumber() + ":" + vel.getColumnNumber() + "]:"
                            + ve.getMessage());
                }
            }

        } catch (JseException e) {
            throw e;
        } catch (Exception e) {
            throw new JseException("Cannot parse " + source + ".", e);
        }

        SpringHelper springHelper = new SpringHelper();

        ////////////////
        //
        // Configure Spring and Create Beans
        //
        ////////////////

        TimeZone defaultTimeZone;

        if (schedule.isSetTimeZone()) {
            defaultTimeZone = getTimeZone(schedule.getTimeZone());
        } else {
            defaultTimeZone = TimeZone.getDefault();
        }

        if (schedule.isSetSpringContext() && schedule.getSpringContext().isSetConfig()) {
            for (Config config : schedule.getSpringContext().getConfig()) {
                springHelper.addXmlClassPathConfigLocation(config.getClassPathResource());
            }
        }

        for (org.anodyneos.jse.cron.config.JobGroup jobGroup : schedule.getJobGroup()) {
            for (Job job : jobGroup.getJob()) {
                if (job.isSetBeanRef()) {
                    if (job.isSetBean() || job.isSetClassName()) {
                        throw new JseException("Cannot set bean or class attribute for job when beanRef is set.");
                    } // else config ok
                } else {
                    if (!job.isSetClassName()) {
                        throw new JseException("must set either class or beanRef for job.");
                    }
                    GenericBeanDefinition beanDef = new GenericBeanDefinition();
                    MutablePropertyValues propertyValues = new MutablePropertyValues();

                    if (!job.isSetBean()) {
                        job.setBean(UUID.randomUUID().toString());
                    }

                    if (springHelper.containsBean(job.getBean())) {
                        throw new JseException(
                                "Bean name already used; overriding not allowed here: " + job.getBean());
                    }

                    beanDef.setBeanClassName(job.getClassName());

                    for (Property prop : job.getProperty()) {
                        String value = null;
                        if (prop.isSetSystemProperty()) {
                            value = System.getProperty(prop.getSystemProperty());
                        }
                        if (null == value) {
                            value = prop.getValue();
                        }

                        propertyValues.addPropertyValue(prop.getName(), value);
                    }

                    beanDef.setPropertyValues(propertyValues);
                    springHelper.registerBean(job.getBean(), beanDef);
                    job.setBeanRef(job.getBean());
                }
            }
        }

        springHelper.init();

        ////////////////
        //
        // Configure Timer Services
        //
        ////////////////

        for (org.anodyneos.jse.cron.config.JobGroup jobGroup : schedule.getJobGroup()) {

            String jobGroupName;
            JseTimerService service = new JseTimerService();

            timerServices.add(service);

            if (jobGroup.isSetName()) {
                jobGroupName = jobGroup.getName();
            } else {
                jobGroupName = UUID.randomUUID().toString();
            }

            if (jobGroup.isSetMaxConcurrent()) {
                service.setMaxConcurrent(jobGroup.getMaxConcurrent());
            }

            for (Job job : jobGroup.getJob()) {

                TimeZone jobTimeZone = defaultTimeZone;

                if (job.isSetTimeZone()) {
                    jobTimeZone = getTimeZone(job.getTimeZone());
                } else {
                    jobTimeZone = defaultTimeZone;
                }

                Object obj;

                Date notBefore = null;
                Date notAfter = null;

                if (job.isSetNotBefore()) {
                    notBefore = job.getNotBefore().toGregorianCalendar(jobTimeZone, null, null).getTime();
                }
                if (job.isSetNotAfter()) {
                    notAfter = job.getNotAfter().toGregorianCalendar(jobTimeZone, null, null).getTime();
                }

                CronSchedule cs = new CronSchedule(job.getSchedule(), jobTimeZone, job.getMaxIterations(),
                        job.getMaxQueue(), notBefore, notAfter);

                obj = springHelper.getBean(job.getBeanRef());
                log.info("Adding job " + jobGroup.getName() + "/" + job.getName() + " using bean "
                        + job.getBeanRef());
                if (obj instanceof CronJob) {
                    ((CronJob) obj).setCronContext(new CronContext(jobGroupName, job.getName(), cs));
                }
                if (obj instanceof JseDateAwareJob) {
                    service.createTimer((JseDateAwareJob) obj, cs);
                } else if (obj instanceof Runnable) {
                    service.createTimer((Runnable) obj, cs);
                } else {
                    throw new JseException("Job must implement Runnable or JseDateAwareJob");
                }
            }
        }
    }

    public void start() {
        Iterator<JseTimerService> it = timerServices.iterator();
        while (it.hasNext()) {
            JseTimerService service = it.next();
            log.info("Starting service thread: " + service.getName());
            service.start();
        }
    }

    public static void main(String[] args) throws Exception {
        try {
            InputSource source = new InputSource(args[0]);
            CronDaemon server = new CronDaemon(source);
            server.start();
        } catch (Exception e) {
            log.fatal(e.getMessage(), e);
            throw e;
        }
    }

    protected TimeZone getTimeZone(String tzs) throws JseException {
        String[] allTZ = TimeZone.getAvailableIDs();
        TimeZone tz = null;
        for (int i = 0; i < allTZ.length; i++) {
            if (allTZ[i].equals(tzs)) {
                tz = TimeZone.getTimeZone(tzs);
                break;
            }
        }
        if (null == tz) {
            throw new JseException("TimeZone not available: " + tzs);
        }
        return tz;
    }

}