/********************************************************* begin of preamble
**
** Copyright (C) 2003-2010 Software- und Organisations-Service GmbH.
** All rights reserved.
**
** This file may be used under the terms of either the
**
** GNU General Public License version 2.0 (GPL)
**
** as published by the Free Software Foundation
** http://www.gnu.org/licenses/gpl-2.0.txt and appearing in the file
** LICENSE.GPL included in the packaging of this file.
**
** or the
**
** Agreement for Purchase and Licensing
**
** as offered by Software- und Organisations-Service GmbH
** in the respective terms of supply that ship with this file.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
** IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
** THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
** POSSIBILITY OF SUCH DAMAGE.
********************************************************** end of preamble*/
/*
* CronConverter.java
* Created on 20.08.2007
*
*/
package sos.scheduler.cron;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import sos.util.SOSLogger;
import sos.util.SOSStandardLogger;
/**
* This Class converts a crontab file to a Job Scheduler XML Configuration
*
* @author Andreas Liebert
*/
public class CronConverter {
private DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
private DocumentBuilder docBuilder;
/**
* Regular Expression
* -?([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+(.+)$
* matches cron lines such as
* 59 23 * * * /usr/bin/xmessage.sh
* with grouping
*/
private final static String cronRegEx = "-?([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+(.+)$";
/**
* Regular Expression for system crontab has one more column (user)
*/
private final static String cronRegExSystem = "-?([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+(.+)$";
protected Pattern cronRegExPattern;
protected Pattern cronRegExSystemPattern;
private SOSLogger logger;
private boolean systemCronTab = false;
private boolean oldRunTime = false;
private boolean usedNewRunTime = false;
private String changeUserCommand = "";
/**
* Regular Expression
* (@reboot|@yearly|@monthly|@weekly|@daily|@hourly)\s+(.+)$
* matches cron lines with aliases such as
* @monthly test -x /usr/sbin/texpire && /usr/sbin/texpire
*/
private final static String cronRegExAlias = "(@reboot|@yearly|@annually|@monthly|@weekly|@daily|@midnight|@hourly)\\s+(.+)$";
protected Pattern cronRegExAliasPattern;
/**
* Regular Expression
* ^\s*#\s*(.+)
* matches cron comment lines
*/
private final static String cronRegExComment = "^\\s*#\\s*(.+)";
protected Pattern cronRegExCommentPattern;
/**
* Regular Expression
* \s*job_name\s*=\s*(.+)
* matches comments which set a job name
*/
private final static String cronRegExJobName = "\\s*job_name\\s*=\\s*(.+)";
protected Pattern cronRegExJobNamePattern;
/**
* Regular Expression
* \s*job_title\s*=\s*(.+)
* matches comments which set a job title
*/
private final static String cronRegExJobTitle = "\\s*job_title\\s*=\\s*(.+)";
protected Pattern cronRegExJobTitlePattern;
/**
* Regular Expression
* \s*job_timeout\s*=\s*(.+)
* matches comments which set a job timeout
*/
private final static String cronRegExJobTimeout = "\\s*job_timeout\\s*=\\s*(.+)";
protected Pattern cronRegExJobTimeoutPattern;
/**
* Regular Expression
* ^\s*(\w+)\s*=\s*(.+)
* matches environment variable settings
*/
private final static String cronRegExEnvironment = "^\\s*(\\w+)\\s*=\\s*(.+)";
protected Pattern cronRegExEnvironmentPattern;
/**
* Regular Expression
* [^\s]* /[^\s]*
* matches a path (at least one "/")
*/
private final static String commandRegEx = "[^\\s]*/[^\\s]*";
private Pattern commandRegExPattern;
/**
* Regular expression
* (.*)_(\d*)$
* Matches incremented job names
*/
private final static String jobNameRegEx = "(.*)_(\\d*)$";
private Pattern jobNameRegExPattern;
private HashSet skipLines = new HashSet();
private HashSet reservedJobNames = new HashSet();
private String timeout = "600";
protected Pattern currentCronPattern;
/**
* @param args
*/
public static void main(String[] args) {
/*try {
test();
} catch(Exception ex){
ex.printStackTrace();
}
*/
try {
//SOSArguments arguments = new SOSArguments(args);
SOSLogger sosLogger;
String sourceFile="";
String targetFile="";
String changeUser ="";
File source=null;
File target=null;
int logLevel=0;
boolean sysTab = false;
boolean useOldRunTime = false;
String jobTimeout="";
/*
try {
sourceFile = arguments.as_string("-crontab=");
if (sourceFile.equalsIgnoreCase("/etc/crontab")) sysTab = true;
targetFile = arguments.as_string("-target=");
logLevel = arguments.as_int("-v=",SOSStandardLogger.INFO);
sysTab = arguments.as_bool("-system=",sysTab);
useOldRunTime = arguments.as_bool("-oldRunTime=",false);
changeUser = arguments.as_string("-change-user=", "su");
} catch (Exception e1) {
System.out.println(e1.getMessage());
showUsage();
System.exit(0);
}*/
Options options = new Options();
Option optSysTab = OptionBuilder.withArgName("0|1").hasArg().withDescription("set to 1 if source is the system crontab (with user field)").create("systab");
Option optSourceFile = OptionBuilder.withArgName("file").hasArgs().isRequired()
.withDescription("crontab file").create("crontab");
Option optTargetFile = OptionBuilder.withArgName("file").hasArgs().isRequired()
.withDescription("xml configuration file").create("target");
Option optLogLevel = OptionBuilder.withArgName("level").hasArg().withType(new Integer(0))
.withDescription("loglevel [0=info] [1=debug1]...[9=debug9]").create("v");
Option optChangeUser = OptionBuilder.withArgName("command").hasArgs()
.withDescription("change user command for -systab=1. 'su' or 'sudo' or define your own command using $SCHEDULER_CRONTAB_USER.")
.create("changeuser");
Option optTimeOut = OptionBuilder.withArgName("seconds").hasArg()
.withDescription("job timeout (0 for unlimited").create("timeout");
Option optOldRunTime = new Option("oldRunTime","");
options.addOption(optSysTab);
options.addOption(optSourceFile);
options.addOption(optTargetFile);
options.addOption(optLogLevel);
options.addOption(optChangeUser);
CommandLineParser parser = new GnuParser();
CommandLine line=null;
try{
line = parser.parse(options, args);
} catch(Exception e){
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("cronconverter", options, true);
System.exit(0);
}
sourceFile = getWholeArgument(line.getOptionValues("crontab"));
if (sourceFile.equalsIgnoreCase("/etc/crontab")) sysTab = true;
targetFile = getWholeArgument(line.getOptionValues("target"));
String ll = line.getOptionValue("v", ""+SOSStandardLogger.INFO);
logLevel = Integer.parseInt(ll);
if (line.hasOption(optSysTab.getOpt())){
sysTab =(line.getOptionValue(optSysTab.getOpt()).trim().equals("1"));
}
useOldRunTime = line.hasOption("oldRunTime");
changeUser = "";
if (line.hasOption("changeuser")){
changeUser = getWholeArgument(line.getOptionValues("changeuser"));
}
jobTimeout = line.getOptionValue("timeout");
if (logLevel==0) logLevel=SOSLogger.INFO;
sosLogger = new SOSStandardLogger(logLevel);
target = new File(targetFile);
source = new File(sourceFile);
CronConverter cc = new CronConverter(sosLogger);
if (jobTimeout!=null && jobTimeout.length()>0) cc.setTimeout(jobTimeout);
cc.setChangeUserCommand(changeUser);
cc.setSystemCronTab(sysTab);
cc.oldRunTime = useOldRunTime;
cc.cronFile2SchedulerXMLFile(source, target);
} catch (Exception e) {
e.printStackTrace();
}
}
private static String getWholeArgument(String[] optionValues) {
String value="";
for (int i = 0; i < optionValues.length; i++) {
value += optionValues[i];
if (i+1<optionValues.length) value+=" ";
}
return value;
}
private static void test() throws Exception {
SOSLogger log = new SOSStandardLogger(SOSLogger.DEBUG9);
CronConverter cc = new CronConverter(log);
// File inputFile = new File("J:\\E\\java\\al\\sos.scheduler\\config\\crontab");
// File outputFile = new File("J:\\E\\java\\al\\sos.scheduler\\config\\scheduler_cron.xml");
File inputFile = new File("c:/temp/cronfile.cron");
File outputFile = new File("c:/temp/scheduler_cron.xml");
cc.cronFile2SchedulerXMLFile(inputFile, outputFile);
// von http://www.newbie-net.de/anleitung_cron.html
//Document job = cc.line2Job("5 22 * * * test -x /usr/sbin/texpire && /usr/sbin/texpire");
//Document job = cc.line2Job("5 22 * * * test -x bla");
//Document job = cc.line2Job("5 22 3 * * test -x bla");
//Document job = cc.line2Job("5 22 3,5,8-12 * * test -x bla");
//Document job = cc.line2Job("1 0 * * 1 /usr/bin/xsoftwaresamba.sh");
//Ein Bindestrich - gibt einen Zeitraum an. Jeden Tag von 12-24 Uhr (jede Stunde) ...:
//Document job = cc.line2Job("0 12-24 * * * /usr/bin/xsoftwaresamba.sh");
// Ein Schr�gstrich / teilt einen Zeitraum ein. Zwischen 6 und 23 Uhr alle 15 Minuten ...:
//Document job = cc.line2Job("*/15 6-23 * * * /usr/bin/xsoftwaresamba.sh");
// Jeden Tag um 0:00 und um 12:00 Uhr wird das Script xmessage.sh aufgerufen:
//Document job = cc.line2Job("0 0,12 * * * /usr/bin/xmesssage.sh");
// von http://docs.phplist.com/CronJobExamples
// ...I want the script to run every 15 minutes starting at 5 p.m. (17) and running through 11:00 p.m. (23). This should happen Tue. (2) through Sat. (6)
//Document job = cc.line2Job("*/15 17-23 * * 2,3,4,5,6 /sbin/phplist -pprocessqueue");
//Document job = cc.line2Job("*/15 17-23 * * Tue-sat /sbin/phplist -pprocessqueue");
//Document job = cc.line2Job("15,30,45,00 * * * * /var/www/www.domain.com/phplist phplist-2.10.2/bin/phplist -p processqueue > /dev/null");
// von http://www.pantz.org/os/linux/programs/cron.shtml
// This line executes the "ping" and the "ls" command every 12am and 12pm on the 1st day of every 2nd month
// Document job = cc.line2Job("0 0,12 1 */2 * /sbin/ping -c 192.168.0.1; ls -la >>/var/log/cronrun");
// von http://www.monetizers.com/cronjob.php
// every day 23 minutes after every even hour (0:23, 2:23, ...)
//Document job = cc.line2Job("23 0-23/2 * * * $HOME/report.sh");
/*StringWriter out = new StringWriter();
OutputFormat format = new OutputFormat(job);
format.setIndenting(true);
format.setIndent(2);
XMLSerializer serializer = new XMLSerializer(out, format);
serializer.serialize(job);
log.info(out.toString());*/
}
public CronConverter(SOSLogger log) throws Exception{
docBuilder = docFactory.newDocumentBuilder();
cronRegExAliasPattern = Pattern.compile(cronRegExAlias);
cronRegExPattern = Pattern.compile(cronRegEx);
cronRegExSystemPattern = Pattern.compile(cronRegExSystem);
cronRegExCommentPattern = Pattern.compile(cronRegExComment);
cronRegExEnvironmentPattern = Pattern.compile(cronRegExEnvironment);
commandRegExPattern = Pattern.compile(commandRegEx);
jobNameRegExPattern = Pattern.compile(jobNameRegEx);
cronRegExJobNamePattern = Pattern.compile(cronRegExJobName);
cronRegExJobTitlePattern = Pattern.compile(cronRegExJobTitle);
cronRegExJobTimeoutPattern = Pattern.compile(cronRegExJobTimeout);
currentCronPattern = cronRegExPattern;
logger = log;
}
public void cronFile2SchedulerXMLFile(File cronFile, File schedulerXML) throws Exception{
try{
Document configurationDocument = cronFile2SchedulerXML(cronFile, new HashMap());
logger.debug("writing "+schedulerXML.getAbsolutePath());
OutputStream fout = new FileOutputStream(schedulerXML,false);
OutputStreamWriter out = new OutputStreamWriter(fout, "UTF-8");
OutputFormat format = new OutputFormat(configurationDocument);
format.setEncoding("UTF-8");
format.setIndenting(true);
format.setIndent(2);
XMLSerializer serializer = new XMLSerializer(out, format);
serializer.serialize(configurationDocument);
out.close();
} catch (Exception e){
throw new Exception ("Error writing Job Scheduler configuration file: "+e,e);
}
}
/**
* Converts a crontab to a Scheduler XML as DOM document and
* provides easy access to the job elements by putting them to the vector
* jobs
* @param cronFile crontab file
* @param cron2jobMapping empty vector which will be filled with cron lines mapped to job DOM Elements
* @return DOM Document with scheduler configuration
* @throws Exception
*/
public Document cronFile2SchedulerXML(File cronFile, HashMap cron2jobMapping) throws Exception{
try{
HashSet jobNames = new HashSet();
if(reservedJobNames!=null) jobNames.addAll(reservedJobNames);
HashMap environmentVariables = new HashMap();
Document configurationDoc = docBuilder.newDocument();
Element spoolerElement = configurationDoc.createElement("spooler");
configurationDoc.appendChild(spoolerElement);
Element configElement = configurationDoc.createElement("config");
spoolerElement.appendChild(configElement);
Element jobsElement = configurationDoc.createElement("jobs");
configElement.appendChild(jobsElement);
BufferedReader in = new BufferedReader ( new FileReader (cronFile) );
String currentLine ="";
String lastComment = "";
String lastCommentJobName = "";
String lastCommentJobTitle = "";
String lastCommentJobTimeout = "";
while( (currentLine = in.readLine()) != null ) {
if (currentLine.trim().length()==0){
lastComment="";
continue;
}
if (skipLines!=null && skipLines.contains(currentLine)){
logger.debug6("Skipping line "+currentLine);
lastComment="";
lastCommentJobName ="";
lastCommentJobTitle = "";
lastCommentJobTimeout = "";
continue;
}
Matcher commentMatcher = cronRegExCommentPattern.matcher(currentLine);
if (commentMatcher.matches()){
Matcher jobNameMatcher = cronRegExJobNamePattern.matcher(commentMatcher.group(1));
Matcher jobTitleMatcher = cronRegExJobTitlePattern.matcher(commentMatcher.group(1));
Matcher jobTimeoutMatcher = cronRegExJobTimeoutPattern.matcher(commentMatcher.group(1));
if (jobNameMatcher.matches()){
lastCommentJobName = jobNameMatcher.group(1);
logger.debug5("Found job name in comment: "+lastCommentJobName);
continue;
}
if (jobTitleMatcher.matches()){
lastCommentJobTitle = jobTitleMatcher.group(1);
logger.debug5("Found job title in comment: "+lastCommentJobTitle);
continue;
}
if (jobTimeoutMatcher.matches()){
lastCommentJobTimeout = jobTimeoutMatcher.group(1);
logger.debug5("Found job timeout in comment: "+lastCommentJobTimeout);
continue;
}
if (lastComment.length()>0) lastComment+="\n";
lastComment+=commentMatcher.group(1);
continue;
}
Matcher environmentMatcher = cronRegExEnvironmentPattern.matcher(currentLine);
if(environmentMatcher.matches()){
String envName = environmentMatcher.group(1);
String envValue = environmentMatcher.group(2);
logger.debug3("Found environment variable ["+envName+"]: "+envValue);
if (envValue.startsWith("\"") && envValue.endsWith("\"")){
envValue=envValue.substring(1, envValue.length()-1);
}
environmentVariables.put(envName, envValue);
lastComment = "";
}
Matcher cronMatcher = currentCronPattern.matcher(currentLine);
Matcher cronAliasMatcher = cronRegExAliasPattern.matcher(currentLine);
if (cronMatcher.matches() || cronAliasMatcher.matches()){
Document jobDocument = line2Job(currentLine, environmentVariables);
Element jobElement = jobDocument.getDocumentElement();
//NodeList jobChildren = jobElement.getChildNodes();
//Element paramsElement = null;
/*for (int i=0; i<jobChildren.getLength() && paramsElement==null;i++){
Node currentNode = jobChildren.item(i);
if (currentNode.getNodeName().equals("params")) paramsElement = (Element) currentNode;
}*/
boolean jobNameChanged = false;
if (lastCommentJobName.length()>0) {
jobElement.setAttribute("name", lastCommentJobName.replaceAll("/", "_"));
lastCommentJobName = "";
}
if (lastCommentJobTitle.length()>0){
jobElement.setAttribute("title", lastCommentJobTitle);
lastCommentJobTitle = "";
}
if (lastCommentJobTimeout.length()>0){
jobElement.setAttribute("timeout", lastCommentJobTimeout);
lastCommentJobTimeout = "";
}
String jobName = jobElement.getAttribute("name");
while(jobNames.contains(jobName)){
logger.debug3("Configuration already contains a job named \""+jobName+"\". Looking for new name.");
jobName = incrementJobName(jobName);
jobNameChanged=true;
}
if (jobNameChanged){
logger.debug3("Setting new job name \""+jobName+"\"");
jobElement.setAttribute("name", jobName);
}
jobNames.add(jobName);
Node importedJob = configurationDoc.importNode(jobElement, true);
cron2jobMapping.put(currentLine, jobElement);
if (lastComment.length()>0){
Comment jobComment = configurationDoc.createComment(lastComment);
jobsElement.appendChild(jobComment);
}
jobsElement.appendChild(importedJob);
lastComment="";
}
}
in.close();
return configurationDoc;
}catch (Exception e){
throw new Exception("Error converting file "+cronFile.getAbsolutePath()+" to Job Scheduler XML: "+e,e);
}
}
private String incrementJobName(String jobName) {
Matcher jobNameMatcher = jobNameRegExPattern.matcher(jobName);
if (jobNameMatcher.matches()){
String baseName = jobNameMatcher.group(1);
String counter = jobNameMatcher.group(2);
int iCounter = Integer.parseInt(counter);
iCounter++;
jobName = baseName + "_" + iCounter;
}else{
jobName = jobName+"_1";
}
return jobName;
}
public Document line2Job(String cronline) throws Exception{
return line2Job(cronline, new HashMap());
}
public Document line2Job(String cronline, HashMap environmentVariables) throws Exception{
try{
logger.info("processing line: "+cronline);
Document jobDoc = docBuilder.newDocument();
Element jobElement = jobDoc.createElement("job");
Matcher cronRegExAliasMatcher = cronRegExAliasPattern.matcher(cronline);
if (cronRegExAliasMatcher.matches()){
logger.debug3("Current line matches pattern "+cronRegExAlias);
cronline = convertAlias(cronRegExAliasMatcher);
}
Matcher cronRegExMatcher = cronRegExPattern.matcher(cronline);
int commandIndex = 6;
if (isSystemCronTab()) {
commandIndex = 7;
cronRegExMatcher = cronRegExSystemPattern.matcher(cronline);
}
if (!cronRegExMatcher.matches()){
throw new Exception("Faile to parse cron line \""+cronline+"\"");
}
String jobname = getJobName(cronRegExMatcher.group(commandIndex));
jobElement.setAttribute("name", jobname);
jobElement.setAttribute("title", "Cron Job "+cronRegExMatcher.group(commandIndex).trim());
//jobElement.setAttribute("replace", "yes");
if(timeout!=null && !timeout.equals("0")){
jobElement.setAttribute("timeout", timeout);
}
String schedulerUser = "";
String command = cronRegExMatcher.group(commandIndex);
if (isSystemCronTab()){
schedulerUser = cronRegExMatcher.group(6);
command = (changeUserCommand+" "+command).trim();
}
/*logger.debug9("Creating params element");
Element paramsElement = jobDoc.createElement("params");
logger.debug9("Creating param element (command)");
Element paramCommandElement = jobDoc.createElement("param");
paramCommandElement.setAttribute("name", "command");
paramCommandElement.setAttribute("value", command);
paramsElement.appendChild(paramCommandElement);
jobElement.appendChild(paramsElement);
*/
logger.debug9("Creating script element");
Element scriptElement = jobDoc.createElement("script");
//scriptElement.setAttribute("language", "java");
//scriptElement.setAttribute("java_class", "sos.scheduler.managed.JobSchedulerManagedExecutableJob");
scriptElement.setAttribute("language", "shell");
String script = "\n";
if (schedulerUser.length()>0) script += "export SCHEDULER_CRONTAB_USER="+schedulerUser+"\n";
Iterator envIter = environmentVariables.keySet().iterator();
// set environment variables on job
while(envIter.hasNext()){
String envName = envIter.next().toString();
String envValue = environmentVariables.get(envName).toString();
script += "export "+envName+"="+envValue+"\n";
}
script+=command;
Node scriptData = jobDoc.createCDATASection(script);
scriptElement.appendChild(scriptData);
jobElement.appendChild(scriptElement);
Element runTimeElement = jobDoc.createElement("run_time");
createRunTime(cronRegExMatcher, runTimeElement);
if (usedNewRunTime && oldRunTime){
// workaround while <month> Element is not available
// can later be deleted (keep only else branch)
usedNewRunTime = false;
Document runTimeDocument = docBuilder.newDocument();
runTimeDocument.appendChild(runTimeDocument.importNode(runTimeElement, true));
StringWriter out = new StringWriter();
OutputFormat format = new OutputFormat(runTimeDocument);
format.setIndenting(true);
format.setIndent(2);
format.setOmitXMLDeclaration(true);
XMLSerializer serializer = new XMLSerializer(out, format);
serializer.serialize(runTimeDocument);
Comment runTimeComment = jobDoc.createComment("This run_time is currently not supported:\n"+out.toString());
jobElement.appendChild(runTimeComment);
} else{
jobElement.appendChild(runTimeElement);
}
jobDoc.appendChild(jobElement);
return jobDoc;
} catch (Exception e){
throw new Exception ("Error occured creating job from cron line: "+e);
}
}
private void createRunTime(Matcher cronRegExMatcher, Element runTimeElement) throws Exception{
try {
String minutes = cronRegExMatcher.group(1);
String hours = cronRegExMatcher.group(2);
String days = cronRegExMatcher.group(3);
String months = cronRegExMatcher.group(4);
String weekdays = cronRegExMatcher.group(5);
if (minutes.equals("@reboot")){
runTimeElement.setAttribute("once", "yes");
return;
}
Vector childElements = new Vector();
Element periodElement = runTimeElement.getOwnerDocument().createElement("period");
logger.debug6("processing hours ["+hours+"] and minutes ["+minutes+"]");
if (minutes.startsWith("*")){
if (minutes.equalsIgnoreCase("*")){
// every minute
periodElement.setAttribute("repeat", "60");
}
else{ // repeat interval is given
String repeat = minutes.substring(2);
repeat = formatTwoDigits(repeat);
periodElement.setAttribute("repeat", "00:"+repeat);
}
if (hours.startsWith("*")){
if (!hours.equalsIgnoreCase("*")){
// repeat interval is given for hours and minutes. Doesn't make sense.
// e.g. */2 */3 every 3 hours repeat every 2 minutes
throw new Exception("Combination of minutes and hours not supported: "+minutes+" "+hours);
}
// every hour: keep interval from minutes
childElements.add(periodElement);
} else{
logger.debug9("Found specific hours, creating periods with begin and end.");
String[] hourArray = hours.split(",");
for (int i = 0; i < hourArray.length; i++) {
String currentHour = hourArray[i];
if (currentHour.indexOf("/")!=-1){
String[] additionalHours = getArrayFromColumn(currentHour);
hourArray = combineArrays(hourArray, additionalHours);
continue;
}
String[] currentHourArray = currentHour.split("-");
Element currentPeriodElement = (Element) periodElement.cloneNode(true);
String beginHour = currentHourArray[0];
int iEndHour = (Integer.parseInt(beginHour)+1) % 24;
// workaround, bis endhour am n�chsten Tag erlaubt
if (iEndHour==0) iEndHour=24;
String endHour = ""+iEndHour;
if (currentHourArray.length>1) endHour= currentHourArray[1];
beginHour = formatTwoDigits(beginHour);
endHour = formatTwoDigits(endHour);
currentPeriodElement.setAttribute("begin", beginHour+":00");
currentPeriodElement.setAttribute("end", endHour+":00");
childElements.add(currentPeriodElement);
}
}
} // end if minutes.startsWith("*")
else{ // one or more minutes are fixed
String[] minutesArray = getArrayFromColumn(minutes);
for (int i = 0; i < minutesArray.length; i++) {
Element currentPeriodElement = (Element) periodElement.cloneNode(true);
String currentMinute = minutesArray[i];
currentMinute=formatTwoDigits(currentMinute);
if (hours.startsWith("*")){
currentPeriodElement.setAttribute("absolute_repeat", "01:00");
usedNewRunTime = true;
if (!hours.equalsIgnoreCase("*")){// repeat interval is given for hours
String repeat = hours.substring(2);
repeat=formatTwoDigits(repeat);
currentPeriodElement.setAttribute("absolute_repeat", repeat+":00");
}
currentPeriodElement.setAttribute("begin", "00:"+currentMinute);
childElements.add(currentPeriodElement);
} else{ //fixed hour(s) is set
String[] hourArray = hours.split(",");
for (int j = 0; j < hourArray.length; j++) {
currentPeriodElement = (Element) periodElement.cloneNode(true);
String currentHour = hourArray[j];
if (currentHour.indexOf("-")==-1){
// fixed hour and fixed minute --> create single_start
currentHour = formatTwoDigits(currentHour);
currentPeriodElement.setAttribute("single_start", currentHour+":"+currentMinute);
}else{
// range of hours is set, create begin and end attributes
String[] currentHourArray = currentHour.split("[-/]");
int beginHour = Integer.parseInt(currentHourArray[0]);
int endHour = Integer.parseInt(currentHourArray[1]);
int beginMinute = Integer.parseInt(currentMinute);
int endMinute = beginMinute+1;
// workaround, bis endhour am n�chsten Tag erlaubt
endMinute = beginMinute;
if (endMinute==60){
endMinute = 0;
endHour = (endHour+1);
}
endHour = endHour%24;
// workaround, bis endhour am n�chsten Tag erlaubt
if (endHour==0) endHour=24;
String stepSize = "1";
if (currentHourArray.length==3){
stepSize=formatTwoDigits(currentHourArray[2]);
}
currentPeriodElement.setAttribute("absolute_repeat", stepSize+":00");
usedNewRunTime = true;
currentPeriodElement.setAttribute("begin", formatTwoDigits(beginHour)+":"+formatTwoDigits(beginMinute));
currentPeriodElement.setAttribute("end", formatTwoDigits(endHour)+":"+formatTwoDigits(endMinute));
}
childElements.add(currentPeriodElement);
}
}
}
}
logger.debug6("processing days ["+days+"]");
boolean monthDaysSet = false;
if (days.startsWith("*")){
if (days.equals("*")){
// every day - do nothing, just keep periods
}else{
// repeat interval is given for days
// this is not possible in the Job Scheduler but can be poorly emulated
Element monthDaysElement = runTimeElement.getOwnerDocument().createElement("monthdays");
String repeat = days.substring(2);
int iRepeat = Integer.parseInt(repeat);
// use only 30 days
for(int i=1; i<=30; i=i+iRepeat){
String day = ""+i;
addDay(day,monthDaysElement,childElements);
}
childElements.clear();
childElements.add(monthDaysElement);
monthDaysSet = true;
}
} else {
Element monthDaysElement = runTimeElement.getOwnerDocument().createElement("monthdays");
String [] daysArray = getArrayFromColumn(days);
for (int i = 0; i < daysArray.length; i++) {
String day = daysArray[i];
addDay(day,monthDaysElement,childElements);
}
childElements.clear();
childElements.add(monthDaysElement);
monthDaysSet = true;
}
if (!weekdays.equals("*") && monthDaysSet){
logger.info("Weekdays will not be processed as days are already set in current line.");
}else{
logger.debug6("processing weekdays ["+weekdays+"]");
weekdays = replaceDayNames(weekdays);
if(weekdays.startsWith("*/")) throw new Exception("Repeat intervals for the weekdays column ["+weekdays+"] are not supported. Please use the days column.");
if(weekdays.equals("*")){
// all weekdays, do nothing
}else{
Element weekDaysElement = runTimeElement.getOwnerDocument().createElement("weekdays");
String [] daysArray = getArrayFromColumn(weekdays);
for (int i = 0; i < daysArray.length; i++) {
String day = daysArray[i];
addDay(day,weekDaysElement,childElements);
}
childElements.clear();
childElements.add(weekDaysElement);
}
}
logger.debug6("processing months ["+months+"]");
if (months.startsWith("*")){
if (months.equals("*")){
// every month - do nothing
}else{
months = replaceMonthNames(months);
// repeat interval is given for months
// this is not possible in the Job Scheduler but can be poorly emulated
Vector newChildElements = new Vector();
String repeat = months.substring(2);
int iRepeat = Integer.parseInt(repeat);
for(int i=1; i<=12; i=i+iRepeat){
String month = ""+i;
Element monthElement = runTimeElement.getOwnerDocument().createElement("month");
usedNewRunTime = true;
monthElement.setAttribute("month", month);
Iterator iter = childElements.iterator();
while (iter.hasNext()){
Element child = (Element) iter.next();
monthElement.appendChild(child.cloneNode(true));
}
newChildElements.add(monthElement);
}
childElements = newChildElements;
}
}else{// list of months is given
Vector newChildElements = new Vector();
String[] monthArray = getArrayFromColumn(months);
for (int i = 0; i < monthArray.length; i++) {
String month = monthArray[i];
Element monthElement = runTimeElement.getOwnerDocument().createElement("month");
usedNewRunTime = true;
monthElement.setAttribute("month", month);
Iterator iter = childElements.iterator();
while (iter.hasNext()){
Element child = (Element) iter.next();
monthElement.appendChild(child.cloneNode(true));
}
newChildElements.add(monthElement);
}
childElements = newChildElements;
}
// add topmost child elements to run_time element
Iterator iter = childElements.iterator();
while (iter.hasNext()){
Element someElement = (Element) iter.next();
runTimeElement.appendChild(someElement);
}
} catch (Exception e) {
throw new Exception("Error creating run time: "+e,e);
}
}
private static String[] combineArrays(String[] hourArray, String[] additionalHours) {
String[] newArray = new String[hourArray.length+additionalHours.length];
for (int i = 0; i < hourArray.length; i++) {
newArray[i]=hourArray[i];
}
for (int i = 0; i < additionalHours.length; i++) {
newArray[i+hourArray.length] = additionalHours[i];
}
return newArray;
}
private void addDay(String day, Element parentDaysElement, Vector childElements) throws Exception{
logger.debug9("adding day: "+day);
Element dayElement = parentDaysElement.getOwnerDocument().createElement("day");
dayElement.setAttribute("day", day);
Iterator iter = childElements.iterator();
while (iter.hasNext()){
Element child = (Element) iter.next();
dayElement.appendChild(child.cloneNode(true));
}
parentDaysElement.appendChild(dayElement);
}
private String[] getArrayFromColumn(String column) {
String[] elements = column.split(",");
Vector result = new Vector();
for (int i = 0; i < elements.length; i++) {
String element = elements[i];
if (element.indexOf("-")==-1){
result.add(element);
} else{
String[] range = element.split("[-/]");
if(range.length<2 || range.length>3){
try{
logger.warn("unknown crontab synthax: "+element);
} catch (Exception e){}
} else{
int from = Integer.parseInt(range[0]);
int to = Integer.parseInt(range[1]);
int stepSize = 1;
// if e.g. 8-20/2
if (range.length==3) stepSize=Integer.parseInt(range[2]);
for (int j=from;j<=to;j=j+stepSize) result.add(""+j);
}
}
}
String[] dummy=new String[1];
return (String[]) result.toArray(dummy);
}
private static String replaceDayNames(String element) {
element = element.replaceAll("(?i)mon", "1");
element = element.replaceAll("(?i)tue", "2");
element = element.replaceAll("(?i)wed", "3");
element = element.replaceAll("(?i)thu", "4");
element = element.replaceAll("(?i)fri", "5");
element = element.replaceAll("(?i)sat", "6");
element = element.replaceAll("(?i)sun", "7");
return element;
}
private static String replaceMonthNames(String element) {
element = element.replaceAll("(?i)jan", "1");
element = element.replaceAll("(?i)feb", "2");
element = element.replaceAll("(?i)mar", "3");
element = element.replaceAll("(?i)apr", "4");
element = element.replaceAll("(?i)may", "5");
element = element.replaceAll("(?i)jun", "6");
element = element.replaceAll("(?i)jul", "7");
element = element.replaceAll("(?i)aug", "8");
element = element.replaceAll("(?i)sep", "9");
element = element.replaceAll("(?i)oct", "10");
element = element.replaceAll("(?i)nov", "11");
element = element.replaceAll("(?i)dec", "12");
return element;
}
private String getJobName(String command) {
Matcher commandMatcher = commandRegExPattern.matcher(command);
if (commandMatcher.find()){
command = commandMatcher.group();
} else {
int space = command.indexOf(" ");
if (space!=-1){
command = command.substring(0, space);
}
}
if (command.startsWith("/")) command=command.substring(1);
command = command.replaceAll("/", "_");
return command;
}
private String convertAlias(Matcher matcher) throws Exception {
logger.debug5("Converting alias...");
try{
String alias = matcher.group(1);
String rest = matcher.group(2);
String result = "";
if (alias.equals("@yearly") || alias.equals("@annually")) result = "0 0 1 1 * ";
if (alias.equals("@monthly")) result = "0 0 1 * * ";
if (alias.equals("@weekly")) result = "0 0 * * 0 ";
if (alias.equals("@daily") || alias.equals("@midnight")) result = "0 0 * * * ";
if (alias.equals("@hourly")) result = "0 * * * * ";
if (alias.equals("@reboot")) result = "@reboot @reboot @reboot @reboot @reboot";
result += rest;
logger.debug5("Alias converted to "+result);
return result;
} catch(Exception e){
throw new Exception ("Error converting alias: "+e);
}
}
private static String formatTwoDigits(String number){
if (number.length()==1) return "0"+number;
return number;
}
private static String formatTwoDigits(int number){
return formatTwoDigits(""+number);
}
/**
* This function does not anylyse the crontab, it only
* returns what type of crontab the converter is configured
* to parse
* @return the value for systemCronTab
*/
public boolean isSystemCronTab() {
return systemCronTab;
}
/**
* Sets if the current crontab is a system crontab
* which has one more column (user)
* @param systemCronTab true=current crontab is a system crontab
*/
public void setSystemCronTab(boolean systemCronTab) {
if (systemCronTab) {
currentCronPattern = cronRegExSystemPattern;
} else currentCronPattern = cronRegExPattern;
this.systemCronTab = systemCronTab;
}
/**
* @return the oldRunTime
*/
protected boolean isOldRunTime() {
return oldRunTime;
}
/**
* @param oldRunTime the oldRunTime to set
*/
protected void setOldRunTime(boolean oldRunTime) {
this.oldRunTime = oldRunTime;
}
/**
* @return the skipLines
*/
public HashSet getSkipLines() {
return skipLines;
}
/**
* The cron lines contained in this set will be skipped when
* converting a cron file
* @param skipLines
*/
public void setSkipLines(HashSet skipLines) {
this.skipLines = skipLines;
}
/**
* @return the reservedJobNames
*/
public HashSet getReservedJobNames() {
return reservedJobNames;
}
/**
* Sets reserved job names. Reserved job names will not be used as job names.
* @param reservedJobNames HashSet of reserved job names
*/
public void setReservedJobNames(HashSet reservedJobNames) {
this.reservedJobNames = reservedJobNames;
}
/**
* @return the docBuilder
*/
protected DocumentBuilder getDocBuilder() {
return docBuilder;
}
/**
* @return the changeUserCommand
*/
public String getChangeUserCommand() {
return changeUserCommand;
}
/**
* @param changeUserCommand the changeUserCommand to set
*/
public void setChangeUserCommand(String changeUserCommand) {
if (changeUserCommand.equalsIgnoreCase("su")) changeUserCommand = "su $SCHEDULER_CRONTAB_USER -c";
if (changeUserCommand.equalsIgnoreCase("sudo")) changeUserCommand = "sudo -u $SCHEDULER_CRONTAB_USER";
this.changeUserCommand = changeUserCommand;
}
/**
* @return the timeout
*/
public String getTimeout() {
return timeout;
}
/**
* @param timeout the timeout to set
*/
public void setTimeout(String timeout) {
this.timeout = timeout;
}
}