/** * 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.code; import edu.isi.pegasus.planner.code.gridstart.*; import edu.isi.pegasus.planner.classes.ADag; import edu.isi.pegasus.planner.classes.Job; import edu.isi.pegasus.planner.classes.AggregatedJob; import edu.isi.pegasus.planner.common.PegasusProperties; import edu.isi.pegasus.planner.namespace.Pegasus; import edu.isi.pegasus.planner.namespace.Dagman; import edu.isi.pegasus.common.util.DynamicLoader; import java.util.Map; import java.util.HashMap; import edu.isi.pegasus.planner.classes.PegasusBag; import edu.isi.pegasus.planner.common.PegasusConfiguration; /** * An abstract factory class to load the appropriate type of GridStart * implementations, and their corresponding POSTScript classes. * This factory class is different from other factories, in the sense that it * must be instantiated first and intialized first before calling out to any * of the Factory methods. * * * @author Karan Vahi * @version $Revision$ */ public class GridStartFactory { /** * The package name where the implementations of this interface reside * by default. */ public static final String DEFAULT_PACKAGE_NAME = "edu.isi.pegasus.planner.code.gridstart"; /** * The default Gridstart mode */ public static final String DEFAULT_GRIDSTART_MODE = "Kickstart"; /** * The corresponding short names for the implementations. */ public static String[] GRIDSTART_SHORT_NAMES = { "kickstart", "none" }; /** * The known gridstart implementations. */ public static String[] GRIDSTART_IMPLEMENTING_CLASSES = { "Kickstart", "NoGridStart" }; /** * The index in the constant arrays for NoGridStart. */ public static final int KICKSTART_INDEX = 0; /** * The index in the constant arrays for NoGridStart. */ public static final int NO_GRIDSTART_INDEX = 1; /** * The postscript mode in which post scripts are added only for essential * jobs. */ public static final String ESSENTIAL_POST_SCRIPT_SCOPE = "essential"; /** * The postscript mode in which post scripts are added only for all * jobs. */ public static final String ALL_POST_SCRIPT_SCOPE = "all"; // /** * A table that associates POSTScript implementing classes with their * SHORT_NAMES. */ private static Map POSTSCRIPT_IMPLEMENTING_CLASS_TABLE; /** * Initializes the <code>POSTScript</code> implementation table, associating * short names for the POSTScript with the name of the classes itself. */ static{ POSTSCRIPT_IMPLEMENTING_CLASS_TABLE = new HashMap( 8 ); //not really the best way. should have avoided creating objects //but then too many constants everywhere. associate( new UserPOSTScript() ); associate( new NoPOSTScript() ); associate( new NetloggerPostScript() ); associate( new PegasusExitCode() ); } /** * Associates a shortname with the classname. * * @param ps the <code>POSTScript</code> implementation. */ private static void associate( POSTScript ps ){ POSTSCRIPT_IMPLEMENTING_CLASS_TABLE.put( ps.shortDescribe(), ps.getClass().getName() ); } /** * Associates a shortname with the classname. * * @param shortName the shortName for the POSTScript implementation * @param className the fully qualified className of the implementing class. */ private static void associate( String shortName, String className ){ POSTSCRIPT_IMPLEMENTING_CLASS_TABLE.put( shortName, className ); } /** * Returns the name of the implementing POSTSCript class. * * @param shortName the shortName for the POSTScript implementation * * @return the className the fully qualified className of the implementing class, * else null. */ private static String implementingPOSTScriptClass( String shortName ){ Object obj = POSTSCRIPT_IMPLEMENTING_CLASS_TABLE.get( shortName ); return ( obj == null ) ? null : ( String )obj; } /** * The postscript mode. Whether to add postscripts for the jobs or not. * At present just two modes supported * all add postscripts for jobs where kickstart is present. * none do not add postscripts to anyjob */ // private String mPostScriptScope; /** * A table that maps short names of <code>POSTScript</code> implementations * with the implementations themselves. */ private Map mPOSTScriptImplementationTable; /** * A table that maps short names of <code>GridStart</code> implementations * with the implementations themselves. */ private Map mGridStartImplementationTable ; /** * The bag of objects used for initialization. */ private PegasusBag mBag; /** * The properties object holding all the properties. */ private PegasusProperties mProps; /** * The submit directory where the submit files are being generated for * the workflow. */ private String mSubmitDir; /** * The workflow object. */ private ADag mDAG; /** * A boolean indicating that the factory has been initialized. */ private boolean mInitialized; /** * path to the log file to which postscripts should log */ private String mPostScriptLoG; /** * The default constructor. */ public GridStartFactory() { mGridStartImplementationTable = new HashMap( 3 ); mPOSTScriptImplementationTable = new HashMap( 3 ); mInitialized = false; } /** * Initializes the factory with known GridStart implementations. * * @param bag the bag of objects that is used for initialization. * @param dag the concrete dag so far. * @param postScriptLog path to the log file to which postscripts should log */ public void initialize( PegasusBag bag, ADag dag, String postScriptLog ){ mBag = bag; mProps = bag.getPegasusProperties(); mSubmitDir = bag.getPlannerOptions().getSubmitDirectory() ; mDAG = dag; mPostScriptLoG = postScriptLog; // mPostScriptScope = mProps.getPOSTScriptScope(); //load all the known implementations and initialize them for( int i = 0; i < GRIDSTART_IMPLEMENTING_CLASSES.length; i++){ //load via reflection just once registerGridStart( GRIDSTART_SHORT_NAMES[i], this.loadGridStart( bag, dag, GRIDSTART_IMPLEMENTING_CLASSES[i] ) ); } mInitialized = true; } /** * Loads the appropriate gridstart implementation for a job on the basis of * the value of the GRIDSTART_KEY in the Pegasus namepsace. If no value is * specified then the value in the properties file is picked up. * * @param job the job for which we want the gridstart handle. * @param gridStartPath the path to the gridstart from the site catalog. * * @return a handle to appropriate GridStart implementation. * * @see org.griphyn.cPlanner.namespace.Pegasus#GRIDSTART_KEY * @see org.griphyn.cPlanner.common.PegasusProperties#getGridStart() * * @throws GridStartFactoryException that nests any error that * might occur during the instantiation of the implementation. */ public GridStart loadGridStart( Job job, String gridStartPath ) throws GridStartFactoryException { //sanity checks first if( !mInitialized ){ throw new GridStartFactoryException( "GridStartFactory needs to be initialized first before using" ); } GridStart gs = null; if ( job.isMPIJob() && !(job instanceof AggregatedJob )){ //for only MPI jobs that are not PMC, we associate exitcode postscript //with the rotation of logs option and explicity associate //NoGridStart with them job.vdsNS.construct(Pegasus.GRIDSTART_KEY, "None" ); //no empty postscript but arguments to exitcode to add -r $RETURN job.dagmanVariables.construct( Dagman.POST_SCRIPT_KEY, PegasusExitCode.SHORT_NAME ); job.dagmanVariables.construct( Dagman.POST_SCRIPT_ARGUMENTS_KEY, PegasusExitCode.POSTSCRIPT_ARGUMENTS_FOR_ONLY_ROTATING_LOG_FILE ); } //determine the short name of GridStart implementation //on the basis of any profile associated or from the properties file String shortName = this.getGridStartShortName(job); //try loading on the basis of short name from the cache Object obj = this.gridStart( shortName ); if( obj == null ){ //load via reflection and register in the cache obj = this.loadGridStart( mBag, mDAG, shortName ); this.registerGridStart( shortName, (GridStart)obj ); } gs = (GridStart) obj; return gs; } /** * Loads the appropriate POST Script implementation for a job on the basis of * the value of the Pegasus profile GRIDSTART_KEY, and the DAGMan profile * POST_SCRIPT_KEY in the Pegasus namepsace. If no value is * specified then the value in the properties file is picked up. * * @param job the job for which we want the gridstart handle. * @param gridStart the <code>GridStart</code> for which we want to load * the POSTSCRIPT implementation. * * @return a handle to appropriate POSTScript implementation. * * @see org.griphyn.cPlanner.namespace.Pegasus#GRIDSTART_KEY * @see org.griphyn.cPlanner.namespace.Dagman#POST_SCRIPT_KEY * @see org.griphyn.cPlanner.common.PegasusProperties#getGridStart() * * @throws GridStartFactoryException that nests any error that * might occur during the instantiation of the implementation. */ public POSTScript loadPOSTScript( Job job, GridStart gridStart ) throws GridStartFactoryException { //sanity checks first if( !mInitialized ){ throw new GridStartFactoryException( "GridStartFactory needs to be initialized first before using" ); } if ( gridStart == null ){ throw new GridStartFactoryException( "POSTScript can only be instantiated if supplied a GridStart implementation" ); } //figure out the postscript type. the scope takes precedence String postScriptType; String postScriptScope = (String) job.dagmanVariables.get( Dagman.POST_SCRIPT_SCOPE_KEY ); postScriptScope = ( postScriptScope == null )? GridStartFactory.ALL_POST_SCRIPT_SCOPE: postScriptScope; if ( postScriptScope.equals( GridStartFactory.ALL_POST_SCRIPT_SCOPE ) || ( postScriptScope.equals( GridStartFactory.ESSENTIAL_POST_SCRIPT_SCOPE ) && job.getJobType() != Job.REPLICA_REG_JOB) ) { //we need to apply some postscript //let us figure out the type of postscript to instantiate Object profileValue = job.dagmanVariables.get( Dagman.POST_SCRIPT_KEY ); postScriptType = ( profileValue == null )? //get the default associated with gridstart gridStart.defaultPOSTScript(): //use the one specified in profiles/properties ( String ) profileValue; } else{ //mode is none , make sure to remove post key and the arguments postScriptType = NoPOSTScript.SHORT_NAME; } //try loading on the basis of postscript type from the cache Object obj = this.postScript( postScriptType ); POSTScript ps = null; if( obj == null ){ //determine the className for postScriptType String className = GridStartFactory.implementingPOSTScriptClass( postScriptType ); if( className == null ){ //so this is a user specified postscript className = GridStartFactory.implementingPOSTScriptClass( UserPOSTScript.SHORT_NAME ); } //load via reflection and register in the cache obj = this.loadPOSTScript( mProps, mSubmitDir, //mProps.getPOSTScriptPath( postScriptType ), job.dagmanVariables.getPOSTScriptPath( postScriptType ), mPostScriptLoG, className ); this.registerPOSTScript( postScriptType, (POSTScript)obj ); } ps = ( POSTScript ) obj; return ps; } /** * Returns the short name for the gridstart implementation that needs to be * loaded for the job. * * @param job * * @return */ protected String getGridStartShortName( Job job ){ if ( job.vdsNS.containsKey( Pegasus.GRIDSTART_KEY) ){ //pick the one associated in profiles return ( String ) job.vdsNS.get( Pegasus.GRIDSTART_KEY ); } String propValue = mProps.getGridStart(); if ( job.vdsNS.containsKey( Pegasus.DATA_CONFIGURATION_KEY ) ){ //pick up on the basis of the data configuration key value String conf = job.vdsNS.getStringValue( Pegasus.DATA_CONFIGURATION_KEY ); if( (conf.equalsIgnoreCase( PegasusConfiguration.CONDOR_CONFIGURATION_VALUE) || conf.equalsIgnoreCase( PegasusConfiguration.NON_SHARED_FS_CONFIGURATION_VALUE ) ) && propValue == null ){ //PegasusLite for condorio and nonsharedfs mode //as long as user did not specify explicilty in the properties file return "PegasusLite"; } } return ( propValue == null ) ? GridStartFactory.DEFAULT_GRIDSTART_MODE: propValue; //return what was specified in the properties file. } /** * Loads the implementing class corresponding to the class. If the package * name is not specified with the class, then class is assumed to be * in the DEFAULT_PACKAGE. The properties object passed should not be null. * * @param bag the bag of initialization objects * @param dag the concrete dag so far. * @param className the name of the class that implements the mode. It is the * name of the class, not the complete name with package. That * is added by itself. * * @return the instance of the class implementing this interface. * * @throws GridStartFactoryException that nests any error that * might occur during the instantiation of the implementation. * * @see #DEFAULT_PACKAGE_NAME */ private GridStart loadGridStart( PegasusBag bag, ADag dag, String className ) throws GridStartFactoryException { //prepend the package name className = (className.indexOf('.') == -1)? //pick up from the default package DEFAULT_PACKAGE_NAME + "." + className: //load directly className; //try loading the class dynamically GridStart gs = null; try{ DynamicLoader dl = new DynamicLoader( className); gs = (GridStart) dl.instantiate( new Object[0] ); gs.initialize( bag, dag); } catch (Exception e) { throw new GridStartFactoryException("Instantiating GridStart ", className, e); } return gs; } /** * Loads the implementing class corresponding to the class. If the package * name is not specified with the class, then class is assumed to be * in the DEFAULT_PACKAGE. The properties object passed should not be null. * * * @param properties the <code>PegasusProperties</code> object containing all * the properties required by Pegasus. * @param submitDir the submit directory where the submit file for the job * has to be generated. * @param path the path to the postscript on the submit host. * @param globalLog path to global postscript log file * @param className the name of the class that implements the mode. It is the * name of the class, not the complete name with package. That * is added by itself. * * @return the instance of the class implementing this interface. * * @throws GridStartFactoryException that nests any error that * might occur during the instantiation of the implementation. * * @see #DEFAULT_PACKAGE_NAME */ private POSTScript loadPOSTScript( PegasusProperties properties, String submitDir, String path, String globalLog, String className ) throws GridStartFactoryException { //prepend the package name className = (className.indexOf('.') == -1)? //pick up from the default package DEFAULT_PACKAGE_NAME + "." + className: //load directly className; //try loading the class dynamically POSTScript ps = null; try{ DynamicLoader dl = new DynamicLoader( className); ps = ( POSTScript ) dl.instantiate( new Object[0] ); ps.initialize( properties, path, submitDir, globalLog ); } catch (Exception e) { throw new GridStartFactoryException("Instantiating GridStart ", className, e); } return ps; } /** * Returns the cached implementation of <code>POSTScript</code> * from the implementing class table. * * @param type the short name for a <code>POSTScript</code> implementation * * @return implementation the object class implementing that style, else null */ private POSTScript postScript( String type ){ Object obj = mPOSTScriptImplementationTable.get( type.toLowerCase() ); return ( obj == null ) ? null : (POSTScript)obj ; } /** * Inserts an entry into the implementing class table. The name is * converted to lower case before being stored. * * @param name the short name for a <code>POSTScript</code> implementation * @param implementation the object of the class implementing that style. */ private void registerPOSTScript( String name, POSTScript implementation){ mPOSTScriptImplementationTable.put( name.toLowerCase(), implementation ); } /** * Returns the cached implementation of GridStart from the implementing * class table. * * @param name the short name for a GridStart implementation * * @return implementation the object of the class implementing that style, else null */ private GridStart gridStart( String name ){ Object obj = mGridStartImplementationTable.get( name.toLowerCase() ); return ( obj == null ) ? null : (GridStart)obj ; } /** * Inserts an entry into the implementing class table. The name is * converted to lower case before being stored. * * @param name the short name for a GridStart implementation * @param implementation the object of the class implementing that style. */ private void registerGridStart( String name, GridStart implementation){ mGridStartImplementationTable.put( name.toLowerCase(), implementation ); } }