sos.scheduler.cron.CronConverter.java Source code

Java tutorial

Introduction

Here is the source code for sos.scheduler.cron.CronConverter.java

Source

/**
 * Copyright (C) 2014 BigLoupe http://bigloupe.github.io/SoS-JobScheduler/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */
/********************************************************* begin of preamble
**
** Copyright (C) 2003-2012 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.log4j.Logger;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Comment;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import sos.util.SOSDate;
import sos.util.SOSLogger;
import sos.util.SOSStandardLogger;

import com.sos.JSHelper.Exceptions.JobSchedulerException;
import com.sos.JSHelper.Logging.Log4JHelper;

/**
 * This Class converts a crontab file to a JobScheduler XML Configuration
 *
 * @author Andreas Liebert
 */
@SuppressWarnings("deprecation")
public class CronConverter {

    private static final String conTRUE = "true";

    private static final String conSystemCrontabName = "/etc/crontab";

    protected static Logger logger = Logger.getLogger(CronConverter.class);

    private static final String conStateERROR = "error";
    private static final String conStateSUCCESS = "success";
    private static final String conAttributeERROR_STATE = "error_state";
    private static final String conAttributeNEXT_STATE = "next_state";
    private static final String conTagJOB_CHAIN_NODE = "job_chain_node";
    private static final String conAttributeJOB_CHAIN = "job_chain";
    private static final String conAttributeID = "id";
    private static final String conAttributeSTATE = "state";
    private static final String conTagADD_ORDER = "add_order";
    private static final String conAttributeNAME = "name";
    private static final String conTagJOB = "job";
    private static final String conAttributeORDER = "order";
    private static final String conAttributeTITLE = "title";
    private final String conClassName = "CronConverter";
    private static final String conTagRUN_TIME = "run_time";
    private static final String conOptionCREATE_MOCK = "createMock";
    private static final String conOptionCREATE_JobChains = "createJobChains";
    private static final String conNewline = "\n";
    private static final String conAttributeLANGUAGE = "language";
    private static final String conTagSCRIPT = "script";
    private static final String conTagSPOOLER = "spooler";
    private static final String conOptionTIMEOUT = "timeout";
    private static final String conOptionCHANGEUSER = "changeuser";
    private static final String conOptionVERBOSE = "v";
    private static final String conOptionTARGET = "target";
    private static final String conOptionsSYSTAB = "systab";
    private static final String conOptionCRONTAB = "crontab";
    private final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
    private final 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 final SOSLogger               logger;

    private boolean systemCronTab = false;

    private boolean oldRunTime = false;
    private boolean usedNewRunTime = false;
    private final boolean flgCreateSubFolderStructure = true;
    private String changeUserCommand = "";
    private String strCronLine = "";

    /**
     * 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 final Pattern commandRegExPattern;

    /**
     * Regular expression
     * (.*)_(\d*)$
     * Matches incremented job names
     */
    private final static String jobNameRegEx = "(.*)_(\\d*)$";
    private final Pattern jobNameRegExPattern;
    private HashSet<String> skipLines = new HashSet<String>();
    private HashSet<String> reservedJobNames = new HashSet<String>();
    private String timeout = "600";
    protected Pattern currentCronPattern;
    protected String strBaseDirectory = "GlobalCollect";
    protected String strMockCommand = "ping -n 20 localhost";
    private String lastComment = "";

    public boolean isCreateAMock() {
        return flgCreateAMock;
    }

    public void setCreateAMock(final boolean createAMok) {
        flgCreateAMock = createAMok;
    }

    public boolean isCreateJobChainJobs() {
        return flgCreateJobChainJobs;
    }

    public void setCreateJobChainJobs(final boolean createJobChainJobs) {
        flgCreateJobChainJobs = createJobChainJobs;
    }

    private boolean flgCreateAMock = false;
    private boolean flgCreateJobChainJobs = false;

