/*
* ProActive Parallel Suite(TM):
* The Open Source library for parallel and distributed
* Workflows & Scheduling, Orchestration, Cloud Automation
* and Big Data Analysis on Enterprise Grids & Clouds.
*
* Copyright (c) 2007 - 2017 ActiveEon
* Contact: contact@activeeon.com
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation: version 3 of
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If needed, contact us to obtain a release under GPL Version 2 or 3
* or a different license than the AGPL.
*/
package org.ow2.proactive.scheduler.common.job.factories;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.text.NumberFormat;
import java.util.ArrayList;
import org.apache.log4j.Logger;
import org.objectweb.proactive.annotation.PublicAPI;
import org.ow2.proactive.scheduler.common.exception.JobCreationException;
import org.ow2.proactive.scheduler.common.job.Job;
import org.ow2.proactive.scheduler.common.job.TaskFlowJob;
import org.ow2.proactive.scheduler.common.task.NativeTask;
import org.ow2.proactive.scripting.InvalidScriptException;
import org.ow2.proactive.scripting.SelectionScript;
import org.ow2.proactive.scripting.SimpleScript;
import org.ow2.proactive.utils.Tools;
/**
* This class implements static methods use to parse a text file containing commands, and from it build
* a ProActive Scheduler job made of native tasks. each task of the jobs corresponds to a line of the
* parsed file. This is a way to avoid creation of XML job descriptor for creation of simple jobs.
*
* Every line of the text file is taken and considered as a native command from which a native task is built,
* except lines beginning with {@link FlatJobFactory#CMD_FILE_COMMENT_CHAR} and empty lines.
* dependencies between tasks cannot be set, task names are automatically set. A log file can be specified.
* A selection script can be associated for all the tasks, but not specific selection script for each tasks.
* A Job name can be specified too.
*
* This class does not intend to provide a job specification with all ProActive Scheduler jobs feature, but is
* way to define quickly jobs made of native tasks to execute in parallel.
* If you need to define jobs with dependencies, jobs with java Tasks, specific selection script for each task,
* or generation scripts... you should rather use XML job descriptors and {@link JobFactory}.
*
*
* the class presents too a way to create a job made of one task from a String representing a native command to launch.
*
* @author ProActive team
*
*/
@PublicAPI
public class FlatJobFactory {
/**
* Log4j logger name
*/
public static Logger logger = Logger.getLogger(FlatJobFactory.class);
/**
* comment character used to ignore line in text file containing
* native commands
*/
public static final String CMD_FILE_COMMENT_CHAR = "#";
/**
* String prefix used to build default job name (if no job name is specified).
*/
public static final String JOB_DEFAULT_NAME_PREFIX = "Job_";
/**
* Singleton Pattern
*/
private static FlatJobFactory factory = null;
/**
* Return the instance of the jobFactory.
*
* @return the instance of the jobFactory.
*/
public static FlatJobFactory getFactory() {
if (factory == null) {
factory = new FlatJobFactory();
}
return factory;
}
/**
* Create a job from a String representing file path, this text file contains native commands to launch
* Every line of the text file is taken and considered as a native command from which a native task is built,
* except lines beginning with {@link FlatJobFactory#JOB_DEFAULT_NAME_PREFIX} and empty lines.
* So job in result is made of several native tasks without dependencies.
*
* @param commandFilePath a string representing a text file containing native commands.
* @param jobName A String representing a name to give to the job. If null, default job name is made of
* {@link FlatJobFactory#JOB_DEFAULT_NAME_PREFIX} + userName parameter.
* @param selectionScriptPath a Path to a file containing a selection script, or null if
* no script is needed.
* @param userName name of connected user that asked job creation, null otherwise. This parameter
* is only used for default job's name creation.
* @return a job object representing created job and ready-to-schedule job.
* @throws JobCreationException with a relevant error message if an error occurs.
*/
public Job createNativeJobFromCommandsFile(String commandFilePath, String jobName, String selectionScriptPath,
String userName) throws JobCreationException {
if (jobName == null) {
jobName = JOB_DEFAULT_NAME_PREFIX + userName;
}
Job nativeJob = new TaskFlowJob();
nativeJob.setName(jobName);
logger.debug("Job : " + nativeJob.getName());
try {
File commandFile = new File(commandFilePath);
if (!commandFile.isFile()) {
throw new JobCreationException("Error occured during Job creation, " + "check that file " +
commandFilePath + " exists and is a readable file");
}
String commandLine;
int task_number = 0;
BufferedReader reader = new BufferedReader(new FileReader(commandFile));
ArrayList<String> commandList = new ArrayList<>();
while ((commandLine = reader.readLine()) != null) {
commandLine = commandLine.trim();
if (!commandLine.startsWith(CMD_FILE_COMMENT_CHAR, 0) && !"".equals(commandLine)) {
commandList.add(commandLine);
}
}
if (commandList.size() == 0) {
throw new JobCreationException("Error occured during Job creation, " +
"No any valid command line has been built from" + commandFilePath + "");
}
//compute padding for task number
int numberOfDigit = Integer.toString(commandList.size()).length();
NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumIntegerDigits(numberOfDigit);
nf.setMinimumIntegerDigits(numberOfDigit);
for (String command : commandList) {
NativeTask t = createNativeTaskFromCommandString(command,
"task_" + (nf.format(++task_number)),
selectionScriptPath);
t.setPreciousResult(true);
((TaskFlowJob) nativeJob).addTask(t);
logger.debug("-> Task Name = " + t.getName());
logger.debug("-> command = " + t.getCommandLine() + "\n");
}
} catch (Exception e) {
throw new JobCreationException(e);
}
return nativeJob;
}
/**
* Creates a job from a String representing a native command to launch. So job in result is made
* of one native task.
*
* @param command a string representing an executable command to launch.
* @param jobName A String representing a name to give to the job, if null. default job name is made of
* {link FlatJobFactory#JOB_DEFAULT_NAME_PREFIX} + userName parameter.
* @param selectionScriptPath A Path to a file containing a selection script, or null if
* no script is needed.
* @param userName name of connected user that asked job creation, null otherwise. This parameter
* is just used for default job's name creation.
* @return a job object representing created job and ready-to-schedule job.
* @throws JobCreationException with a relevant error message if an error occurs.
*/
public Job createNativeJobFromCommand(String command, String jobName, String selectionScriptPath, String userName)
throws JobCreationException {
if (command == null || "".equalsIgnoreCase(command)) {
throw new JobCreationException("Error, command cannot be null");
}
if (jobName == null) {
jobName = JOB_DEFAULT_NAME_PREFIX + userName;
}
Job nativeJob = new TaskFlowJob();
nativeJob.setName(jobName);
logger.debug("Job : " + nativeJob.getName());
try {
NativeTask t = createNativeTaskFromCommandString(command, "task1", selectionScriptPath);
t.setPreciousResult(true);
((TaskFlowJob) nativeJob).addTask(t);
logger.debug("-> Task Name = " + t.getName());
logger.debug("-> command = " + t.getCommandLine() + "\n");
} catch (Exception e) {
throw new JobCreationException(e);
}
return nativeJob;
}
/**
* Creates a native task from a string representing a native command to execute.
* @param command a String representing a native command.
* @param taskName an eventual name for the task.
* @param selectionScriptPath path to an existing file containing a selection script code.
* @return a NativeTask object that can be put in a Job Object.
* @throws InvalidScriptException if an error occurs in definition of selection script
* from file path specified.
*/
private NativeTask createNativeTaskFromCommandString(String command, String taskName, String selectionScriptPath)
throws InvalidScriptException {
NativeTask desc = new NativeTask();
desc.setCommandLine(Tools.parseCommandLine(command));
desc.setName(taskName);
if (selectionScriptPath != null) {
SelectionScript script = new SelectionScript(new SimpleScript(new File(selectionScriptPath), null), true);
desc.addSelectionScript(script);
}
return desc;
}
}