/**
* Copyright 2012-2013 University Of Southern California
*
* 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.
*/
package org.workflowsim;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.cloudbus.cloudsim.Log;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.workflowsim.utils.Parameters;
import org.workflowsim.utils.Parameters.FileType;
import org.workflowsim.utils.ReplicaCatalog;
/**
* WorkflowParser parse a DAX into tasks so that WorkflowSim can manage them
*
* @author Weiwei Chen
* @since WorkflowSim Toolkit 1.0
* @date Aug 23, 2013
* @date Nov 9, 2014
*/
public final class WorkflowParser {
/**
* The path to DAX file.
*/
private final String daxPath;
/**
* The path to DAX files.
*/
private final List<String> daxPaths;
/**
* All tasks.
*/
private List<Task> taskList;
/**
* User id. used to create a new task.
*/
private final int userId;
/**
* current job id. In case multiple workflow submission
*/
private int jobIdStartsFrom;
/**
* Gets the task list
*
* @return the task list
*/
@SuppressWarnings("unchecked")
public List<Task> getTaskList() {
return taskList;
}
/**
* Sets the task list
*
* @param taskList the task list
*/
protected void setTaskList(List<Task> taskList) {
this.taskList = taskList;
}
/**
* Map from task name to task.
*/
protected Map<String, Task> mName2Task;
/**
* Initialize a WorkflowParser
*
* @param userId the user id. Currently we have just checked single user
* mode
*/
public WorkflowParser(int userId) {
this.userId = userId;
this.mName2Task = new HashMap<>();
this.daxPath = Parameters.getDaxPath();
this.daxPaths = Parameters.getDAXPaths();
this.jobIdStartsFrom = 1;
setTaskList(new ArrayList<>());
}
/**
* Start to parse a workflow which is a xml file(s).
*/
public void parse() {
if (this.daxPath != null) {
parseXmlFile(this.daxPath);
} else if (this.daxPaths != null) {
for (String path : this.daxPaths) {
parseXmlFile(path);
}
}
}
/**
* Sets the depth of a task
*
* @param task the task
* @param depth the depth
*/
private void setDepth(Task task, int depth) {
if (depth > task.getDepth()) {
task.setDepth(depth);
}
for (Task cTask : task.getChildList()) {
setDepth(cTask, task.getDepth() + 1);
}
}
/**
* Parse a DAX file with jdom
*/
private void parseXmlFile(String path) {
try {
SAXBuilder builder = new SAXBuilder();
//parse using builder to get DOM representation of the XML file
Document dom = builder.build(new File(path));
Element root = dom.getRootElement();
List<Element> list = root.getChildren();
for (Element node : list) {
switch (node.getName().toLowerCase()) {
case "job":
long length = 0;
String nodeName = node.getAttributeValue("id");
String nodeType = node.getAttributeValue("name");
/**
* capture runtime. If not exist, by default the runtime
* is 0.1. Otherwise CloudSim would ignore this task.
* BUG/#11
*/
double runtime;
if (node.getAttributeValue("runtime") != null) {
String nodeTime = node.getAttributeValue("runtime");
runtime = 1000 * Double.parseDouble(nodeTime);
if (runtime < 100) {
runtime = 100;
}
length = (long) runtime;
} else {
Log.printLine("Cannot find runtime for " + nodeName + ",set it to be 0");
} //multiple the scale, by default it is 1.0
length *= Parameters.getRuntimeScale();
List<Element> fileList = node.getChildren();
List<FileItem> mFileList = new ArrayList<>();
for (Element file : fileList) {
if (file.getName().toLowerCase().equals("uses")) {
String fileName = file.getAttributeValue("name");//DAX version 3.3
if (fileName == null) {
fileName = file.getAttributeValue("file");//DAX version 3.0
}
if (fileName == null) {
Log.print("Error in parsing xml");
}
String inout = file.getAttributeValue("link");
double size = 0.0;
String fileSize = file.getAttributeValue("size");
if (fileSize != null) {
size = Double.parseDouble(fileSize) /*/ 1024*/;
} else {
Log.printLine("File Size not found for " + fileName);
}
/**
* a bug of cloudsim, size 0 causes a problem. 1
* is ok.
*/
if (size == 0) {
size++;
}
/**
* Sets the file type 1 is input 2 is output
*/
FileType type = FileType.NONE;
switch (inout) {
case "input":
type = FileType.INPUT;
break;
case "output":
type = FileType.OUTPUT;
break;
default:
Log.printLine("Parsing Error");
break;
}
FileItem tFile;
/*
* Already exists an input file (forget output file)
*/
if (size < 0) {
/*
* Assuming it is a parsing error
*/
size = 0 - size;
Log.printLine("Size is negative, I assume it is a parser error");
}
/*
* Note that CloudSim use size as MB, in this case we use it as Byte
*/
if (type == FileType.OUTPUT) {
/**
* It is good that CloudSim does tell
* whether a size is zero
*/
tFile = new FileItem(fileName, size);
} else if (ReplicaCatalog.containsFile(fileName)) {
tFile = ReplicaCatalog.getFile(fileName);
} else {
tFile = new FileItem(fileName, size);
ReplicaCatalog.setFile(fileName, tFile);
}
tFile.setType(type);
mFileList.add(tFile);
}
}
Task task;
//In case of multiple workflow submission. Make sure the jobIdStartsFrom is consistent.
synchronized (this) {
task = new Task(this.jobIdStartsFrom, length);
this.jobIdStartsFrom++;
}
task.setType(nodeType);
task.setUserId(userId);
mName2Task.put(nodeName, task);
for (FileItem file : mFileList) {
task.addRequiredFile(file.getName());
}
task.setFileList(mFileList);
this.getTaskList().add(task);
/**
* Add dependencies info.
*/
break;
case "child":
List<Element> pList = node.getChildren();
String childName = node.getAttributeValue("ref");
if (mName2Task.containsKey(childName)) {
Task childTask = (Task) mName2Task.get(childName);
for (Element parent : pList) {
String parentName = parent.getAttributeValue("ref");
if (mName2Task.containsKey(parentName)) {
Task parentTask = (Task) mName2Task.get(parentName);
parentTask.addChild(childTask);
childTask.addParent(parentTask);
}
}
}
break;
}
}
/**
* If a task has no parent, then it is root task.
*/
ArrayList roots = new ArrayList<>();
for (Task task : mName2Task.values()) {
task.setDepth(0);
if (task.getParentList().isEmpty()) {
roots.add(task);
}
}
/**
* Add depth from top to bottom.
*/
for (Iterator it = roots.iterator(); it.hasNext();) {
Task task = (Task) it.next();
setDepth(task, 1);
}
/**
* Clean them so as to save memory. Parsing workflow may take much
* memory
*/
this.mName2Task.clear();
} catch (JDOMException jde) {
Log.printLine("JDOM Exception;Please make sure your dax file is valid");
} catch (IOException ioe) {
Log.printLine("IO Exception;Please make sure dax.path is correctly set in your config file");
} catch (Exception e) {
e.printStackTrace();
Log.printLine("Parsing Exception");
}
}
}