    /**
     * @param args
     */
    public static void main(final String[] args) {

        Logger logger = Logger.getLogger(CronConverter.class);
        @SuppressWarnings("unused")
        Log4JHelper objLogger = null;

        objLogger = new Log4JHelper(null);

        logger = Logger.getRootLogger();
        logger.info("SOS CronConverter - Main"); //$NON-NLS-1$

        /*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();
            OptionBuilder.withArgName("0|1");
            OptionBuilder.hasArg();
            OptionBuilder.withDescription("set to 1 if source is the system crontab (with user field)");

            Option optSysTab = OptionBuilder.create(conOptionsSYSTAB);

            OptionBuilder.withArgName("file");
            OptionBuilder.hasArgs();
            OptionBuilder.isRequired();
            OptionBuilder.withDescription("crontab file");

            Option optSourceFile = OptionBuilder.create(conOptionCRONTAB);
            OptionBuilder.withArgName("file");
            OptionBuilder.hasArgs();
            OptionBuilder.isRequired();
            OptionBuilder.withDescription("xml configuration file");

            Option optTargetFile = OptionBuilder.create(conOptionTARGET);
            OptionBuilder.withArgName("level");
            OptionBuilder.hasArg();
            OptionBuilder.withType(new Integer(0));
            OptionBuilder.withDescription("loglevel [0=info] [1=debug1]...[9=debug]");

            Option optLogLevel = OptionBuilder.create(conOptionVERBOSE);

            OptionBuilder.withArgName("command");
            OptionBuilder.hasArgs();
            OptionBuilder.withDescription(
                    "change user command for -systab=1. 'su' or 'sudo' or define your own command using $SCHEDULER_CRONTAB_USER.");

            Option optChangeUser = OptionBuilder.create(conOptionCHANGEUSER);

            OptionBuilder.withArgName("seconds");
            OptionBuilder.hasArg();
            OptionBuilder.withDescription("job timeout (0 for unlimited");
            @SuppressWarnings("unused")
            Option optTimeOut = OptionBuilder.create(conOptionTIMEOUT);
            @SuppressWarnings("unused")
            Option optOldRunTime = new Option("oldRunTime", "");

            OptionBuilder.withArgName("true|false");
            OptionBuilder.hasArg();
            OptionBuilder.withDescription("set to true if script has to be mok'ed");
            Option optCreateMok = OptionBuilder.create(conOptionCREATE_MOCK);

            OptionBuilder.withArgName("true|false");
            OptionBuilder.hasArg();
            OptionBuilder.withDescription("set to true if you want to create jobChains and jobs");
            Option optCreateJobChains = OptionBuilder.create(conOptionCREATE_JobChains);

            options.addOption(optSysTab);
            options.addOption(optSourceFile);
            options.addOption(optTargetFile);
            options.addOption(optLogLevel);
            options.addOption(optChangeUser);
            options.addOption(optCreateMok);
            options.addOption(optCreateJobChains);

            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(conOptionCRONTAB));
            if (sourceFile.equalsIgnoreCase(conSystemCrontabName))
                sysTab = true;
            targetFile = getWholeArgument(line.getOptionValues(conOptionTARGET));

            String ll = line.getOptionValue(conOptionVERBOSE, "" + 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(conOptionCHANGEUSER)) {
                changeUser = getWholeArgument(line.getOptionValues(conOptionCHANGEUSER));
            }

            jobTimeout = line.getOptionValue(conOptionTIMEOUT);
            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);
            if (line.hasOption(conOptionCREATE_MOCK)) { // JITL-28
                cc.setCreateAMock(line.getOptionValue(conOptionCREATE_MOCK).equalsIgnoreCase(conTRUE));
            }
            if (line.hasOption(conOptionCREATE_JobChains)) { // JITL-28
                cc.setCreateJobChainJobs(line.getOptionValue(conOptionCREATE_JobChains).equalsIgnoreCase(conTRUE));
            }

            cc.setSystemCronTab(sysTab);
            cc.oldRunTime = useOldRunTime;
            cc.cronFile2SchedulerXMLFile(source, target);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private static String getWholeArgument(final String[] optionValues) {
        String value = "";
        for (int i = 0; i < optionValues.length; i++) {
            value += optionValues[i];
            if (i + 1 < optionValues.length)
                value += " ";
        }
        return value;
    }

    @SuppressWarnings("unused")
    private static void test() throws Exception {
        SOSLogger log = null; //new SOSStandardLogger(SOSLogger.debug);
        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.createJobElement("5 22  *  *  *   test -x /usr/sbin/texpire && /usr/sbin/texpire");
        //Document job = cc.createJobElement("5 22  *  *  *   test -x bla");
        //Document job = cc.createJobElement("5 22  3  *  *   test -x bla");
        //Document job = cc.createJobElement("5 22  3,5,8-12  *  *   test -x bla");
        //Document job = cc.createJobElement("1  0  *  *  1   /usr/bin/xsoftwaresamba.sh");

        //Ein Bindestrich  -  gibt einen Zeitraum an. Jeden Tag von 12-24 Uhr (jede Stunde) ...:
        //Document job = cc.createJobElement("0  12-24  *  *  *   /usr/bin/xsoftwaresamba.sh");

        // Ein Schrgstrich  /  teilt einen Zeitraum ein. Zwischen 6 und 23 Uhr alle 15 Minuten ...:
        //Document job = cc.createJobElement("*/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.createJobElement("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.createJobElement("*/15 17-23 * * 2,3,4,5,6 /sbin/phplist -pprocessqueue");
        //Document job = cc.createJobElement("*/15 17-23 * * Tue-sat /sbin/phplist -pprocessqueue");

        //Document job = cc.createJobElement("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.createJobElement("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.createJobElement("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(final 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(final File cronFile, final File schedulerXML) throws Exception {
        try {
            Document configurationDocument = cronFile2SchedulerXML(cronFile, new HashMap<String, Element>());
            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.setNamespaces(true);
            serializer.serialize(configurationDocument);
            out.close();
        } catch (Exception e) {
            throw new JobSchedulerException("Error writing JobScheduler 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(final File cronFile, final HashMap<String, Element> cron2jobMapping)
            throws Exception {
        try {
            HashSet<String> jobNames = new HashSet<String>();
            if (reservedJobNames != null) {
                jobNames.addAll(reservedJobNames);
            }
            HashMap<String, String> environmentVariables = new HashMap<String, String>();
            Document configurationDoc = docBuilder.newDocument();
            Element spoolerElement = configurationDoc.createElement(conTagSPOOLER);
            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));
            Vector<Element> vecJobs = new Vector<Element>();
            Vector<String> vecCronRecords = new Vector<String>();
            lastComment = "";
            String lastCommentJobName = "";
            String lastCommentJobTitle = "";
            String lastCommentJobTimeout = "";

            while ((strCronLine = in.readLine()) != null) {
                if (strCronLine.trim().length() == 0) {
                    lastComment = "";
                    continue;
                }
                if (skipLines != null && skipLines.contains(strCronLine)) {
                    logger.debug("Skipping line " + strCronLine);
                    lastComment = "";
                    lastCommentJobName = "";
                    lastCommentJobTitle = "";
                    lastCommentJobTimeout = "";
                    continue;
                }

                Matcher commentMatcher = cronRegExCommentPattern.matcher(strCronLine);
                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.debug("Found job name in comment: " + lastCommentJobName);
                        continue;
                    }
                    if (jobTitleMatcher.matches()) {
                        lastCommentJobTitle = jobTitleMatcher.group(1);
                        logger.debug("Found job title in comment: " + lastCommentJobTitle);
                        continue;
                    }
                    if (jobTimeoutMatcher.matches()) {
                        lastCommentJobTimeout = jobTimeoutMatcher.group(1);
                        logger.debug("Found job timeout in comment: " + lastCommentJobTimeout);
                        continue;
                    }
                    if (lastComment.length() > 0) {
                        lastComment += conNewline;
                    }
                    lastComment += commentMatcher.group(1);
                    continue;
                }

                Matcher environmentMatcher = cronRegExEnvironmentPattern.matcher(strCronLine);
                if (environmentMatcher.matches()) {
                    String envName = environmentMatcher.group(1);
                    String envValue = environmentMatcher.group(2);
                    logger.debug("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(strCronLine);
                Matcher cronAliasMatcher = cronRegExAliasPattern.matcher(strCronLine);
                if (cronMatcher.matches() || cronAliasMatcher.matches()) {
                    vecCronRecords.add(strCronLine);
                    Document jobDocument = createJobElement(strCronLine, 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(conAttributeNAME, lastCommentJobName.replaceAll("/", "_"));
                        lastCommentJobName = "";
                    }
                    if (lastCommentJobTitle.length() > 0) {
                        jobElement.setAttribute(conAttributeTITLE, lastCommentJobTitle);
                        lastCommentJobTitle = "";
                    }
                    if (lastCommentJobTimeout.length() > 0) {
                        jobElement.setAttribute(conOptionTIMEOUT, lastCommentJobTimeout);
                        lastCommentJobTimeout = "";
                    }
                    String jobName = jobElement.getAttribute(conAttributeNAME);
                    while (jobNames.contains(jobName)) {
                        //                  logger.debug("Configuration already contains a job named \"" + jobName + "\". Looking for new name.");
                        jobName = incrementJobName(jobName);
                        jobNameChanged = true;
                    }
                    if (jobNameChanged) {
                        logger.debug("Setting new job name \"" + jobName + "\"");
                        jobElement.setAttribute(conAttributeNAME, jobName);
                    }
                    jobNames.add(jobName);
                    Node importedJob = configurationDoc.importNode(jobElement, true);
                    cron2jobMapping.put(strCronLine, jobElement);

                    if (lastComment.length() > 0) {
                        // change "--" to "__" due to problems with xml-comment parser
                        lastComment = lastComment.replaceAll("--", "__");
                        Comment jobComment = configurationDoc.createComment(lastComment);
                        jobsElement.appendChild(jobComment);
                    }
                    jobsElement.appendChild(importedJob);
                    lastComment = "";
                    vecJobs.add(jobElement);
                }
            }
            if (isCreateJobChainJobs()) {
                Element jobChainsElement = configurationDoc.createElement("job_chains");
                configElement.appendChild(jobChainsElement);

                int i = 0;
                for (Element objJobElement : vecJobs) {
                    strCronLine = vecCronRecords.get(i++);
                    Document objJCD = createJobChainElement(objJobElement);
                    Element objJC = objJCD.getDocumentElement();
                    jobChainsElement.appendChild(configurationDoc.importNode(objJC, true));
                }

                Element commandsElement = configurationDoc.createElement("commands");
                configElement.appendChild(commandsElement);

                i = 0;
                for (Element objJobElement : vecJobs) {
                    strCronLine = vecCronRecords.get(i++);
                    Document objJCD = createOrderElement(objJobElement);
                    Element objJC = objJCD.getDocumentElement();
                    commandsElement.appendChild(configurationDoc.importNode(objJC, true));
                }

            }
            in.close();
            return configurationDoc;
        } catch (Exception e) {
            throw new JobSchedulerException(
                    "Error converting file " + cronFile.getAbsolutePath() + " to JobScheduler 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 createJobElement(final String cronline) throws Exception {
        return createJobElement(cronline, new HashMap<String, String>());
    }

    public Document createJobChainElement(final Element pobjJobElement) throws DOMException, Exception {
        Document jobchain = docBuilder.newDocument();
        Element jobChainElement = jobchain.createElement(conAttributeJOB_CHAIN);
        jobChainElement.setAttribute(conAttributeNAME, pobjJobElement.getAttribute(conAttributeNAME));
        jobChainElement.appendChild(createExtension(jobchain));
        Element jobChainNode = jobchain.createElement(conTagJOB_CHAIN_NODE);
        jobChainNode.setAttribute(conAttributeSTATE, "100");
        jobChainNode.setAttribute(conTagJOB, getNameWithoutPath(pobjJobElement.getAttribute(conAttributeNAME)));
        jobChainNode.setAttribute(conAttributeNEXT_STATE, conStateSUCCESS);
        jobChainNode.setAttribute(conAttributeERROR_STATE, conStateERROR);
        jobChainElement.appendChild(jobChainNode);

        jobChainNode = jobchain.createElement(conTagJOB_CHAIN_NODE);
        jobChainNode.setAttribute(conAttributeSTATE, conStateSUCCESS);
        jobChainElement.appendChild(jobChainNode);

        jobChainNode = jobchain.createElement(conTagJOB_CHAIN_NODE);
        jobChainNode.setAttribute(conAttributeSTATE, conStateERROR);
        jobChainElement.appendChild(jobChainNode);

        jobchain.appendChild(jobChainElement);
        return jobchain;

    }

    private Element createExtension(final Document pobjDoc) throws DOMException, Exception {
        Element objExtensions = pobjDoc.createElement("extensions");
        Element objExtension = pobjDoc.createElementNS("www.sos-berlin.com/schema/joe", "extension");
        Element objGenerator = pobjDoc.createElement("generator");
        objExtension.appendChild(objGenerator);
        objGenerator.setAttribute("name", "CronConverter");
        objGenerator.setAttribute("date", SOSDate.getCurrentTimeAsString());
        objGenerator.setAttribute("vendor", "www.sos-berlin.com");

        Element objComments = pobjDoc.createElement("comment");
        Node objCommentNode = pobjDoc.createCDATASection(lastComment);
        objComments.appendChild(objCommentNode);
        objExtension.appendChild(objComments);

        Element objDocu = pobjDoc.createElement("docu");
        Node objDocuNode = pobjDoc.createCDATASection(strCronLine);
        objDocu.appendChild(objDocuNode);
        objGenerator.appendChild(objDocu);
        objExtensions.appendChild(objExtension);

        return objExtensions;
    }

    //   private Node createExtension (Element pobjParentElement) {
    //      Document
    //      Element objElement = pobjParentElement.createEle
    //   }

    public Document createOrderElement(final Element pobjJobElement) throws Exception {
        Document objOrderDocument = docBuilder.newDocument();
        Element addOrderElement = objOrderDocument.createElement(conTagADD_ORDER);
        String strOrderID = pobjJobElement.getAttribute(conAttributeNAME);
        addOrderElement.setAttribute(conAttributeID, getNameWithoutPath(strOrderID));
        addOrderElement.setAttribute(conAttributeTITLE, pobjJobElement.getAttribute(conAttributeNAME));
        addOrderElement.setAttribute(conAttributeJOB_CHAIN, pobjJobElement.getAttribute(conAttributeNAME));

        addOrderElement.appendChild(createExtension(objOrderDocument));

        Element runTimeElement = objOrderDocument.createElement(conTagRUN_TIME);

        logger.debug(strCronLine);
        cronRegExPattern = Pattern.compile(cronRegEx);
        Matcher cronRegExMatcher = cronRegExPattern.matcher(strCronLine);
        createRunTime(cronRegExMatcher, runTimeElement);

        addOrderElement.appendChild(runTimeElement);

        objOrderDocument.appendChild(addOrderElement);
        return objOrderDocument;
    }

    private String getNameWithoutPath(final String pstrName) {
        String strRet = pstrName;
        int i = pstrName.lastIndexOf("/");

        // Order id must not contain the path-name
        if (i != -1) {
            strRet = pstrName.substring(i + 1);
        }

        return strRet;
    }

    public Document createJobElement(String cronline, final HashMap<String, String> environmentVariables)
            throws Exception {
        try {
            logger.info("processing line: " + cronline);
            Document eleJob = docBuilder.newDocument();
            Element jobElement = eleJob.createElement(conTagJOB);

            Matcher cronRegExAliasMatcher = cronRegExAliasPattern.matcher(cronline);
            if (cronRegExAliasMatcher.matches()) {
                logger.debug("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 JobSchedulerException("Fail to parse cron line \"" + cronline + "\"");
            }
            String jobname = getJobName(cronRegExMatcher.group(commandIndex));
            jobElement.setAttribute(conAttributeNAME, jobname);
            if (isCreateJobChainJobs()) {
                jobElement.setAttribute(conAttributeORDER, "yes");
            } else {
                jobElement.setAttribute(conAttributeORDER, "no");
            }
            jobElement.setAttribute(conAttributeTITLE, "Cron Job " + cronRegExMatcher.group(commandIndex).trim());
            //jobElement.setAttribute("replace", "yes");
            if (timeout != null && !timeout.equals("0")) {
                jobElement.setAttribute(conOptionTIMEOUT, timeout);
            }

            String schedulerUser = "";
            String command = cronRegExMatcher.group(commandIndex);
            if (isSystemCronTab()) {
                schedulerUser = cronRegExMatcher.group(6);
                command = (changeUserCommand + " " + command).trim();
            }

            jobElement.appendChild(createExtension(eleJob));

            if (isCreateJobChainJobs() == false) {
                logger.debug("Creating params element");
                Element paramsElement = eleJob.createElement("params");
                logger.debug("Creating param element (command)");
                Element paramCommandElement = eleJob.createElement("param");
                paramCommandElement.setAttribute("name", "command");
                paramCommandElement.setAttribute("value", command);
                paramsElement.appendChild(paramCommandElement);
                jobElement.appendChild(paramsElement);
            }

            logger.debug("Creating script element");
            Element scriptElement = eleJob.createElement(conTagSCRIPT);
            scriptElement.setAttribute(conAttributeLANGUAGE, "shell");
            String script = conNewline;
            if (schedulerUser.length() > 0) {
                script += "export SCHEDULER_CRONTAB_USER=" + schedulerUser + conNewline;
            }
            Iterator<String> envIter = environmentVariables.keySet().iterator();
            // set environment variables on job
            while (envIter.hasNext()) {
                String envName = envIter.next().toString();
                String envValue = environmentVariables.get(envName).toString();
                script += envName + "=" + envValue + conNewline;
                script += "export " + envName;
            }

            script += "echo created by " + conClassName + ", at " + SOSDate.getCurrentTimeAsString() + conNewline;
            if (isCreateAMock() == true) {
                script += "echo mock-mode: " + command + conNewline;
                if (strMockCommand.length() > 0) {
                    script += strMockCommand + conNewline;
                }
                script += "exit 0" + conNewline;
            } else {
                script += command;
            }

            Node scriptData = eleJob.createCDATASection(script);
            scriptElement.appendChild(scriptData);
            jobElement.appendChild(scriptElement);

            if (isCreateJobChainJobs() == false) {
                Element runTimeElement = eleJob.createElement(conTagRUN_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 = eleJob
                            .createComment("This run_time is currently not supported:\n" + out.toString());
                    jobElement.appendChild(runTimeComment);
                } else {
                    jobElement.appendChild(runTimeElement);
                }
            }

            eleJob.appendChild(jobElement);
            return eleJob;
        } catch (Exception e) {
            throw new JobSchedulerException("Error occured creating job from cron line: " + cronline, e);
        }
    }

    private void createRunTime(final Matcher pcronRegExMatcher, final Element runTimeElement) throws Exception {
        try {
            if (!pcronRegExMatcher.matches()) {
                throw new JobSchedulerException("Fail to parse cron line \"" + strCronLine + "\", regexp is "
                        + pcronRegExMatcher.toString());
            }

            String minutes = pcronRegExMatcher.group(1);
            String hours = pcronRegExMatcher.group(2);
            String days = pcronRegExMatcher.group(3);
            String months = pcronRegExMatcher.group(4);
            String weekdays = pcronRegExMatcher.group(5);

            if (minutes.equals("@reboot")) {
                runTimeElement.setAttribute("once", "yes");
                return;
            }
            Vector<Element> childElements = new Vector<Element>();
            Element periodElement = runTimeElement.getOwnerDocument().createElement("period");

            logger.debug("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 JobSchedulerException(
                                "Combination of minutes and hours not supported: " + minutes + " " + hours);
                    }
                    // every hour: keep interval from minutes
                    childElements.add(periodElement);
                } else {
                    logger.debug("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 nchsten 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 (String element : minutesArray) {
                    Element currentPeriodElement = (Element) periodElement.cloneNode(true);
                    String currentMinute = element;

                    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 (String element2 : hourArray) {
                            currentPeriodElement = (Element) periodElement.cloneNode(true);
                            String currentHour = element2;
                            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 nchsten Tag erlaubt
                                endMinute = beginMinute;
                                if (endMinute == 60) {
                                    endMinute = 0;
                                    endHour = endHour + 1;
                                }
                                endHour = endHour % 24;
                                // workaround, bis endhour am nchsten 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.debug("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 JobScheduler 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 (String day : daysArray) {
                    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.debug("processing weekdays [" + weekdays + "]");
                weekdays = replaceDayNames(weekdays);
                if (weekdays.startsWith("*/"))
                    throw new JobSchedulerException("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 (String day : daysArray) {
                        addDay(day, weekDaysElement, childElements);
                    }
                    childElements.clear();
                    childElements.add(weekDaysElement);
                }
            }

            logger.debug("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 JobScheduler but can be poorly emulated
                    Vector<Element> newChildElements = new Vector<Element>();
                    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<Element> iter = childElements.iterator();
                        while (iter.hasNext()) {
                            Element child = iter.next();
                            monthElement.appendChild(child.cloneNode(true));
                        }
                        newChildElements.add(monthElement);
                    }
                    childElements = newChildElements;
                }
            } else {// list of months is given
                Vector<Element> newChildElements = new Vector<Element>();
                String[] monthArray = getArrayFromColumn(months);
                for (String month : monthArray) {
                    Element monthElement = runTimeElement.getOwnerDocument().createElement("month");
                    usedNewRunTime = true;
                    monthElement.setAttribute("month", month);
                    Iterator<Element> iter = childElements.iterator();
                    while (iter.hasNext()) {
                        Element child = iter.next();
                        monthElement.appendChild(child.cloneNode(true));
                    }
                    newChildElements.add(monthElement);
                }
                childElements = newChildElements;
            }

            // add topmost child elements to run_time element
            Iterator<Element> iter = childElements.iterator();
            while (iter.hasNext()) {
                Element someElement = iter.next();
                runTimeElement.appendChild(someElement);
            }
        } catch (Exception e) {
            throw new JobSchedulerException("Error creating run time: " + e, e);
        }

    }

    private static String[] combineArrays(final String[] hourArray, final 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(final String day, final Element parentDaysElement, final Vector<Element> childElements)
            throws Exception {
        logger.debug("adding day: " + day);
        Element dayElement = parentDaysElement.getOwnerDocument().createElement("day");
        dayElement.setAttribute("day", day);
        Iterator<Element> iter = childElements.iterator();
        while (iter.hasNext()) {
            Element child = iter.next();
            dayElement.appendChild(child.cloneNode(true));
        }
        parentDaysElement.appendChild(dayElement);
    }

    private String[] getArrayFromColumn(final String column) {
        String[] elements = column.split(",");
        Vector<String> result = new Vector<String>();
        for (String element : elements) {
            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 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;
    }

    @SuppressWarnings("unused")
    private String getJobName(String pstrCommand) {
        Matcher commandMatcher = commandRegExPattern.matcher(pstrCommand);
        if (commandMatcher.find()) {
            pstrCommand = commandMatcher.group();
        } else {
            int space = pstrCommand.indexOf(" ");
            if (space != -1) {
                pstrCommand = pstrCommand.substring(0, space);
            }
        }

        if (pstrCommand.startsWith("\"")) {
            pstrCommand = pstrCommand.substring(1);
        }

        if (pstrCommand.endsWith("\"")) {
            pstrCommand = pstrCommand.substring(0, pstrCommand.length() - 1);
        }

        if (pstrCommand.startsWith("/") || pstrCommand.startsWith("\\")) {
            pstrCommand = pstrCommand.substring(1);
        }

        int i = pstrCommand.indexOf("}");
        if (i >= 0) {
            pstrCommand = pstrCommand.substring(i + 1);
        }

        if (flgCreateSubFolderStructure == false) {
            pstrCommand = pstrCommand.replaceAll("/", "_");
            if (pstrCommand.startsWith("_")) {
                pstrCommand = pstrCommand.substring(1);
            }
        } else {
            if (pstrCommand.startsWith("/")) {
                pstrCommand = pstrCommand.substring(1);
            }
        }

        if (strBaseDirectory.length() > 0) {
            pstrCommand = strBaseDirectory + "/" + pstrCommand;
        }
        return pstrCommand;
    }

    private String convertAlias(final Matcher matcher) throws Exception {
        logger.debug("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.debug("Alias converted to " + result);
            return result;
        } catch (Exception e) {
            throw new JobSchedulerException("Error converting alias: " + e, e);
        }
    }

    private static String formatTwoDigits(final String number) {
        if (number.length() == 1)
            return "0" + number;
        return number;
    }

    private static String formatTwoDigits(final 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(final 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(final boolean oldRunTime) {
        this.oldRunTime = oldRunTime;
    }

    /**
     * @return the skipLines
     */
    public HashSet<String> getSkipLines() {
        return skipLines;
    }

    /**
     * The cron lines contained in this set will be skipped when
     * converting a cron file
     * @param skipLines
     */
    public void setSkipLines(final HashSet<String> skipLines) {
        this.skipLines = skipLines;
    }

    /**
     * @return the reservedJobNames
     */
    public HashSet<String> 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(final HashSet<String> 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(final String timeout) {
        this.timeout = timeout;
    }
}