/* * eXist Scheduler Module Extension ScheduleFunctions * Copyright (C) 2006-09 Adam Retter <adam.retter@devon.gov.uk> * www.adamretter.co.uk * * 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. * * $Id$ */ package org.exist.xquery.modules.scheduler; import org.exist.dom.QName; import org.exist.scheduler.Scheduler; import org.exist.scheduler.UserJavaJob; import org.exist.scheduler.UserJob; import org.exist.scheduler.UserXQueryJob; import org.exist.security.User; import org.exist.xquery.BasicFunction; import org.exist.xquery.Cardinality; import org.exist.xquery.FunctionSignature; import org.exist.xquery.XPathException; import org.exist.xquery.XQueryContext; import org.exist.xquery.value.*; import org.w3c.dom.Element; import org.w3c.dom.Node; import java.util.Properties; /** * eXist Scheduler Module Extension ScheduleFunctions * * Schedules job's with eXist's Scheduler * * @author Adam Retter <adam.retter@devon.gov.uk> * @author Loren Cahlander <loren.cahlander@gmail.com> * @serial 2009-05-15 * @version 1.3 * * @see org.exist.xquery.BasicFunction#BasicFunction(org.exist.xquery.XQueryContext, org.exist.xquery.FunctionSignature) */ public class ScheduleFunctions extends BasicFunction { public static final String SCHEDULE_XQUERY_CRON_JOB = "schedule-xquery-cron-job"; public static final String SCHEDULE_XQUERY_PERIODIC_JOB = "schedule-xquery-periodic-job"; public static final String SCHEDULE_JAVA_CRON_JOB = "schedule-java-cron-job"; public static final String SCHEDULE_JAVA_PERIODIC_JOB = "schedule-java-periodic-job"; private Scheduler scheduler = null; private final static FunctionSignature scheduleJavaCronJobNoParam = new FunctionSignature( new QName(SCHEDULE_JAVA_CRON_JOB, SchedulerModule.NAMESPACE_URI, SchedulerModule.PREFIX), "Schedules the Java Class named (the class must extend org.exist.scheduler.UserJavaJob) according " + "to the Cron expression. The job will be registered using the job name.", new SequenceType[] { new FunctionParameterSequenceType("java-classname", Type.STRING, Cardinality.EXACTLY_ONE, "The full name of the class to be executed. It must extend the org.exist.scheduler.UserJavaJob class."), new FunctionParameterSequenceType("cron-expression", Type.STRING, Cardinality.EXACTLY_ONE, "The cron expression. Please see the scheduler documentation."), new FunctionParameterSequenceType("job-name", Type.STRING, Cardinality.EXACTLY_ONE, "The name of the job.") }, new FunctionParameterSequenceType("success", Type.BOOLEAN, Cardinality.EXACTLY_ONE, "a flag indicating successful execution")); private final static FunctionSignature scheduleJavaCronJobParam = new FunctionSignature( new QName(SCHEDULE_JAVA_CRON_JOB, SchedulerModule.NAMESPACE_URI, SchedulerModule.PREFIX), "Schedules the Java Class named (the class must extend org.exist.scheduler.UserJavaJob) according " + "to the Cron expression. The job will be registered using the name passed in $job-name. The final " + "argument can be used to specify " + "parameters for the job, which will be passed to the query as external variables. Parameters are specified " + "in an XML fragment with the following structure: <parameters><param name=\"param-name1\" value=\"param-value1\"/></parameters>.", new SequenceType[] { new FunctionParameterSequenceType("java-classname", Type.STRING, Cardinality.EXACTLY_ONE, "The full name of the class to be executed. It must extend the org.exist.scheduler.UserJavaJob class."), new FunctionParameterSequenceType("cron-expression", Type.STRING, Cardinality.EXACTLY_ONE, "The cron expression. Please see the scheduler documentation."), new FunctionParameterSequenceType("job-name", Type.STRING, Cardinality.EXACTLY_ONE, "The name of the job."), new FunctionParameterSequenceType("job-parameters", Type.ELEMENT, Cardinality.ZERO_OR_ONE, "The XML fragment with the following structure: <parameters><param name=\"param-name1\" value=\"param-value1\"/></parameters>") }, new FunctionParameterSequenceType("success", Type.BOOLEAN, Cardinality.EXACTLY_ONE, "a flag indicating successful execution")); private final static FunctionSignature scheduleJavaPeriodicParam = new FunctionSignature( new QName(SCHEDULE_JAVA_PERIODIC_JOB, SchedulerModule.NAMESPACE_URI, SchedulerModule.PREFIX), "Schedules the Java Class named (the class must extend org.exist.scheduler.UserJavaJob) according " + "to the periodic value. The job will be registered using the job name. The $job-parameters " + "argument can be used to specify " + "parameters for the job, which will be passed to the query as external variables. Parameters are specified " + "in an XML fragment with the following structure: " + "<parameters><param name=\"param-name1\" value=\"param-value1\"/></parameters>, Given the delay and the repeat.", new SequenceType[] { new FunctionParameterSequenceType("java-classname", Type.STRING, Cardinality.EXACTLY_ONE, "The full name of the class to be executed. It must extend the org.exist.scheduler.UserJavaJob class."), new FunctionParameterSequenceType("period", Type.INTEGER, Cardinality.EXACTLY_ONE, "Time in milliseconds between execution of the job"), new FunctionParameterSequenceType("job-name", Type.STRING, Cardinality.EXACTLY_ONE, "The name of the job."), new FunctionParameterSequenceType("job-parameters", Type.ELEMENT, Cardinality.ZERO_OR_ONE, "The XML fragment with the following structure: <parameters><param name=\"param-name1\" value=\"param-value1\"/></parameters>"), new FunctionParameterSequenceType("delay", Type.INTEGER, Cardinality.EXACTLY_ONE, "The period in milliseconds to delay the start of a job."), new FunctionParameterSequenceType("repeat", Type.INTEGER, Cardinality.EXACTLY_ONE, "The number of times to repeat the job after the initial execution") }, new FunctionParameterSequenceType("success", Type.BOOLEAN, Cardinality.EXACTLY_ONE, "a flag indicating successful execution")); private final static FunctionSignature scheduleXQueryCronJobNoParam = new FunctionSignature( new QName(SCHEDULE_XQUERY_CRON_JOB, SchedulerModule.NAMESPACE_URI, SchedulerModule.PREFIX), "Schedules the named XQuery resource (e.g. /db/foo.xql) according to the Cron expression. " + "XQuery job's will be launched under the guest account initially, although the running XQuery may switch permissions through calls to xmldb:login(). " + "The job will be registered using the job name.", new SequenceType[] { new FunctionParameterSequenceType("xquery-resource", Type.STRING, Cardinality.EXACTLY_ONE, "The path to the XQuery resource"), new FunctionParameterSequenceType("cron-expression", Type.STRING, Cardinality.EXACTLY_ONE, "The cron expression. Please see the scheduler documentation."), new FunctionParameterSequenceType("job-name", Type.STRING, Cardinality.EXACTLY_ONE, "The name of the job.") }, new FunctionParameterSequenceType("success", Type.BOOLEAN, Cardinality.EXACTLY_ONE, "a flag indicating successful execution")); private final static FunctionSignature scheduleXQueryCronJobParam = new FunctionSignature( new QName(SCHEDULE_XQUERY_CRON_JOB, SchedulerModule.NAMESPACE_URI, SchedulerModule.PREFIX), "Schedules the named XQuery resource (e.g. /db/foo.xql) according to the Cron expression. " + "XQuery job's will be launched under the guest account initially, although the running XQuery may switch permissions through calls to xmldb:login(). " + "The job will be registered using the job name. The final argument can be used to specify " + "parameters for the job, which will be passed to the query as external variables. Parameters are specified " + "in an XML fragment with the following structure: " + "<parameters><param name=\"param-name1\" value=\"param-value1\"/></parameters>", new SequenceType[] { new FunctionParameterSequenceType("xquery-resource", Type.STRING, Cardinality.EXACTLY_ONE, "The path to the XQuery resource"), new FunctionParameterSequenceType("cron-expression", Type.STRING, Cardinality.EXACTLY_ONE, "A cron expression. Please see the scheduler documentation."), new FunctionParameterSequenceType("job-name", Type.STRING, Cardinality.EXACTLY_ONE, "The name of the job."), new FunctionParameterSequenceType("job-parameters", Type.ELEMENT, Cardinality.ZERO_OR_ONE, "XML fragment with the following structure: <parameters><param name=\"param-name1\" value=\"param-value1\"/></parameters>") }, new FunctionParameterSequenceType("success", Type.BOOLEAN, Cardinality.EXACTLY_ONE, "Flag indicating successful execution")); private final static FunctionSignature scheduleXQueryPeriodicParam = new FunctionSignature( new QName(SCHEDULE_XQUERY_PERIODIC_JOB, SchedulerModule.NAMESPACE_URI, SchedulerModule.PREFIX), "Schedules the named XQuery resource (e.g. /db/foo.xql) according to the period. " + "XQuery job's will be launched under the guest account initially, although the running XQuery may switch permissions through calls to xmldb:login(). " + "The job will be registered using the job name. The job parameters argument can be used to specify " + "parameters for the job, which will be passed to the query as external variables. Parameters are specified " + "in an XML fragment with the following structure: " + "<parameters><param name=\"param-name1\" value=\"param-value1\"/></parameters>" + ", Given the delay passed and the repeat value.", new SequenceType[] { new FunctionParameterSequenceType("xquery-resource", Type.STRING, Cardinality.EXACTLY_ONE, "The path to the XQuery resource"), new FunctionParameterSequenceType("period", Type.INTEGER, Cardinality.EXACTLY_ONE, "Time in milliseconds between execution of the job"), new FunctionParameterSequenceType("job-name", Type.STRING, Cardinality.EXACTLY_ONE, "The name of the job."), new FunctionParameterSequenceType("job-parameters", Type.ELEMENT, Cardinality.ZERO_OR_ONE, "XML fragment with the following structure: <parameters><param name=\"param-name1\" value=\"param-value1\"/></parameters>"), new FunctionParameterSequenceType("delay", Type.INTEGER, Cardinality.EXACTLY_ONE, "Can be used with a period in milliseconds to delay the start of a job."), new FunctionParameterSequenceType("repeat", Type.INTEGER, Cardinality.EXACTLY_ONE, "Number of times to repeat the job after the initial execution") }, new FunctionParameterSequenceType("success", Type.BOOLEAN, Cardinality.EXACTLY_ONE, "Flag indicating successful execution")); public final static FunctionSignature[] signatures = { scheduleJavaCronJobNoParam, scheduleJavaCronJobParam, scheduleJavaPeriodicParam, scheduleXQueryCronJobNoParam, scheduleXQueryCronJobParam, scheduleXQueryPeriodicParam }; /** * ScheduleFunctions Constructor * * @param context The Context of the calling XQuery */ public ScheduleFunctions(XQueryContext context, FunctionSignature signature) { super(context, signature); scheduler = context.getBroker().getBrokerPool().getScheduler(); } /** * evaluate thed call to the xquery function, * it is really the main entry point of this class * * @param args arguments from the function call * @param contextSequence the Context Sequence to operate on (not used here internally!) * @return A sequence representing the result of the function call * * @see org.exist.xquery.BasicFunction#eval(org.exist.xquery.value.Sequence[], org.exist.xquery.value.Sequence) */ public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException { String resource = args[0].getStringValue(); long periodicValue = 0; long delayValue = 0; int repeatValue = -1; String jobName = args[2].getStringValue(); Properties properties = null; if (getArgumentCount() >= 4 && args[3].hasOne()) { Node options = ((NodeValue)args[3].itemAt(0)).getNode(); properties = new Properties(); parseParameters(options, properties); } if (getArgumentCount() >= 5) { delayValue = ((IntegerValue)args[4].itemAt(0)).getLong(); } if (getArgumentCount() >= 6) { repeatValue = ((IntegerValue)args[5].itemAt(0)).getInt(); } User user = context.getUser(); //Check if the user is a DBA if(!user.hasDbaRole()) { return(BooleanValue.FALSE); } Object job = null; boolean isCron = true; //scheule-xquery-cron-job if(isCalledAs(SCHEDULE_XQUERY_CRON_JOB)) { job = new UserXQueryJob(jobName, resource, user); } else if(isCalledAs(SCHEDULE_XQUERY_PERIODIC_JOB)) { periodicValue = ((IntegerValue) args[1].itemAt(0)).getLong(); job = new UserXQueryJob(jobName, resource, user); isCron = false; } //schedule-java-cron-job else if(isCalledAs(SCHEDULE_JAVA_CRON_JOB) || isCalledAs(SCHEDULE_JAVA_PERIODIC_JOB)) { if (isCalledAs(SCHEDULE_JAVA_PERIODIC_JOB)) { periodicValue = ((IntegerValue) args[1].itemAt(0)).getLong(); } try { //Check if the Class is a UserJob Class jobClass = Class.forName(resource); job = jobClass.newInstance(); if(!(job instanceof UserJavaJob)) { LOG.error("Cannot Schedule job. Class " + resource + " is not an instance of org.exist.scheduler.UserJavaJob"); return(BooleanValue.FALSE); } ((UserJavaJob)job).setName(jobName); } catch(ClassNotFoundException cnfe) { LOG.error(cnfe); return(BooleanValue.FALSE); } catch(IllegalAccessException iae) { LOG.error(iae); return(BooleanValue.FALSE); } catch(InstantiationException ie) { LOG.error(ie); return(BooleanValue.FALSE); } } if(job != null) { if (isCron) { //schedule the job String cronExpression = args[1].getStringValue(); if(scheduler.createCronJob(cronExpression, (UserJob)job, properties)) { return(BooleanValue.TRUE); } else { return(BooleanValue.FALSE); } } else { //schedule the job if(scheduler.createPeriodicJob(periodicValue, (UserJob)job, delayValue, properties, repeatValue)) { return(BooleanValue.TRUE); } else { return(BooleanValue.FALSE); } } } else { return(BooleanValue.FALSE); } } private void parseParameters(Node options, Properties properties) throws XPathException { if(options.getNodeType() == Node.ELEMENT_NODE && options.getLocalName().equals("parameters")) { Node child = options.getFirstChild(); while(child != null) { if(child.getNodeType() == Node.ELEMENT_NODE && child.getLocalName().equals("param")) { Element elem = (Element)child; String name = elem.getAttribute("name"); String value = elem.getAttribute("value"); if(name == null || value == null) throw new XPathException(this, "Name or value attribute missing for stylesheet parameter"); properties.setProperty(name, value); } child = child.getNextSibling(); } } } }