/**
* 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.selector.site;
import edu.isi.pegasus.common.logging.LogManager;
import edu.isi.pegasus.planner.classes.Job;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* This ends up scheduling the jobs in a round robin manner. In order to avoid
* starvation, the jobs are scheduled in a round robin manner per level, and
* the queue is initialised for each level.
*
* @author Karan Vahi
* @version $Revision$
*/
public class RoundRobin
extends AbstractPerJob {
/**
* The current level in the abstract workflow. It is level that is designated
* by Chimera while constructing the graph bottom up.
*/
private int mCurrentLevel;
/**
* The list of pools that have been given by the user at run time or has been
* authenticated against. At present these are the same as the list of pools
* that is passed for site selection at each function.
*/
private java.util.LinkedList mExecPools;
/**
* The default constructor. Not to be used.
*/
public RoundRobin() {
mCurrentLevel = -1;
}
/**
* Returns a brief description of the site selection techinque implemented by
* this class.
*
* @return String
*/
public String description() {
String st = "Round Robin Scheduling per level of the workflow";
return st;
}
/**
* Maps a job in the workflow to an execution site.
*
* @param job the job to be mapped.
* @param sites the list of <code>String</code> objects representing the
* execution sites that can be used.
*
*/
public void mapJob( Job job, List sites ){
NameValue current;
NameValue next;
if ( mExecPools == null ) {
initialiseList( sites );
}
if ( job.level != mCurrentLevel ) {
//reinitialize stuff
System.out.println( "Job " + job.getID() + " Change in level to " + job.level );
System.out.println( "execution sites " + listToString( mExecPools ) );
mCurrentLevel = job.level;
for( ListIterator it= mExecPools.listIterator(); it.hasNext() ;) {
( ( NameValue ) it.next() ).setValue( 0 );
}
}
//go around the list and schedule it to the first one where it can
String mapping = null;
for( ListIterator it = mExecPools.listIterator();it.hasNext();){
//System.out.println( "List is " + listToString( mExecPools ) );
current = ( NameValue ) it.next();
//check if job can run on pool
if ( mTCMapper.isSiteValid( job.namespace, job.logicalName,
job.version, current.getName() ) ) {
mapping = current.getName();
//update the the number of times used and place it at the
//correct position in the list
current.increment();
//the current element stays at it's place if it is the only one
//in the list or it's value is less than the next one.
if ( it.hasNext() ) {
next = ( NameValue ) it.next();
if ( current.getValue() <= next.getValue() ) {
it.previous();
continue;
} else {
//current's value is now greater than the next
current = ( NameValue ) it.previous();
current = ( NameValue ) it.previous();
}
}
it.remove();
//System.out.println( "List after removal of " + current + " is " + listToString( mExecPools ) );
//now go thru the list and insert in the correct position
while ( it.hasNext() ) {
next = ( NameValue ) it.next();
if ( current.getValue() <= next.getValue() ) {
//current has to be inserted before next
next = ( NameValue ) it.previous();
break;
}
}
//current goes to the current position or the end of the list
it.add( current );
break;
}
else{
mLogger.log( "Job " + job.getName() + " cannot be mapped to site " + current,
LogManager.DEBUG_MESSAGE_LEVEL );
}
}
//means no pool has been found to which the job could be mapped to.
mLogger.log( "[RoundRobin Site Selector] Mapped job " + job.getID() + " to site " + mapping,
LogManager.DEBUG_MESSAGE_LEVEL );
job.setSiteHandle( mapping );
}
/**
* It initialises the internal list. A node in the list corresponds to a pool
* that can be used, and has the value associated with it which is the
* number of jobs in the current level have been scheduled to it.
*
* @param pools List
*/
private void initialiseList( List pools ) {
if ( mExecPools == null ) {
mExecPools = new java.util.LinkedList();
Iterator it = pools.iterator();
while ( it.hasNext() ) {
mExecPools.add( new NameValue( ( String ) it.next(), 0 ) );
}
}
}
private String listToString( List elements ) {
StringBuilder sb = new StringBuilder();
sb.append( "size -> " ).append( elements.size() ).append( " ");
for( Object element: elements ){
sb.append( element ).append( "," );
}
return sb.toString();
}
/**
* A inner name value class that associates a string with an int value.
* This is used to populate the round robin list that is used by this
* scheduler.
*/
class NameValue {
/**
* Stores the name of the pair (the left handside of the mapping).
*/
private String name;
/**
* Stores the corresponding value to the name in the pair.
*/
private int value;
/**
* The default constructor which initialises the class member variables.
*/
public NameValue() {
name = new String();
value = -1;
}
/**
* Initialises the class member variables to the values passed in the
* arguments.
*
* @param name corresponds to the name in the NameValue pair
*
* @param value corresponds to the value for the name in the NameValue pair
*/
public NameValue( String name, int value ) {
this.name = name;
this.value = value;
}
/**
* The set method to set the value.
* @param value int
*/
public void setValue( int value ) {
this.value = value;
}
/**
* Returns the value associated with this pair.
* @return int
*/
public int getValue() {
return this.value;
}
/**
* Returns the key of this pair, i.e the left hand side of the mapping.
* @return String
*/
public String getName() {
return this.name;
}
/**
* Increments the int value by one.
*/
public void increment() {
value += 1;
}
/**
* Returns a copy of this object.
* @return Object
*/
public Object clone() {
NameValue nv = new NameValue( this.name, this.value );
return nv;
}
/**
* Writes out the contents of the class to a String in form suitable for
* displaying.
* @return String
*/
public String toString() {
String str = name + "-->" + value;
return str;
}
}
}