/**
* Copyright 2007-2008 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 edu.isi.pegasus.planner.classes;
import edu.isi.pegasus.planner.cluster.JobAggregator;
import edu.isi.pegasus.planner.partitioner.graph.Graph;
import edu.isi.pegasus.planner.partitioner.graph.GraphNode;
import edu.isi.pegasus.planner.partitioner.graph.MapGraph;
import java.util.List;
import java.util.Iterator;
import java.util.LinkedList;
/**
* This class holds all the specifics of an aggregated job. An aggregated job
* or a clustered job is a job, that contains a collection of smaller jobs.
* An aggregated job during execution may explode into n smaller job executions.
* At present it does not store information about the dependencies between the
* jobs.
*
* @author Karan Vahi
* @version $Revision$
*/
public class AggregatedJob extends Job implements Graph{
/**
* The collection of jobs that are contained in the aggregated job.
*/
// private List mConstituentJobs;
/**
* Boolean indicating whether a job has been fully rendered to an executable
* job or not i.e the aggregated job has been mapped to the aggregator and
* the constituent jobs have been gridstarted or not.
*/
private boolean mHasBeenRenderedToExecutableForm;
/**
* Handle to the JobAggregator that created this job.
*/
private JobAggregator mJobAggregator;
/**
* Handle to the Graph implementor.
*/
private Graph mGraphImplementor;
/**
* The default constructor.
*/
public AggregatedJob() {
super();
// mConstituentJobs = new ArrayList(3);
mHasBeenRenderedToExecutableForm = false;
this.mJobAggregator = null;
mGraphImplementor = new MapGraph();
}
/**
* The overloaded constructor.
*
* @param num the number of constituent jobs
*/
public AggregatedJob(int num) {
this();
// mConstituentJobs = new ArrayList(num);
}
/**
* The overloaded constructor.
*
* @param job the job whose shallow copy is created, and is the main job.
* @param num the number of constituent jobs.
*/
public AggregatedJob(Job job,int num) {
super((Job)job.clone());
// mConstituentJobs = new ArrayList(num);
mHasBeenRenderedToExecutableForm = false;
this.mJobAggregator = null;
this.mGraphImplementor = new MapGraph();
}
/**
* Returns a boolean indicating whether a job has been rendered to an executable
* form or not
*
* @return boolean
*/
public boolean renderedToExecutableForm( ){
return this.mHasBeenRenderedToExecutableForm;
}
/**
* Returns a boolean indicating whether a job has been rendered to an executable
* form or not
*
* @param value boolean to set to.
*/
public void setRenderedToExecutableForm( boolean value ){
this.mHasBeenRenderedToExecutableForm = value;
}
/**
* Sets the JobAggregator that created this aggregated job.
* Useful for rendering the job to an executable form later on.
*
* @param aggregator handle to the JobAggregator used for aggregating the job
*/
public void setJobAggregator( JobAggregator aggregator ){
this.mJobAggregator = aggregator;
}
/**
* Returns the JobAggregator that created this aggregated job.
* Useful for rendering the job to an executable form later on.
*
* @return JobAggregator
*/
public JobAggregator getJobAggregator( ){
return this.mJobAggregator;
}
/**
* Adds a job to the aggregated job.
*
* @param job the job to be added.
*/
public void add(Job job){
// mConstituentJobs.add(job);
//should be getID() instead of logicalID
//however that requires change in vertical clusterer where
//partition object is indexed by logical id's
this.addNode(new GraphNode( job.getLogicalID(), job ) );
}
/**
* Clustered jobs never originate in the DAX. Always return null.
*
* @return null
*/
public String getDAXID(){
return null;
}
/**
* Returns a new copy of the Object. The constituent jobs are also cloned.
*
* @return Object
*/
public Object clone(){
AggregatedJob newJob = new AggregatedJob((Job)super.clone(),
this.size());
/*
for(Iterator it = this.mConstituentJobs.iterator();it.hasNext();){
newJob.add( (Job)(((Job)it.next()).clone()));
}*/
//shallow clone. Fix me.
newJob.mGraphImplementor = (Graph)((MapGraph)this.mGraphImplementor).clone();
newJob.mHasBeenRenderedToExecutableForm = this.mHasBeenRenderedToExecutableForm;
newJob.mJobAggregator = this.mJobAggregator;
return newJob;
}
/**
* Returns an iterator to the constituent jobs of the AggregatedJob.
*
* @return Iterator
*/
public Iterator<Job> constituentJobsIterator(){
// return mConstituentJobs.iterator();
//need to use the toplogical sort iterator for label based clustering
List<Job> l = new LinkedList();
for( Iterator<GraphNode> it = this.nodeIterator(); it.hasNext(); ){
GraphNode n = it.next();
l.add( (Job)n.getContent() );
}
return l.iterator();
}
/**
* Returns a job from a particular position in the list of constituent jobs
*
* @param index the index to retrieve from
*
* @return a constituent job.
*/
public Job getConstituentJob( int index ){
// return (Job) this.mConstituentJobs.get( index );
//should be deprecated
int i =0;
for( Iterator<GraphNode> it = this.nodeIterator(); it.hasNext(); i++ ){
GraphNode n = it.next();
if( i == index ){
return (Job)n.getContent();
}
}
return null;
}
/**
* Returns the number of constituent jobs.
*
* @return Iterator
*/
public int numberOfConsitutentJobs(){
return this.size();
}
/**
* Sets the relative submit directory for the job. The directory is relative
* to the top level directory where the workflow files are placed. It traverses
* through the internal node list, to ensure constituent jobs that are clustered
* jobs themselves are assigned the relative submit directory correctly.
*
* @param dir the directory
*/
@Override
public void setRelativeSubmitDirectory(String dir) {
super.setRelativeSubmitDirectory( dir );
//PM-833 traverse through the internal list
for (Iterator it = this.nodeIterator(); it.hasNext(); ) {
GraphNode node = ( GraphNode )it.next();
Job constituentJob = (Job) node.getContent();
if( constituentJob instanceof AggregatedJob ){
//PM-833 we need to make sure clustered job part of larger
//cluster also has it set
((AggregatedJob)constituentJob).setRelativeSubmitDirectory(dir);
}
}
}
/**
* Returns a textual description of the object.
*
* @return textual description of the job.
*/
public String toString(){
StringBuffer sb = new StringBuffer(32);
sb.append("\n").append("[MAIN JOB]").append(super.toString());
sb.append("\n").append("[CONSTITUENT JOBS]");
int num = 0;
for(Iterator it = this.nodeIterator();it.hasNext();++num){
sb.append("\n").append("[CONSTITUENT JOB] :").append(num);
sb.append(it.next());
}
return sb.toString();
}
/**
* Adds a node to the Graph. It overwrites an already existing node with the
* same ID.
*
* @param node the node to be added to the Graph.
*/
public void addNode(GraphNode node) {
this.mGraphImplementor.addNode(node);
}
/**
* Adds an edge between two already existing nodes in the graph.
*
* @param parent the parent node ID.
* @param child the child node ID.
*/
public void addEdge(String parent, String child) {
this.mGraphImplementor.addEdge(parent, child);
}
/**
* Adds an edge between two already existing nodes in the graph.
*
* @param parent the parent node .
* @param child the child node .
*/
public void addEdge( GraphNode parent, GraphNode child ){
this.mGraphImplementor.addEdge(parent, child);
}
/**
* A convenience method that allows for bulk addition of edges between
* already existing nodes in the graph.
*
* @param child the child node ID
* @param parents list of parent identifiers as <code>String</code>.
*/
public void addEdges(String child, List parents) {
this.mGraphImplementor.addEdges(child, parents);
}
/**
* Returns the node matching the id passed.
*
* @param identifier the id of the node.
*
* @return the node matching the ID else null.
*/
public GraphNode getNode(String identifier) {
return this.mGraphImplementor.getNode(identifier);
}
/**
* Adds a single root node to the Graph. All the exisitng roots of the
* Graph become children of the root.
*
* @param root the <code>GraphNode</code> to be added as a root.
*
* @throws RuntimeException if a node with the same id already exists.
*/
public void addRoot(GraphNode root) {
this.mGraphImplementor.addRoot(root);
}
/**
* Removes a node from the Graph.
*
* @param identifier the id of the node to be removed.
*
* @return boolean indicating whether the node was removed or not.
*/
public boolean remove(String identifier) {
return this.mGraphImplementor.remove(identifier);
}
/**
* Resets all the dependencies in the Graph, while preserving the nodes.
* The resulting Graph is a graph of independent nodes.
*/
public void resetEdges(){
this.mGraphImplementor.resetEdges();
}
/**
* Returns an iterator for the nodes in the Graph. These iterators are
* fail safe.
*
* @return Iterator
*/
public Iterator<GraphNode> nodeIterator() {
return this.mGraphImplementor.nodeIterator();
}
/**
* Returns an iterator that traverses through the graph using a graph
* traversal algorithm.
*
* @return Iterator through the nodes of the graph.
*/
public Iterator<GraphNode> iterator() {
return this.mGraphImplementor.iterator();
}
/**
* Returns an iterator that traverses the graph bottom up from the leaves.
* At any one time, only one iterator can
* iterate through the graph.
*
* @return Iterator through the nodes of the graph.
*/
public Iterator bottomUpIterator(){
return this.mGraphImplementor.bottomUpIterator();
}
/**
* Returns an iterator for the graph that traverses in topological sort
* order.
*
* @return Iterator through the nodes of the graph.
*/
public Iterator<GraphNode> topologicalSortIterator() {
return this.mGraphImplementor.topologicalSortIterator();
}
/**
* Returns the number of nodes in the graph.
*/
public int size() {
return this.mGraphImplementor.size();
}
/**
* Returns the root nodes of the Graph.
*
* @return a list containing <code>GraphNode</code> corressponding to the
* root nodes.
*/
public List<GraphNode> getRoots() {
return this.mGraphImplementor.getRoots();
}
/**
* Returns the leaf nodes of the Graph.
*
* @return a list containing <code>GraphNode</code> corressponding to the
* leaf nodes.
*/
public List<GraphNode> getLeaves() {
return this.mGraphImplementor.getLeaves();
}
/**
* Returns a boolean if there are no nodes in the graph.
*
* @return boolean
*/
public boolean isEmpty() {
return this.mGraphImplementor.isEmpty();
}
/**
* Returns a boolean indicating whether a graph has cyclic edges or not.
*
* @return boolean
*/
public boolean hasCycles(){
return this.mGraphImplementor.hasCycles();
}
/**
* Returns the detected cyclic edge if , hasCycles returns true
*
* @return
*/
public NameValue getCyclicEdge(){
return this.mGraphImplementor.getCyclicEdge();
}
}