/*
* 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();
}
}
}
}