/* * * 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.parser.dax; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.xml.sax.SAXException; import edu.isi.pegasus.common.logging.LogManager; import edu.isi.pegasus.common.logging.LogManagerFactory; import edu.isi.pegasus.common.logging.LoggingKeys; import edu.isi.pegasus.common.util.CondorVersion; import edu.isi.pegasus.common.util.Separator; import edu.isi.pegasus.common.util.Version; import edu.isi.pegasus.planner.catalog.classes.SysInfo; import edu.isi.pegasus.planner.catalog.site.classes.GridGateway; import edu.isi.pegasus.planner.catalog.transformation.TransformationCatalogEntry; import edu.isi.pegasus.planner.catalog.transformation.classes.TCType; import edu.isi.pegasus.planner.catalog.transformation.impl.Abstract; import edu.isi.pegasus.planner.classes.CompoundTransformation; import edu.isi.pegasus.planner.classes.DAGJob; import edu.isi.pegasus.planner.classes.DAXJob; import edu.isi.pegasus.planner.classes.Job; import edu.isi.pegasus.planner.classes.Notifications; import edu.isi.pegasus.planner.classes.PCRelation; import edu.isi.pegasus.planner.classes.PegasusBag; import edu.isi.pegasus.planner.classes.PegasusFile; import edu.isi.pegasus.planner.classes.Profile; import edu.isi.pegasus.planner.classes.ReplicaLocation; import edu.isi.pegasus.planner.classes.PegasusFile.LINKAGE; import edu.isi.pegasus.planner.code.GridStartFactory; import edu.isi.pegasus.planner.common.VariableExpansionReader; import edu.isi.pegasus.planner.dax.Executable; import edu.isi.pegasus.planner.dax.Invoke; import edu.isi.pegasus.planner.dax.PFN; import edu.isi.pegasus.planner.dax.Executable.ARCH; import edu.isi.pegasus.planner.dax.Executable.OS; import edu.isi.pegasus.planner.dax.Invoke.WHEN; import edu.isi.pegasus.planner.dax.MetaData; import edu.isi.pegasus.planner.namespace.Hints; import edu.isi.pegasus.planner.namespace.Pegasus; import edu.isi.pegasus.planner.parser.StackBasedXMLParser; import java.io.FileReader; import org.xml.sax.InputSource; /** * This class uses the Xerces SAX2 parser to validate and parse an XML * document conforming to the DAX Schema 3.2 * * @author Karan Vahi vahi@isi.edu * @version $Revision$ */ public class DAXParser3 extends StackBasedXMLParser implements DAXParser { /** * The "not-so-official" location URL of the DAX Parser Schema. */ public static final String DEFAULT_SCHEMA_LOCATION = "http://pegasus.isi.edu/schema/dax-3.6.xsd"; /** * The "not-so-official" location URL of the DAX Parser Schema. */ public static final String SCHEMA_LOCATION_DIRECTORY = "http://pegasus.isi.edu/schema/"; /** * uri namespace */ public static final String SCHEMA_NAMESPACE = "http://pegasus.isi.edu/schema/DAX"; /** * Constant denoting an undefined site */ public static final String UNDEFINED_SITE = "undefined"; /* * Predefined Constant for dax version 3.2.0 */ public static final long DAX_VERSION_3_2_0 = CondorVersion.numericValue( "3.2.0" ); /* * Predefined Constant for dax version 3.3.0 */ public static final long DAX_VERSION_3_3_0 = CondorVersion.numericValue( "3.3.0" ); /* * Predefined Constant for dax version 3.4.0 */ public static final long DAX_VERSION_3_4_0 = CondorVersion.numericValue( "3.4.0" ); /* * Predefined Constant for dax version 3.5.0 */ public static final long DAX_VERSION_3_5_0 = CondorVersion.numericValue( "3.5.0" ); /* * Predefined Constant for dax version 3.6.0 */ public static final long DAX_VERSION_3_6_0 = CondorVersion.numericValue( "3.6.0" ); /** * Constant denoting default metadata type */ private String DEFAULT_METADATA_TYPE = "String"; /** * List of parents for a child node in the graph */ protected List<PCRelation> mParents; /** * Handle to the callback */ protected Callback mCallback; /** * A job prefix specifed at command line. */ protected String mJobPrefix; /** * Schema version of the DAX as detected in the factory. */ protected String mSchemaVersion; /** * The overloaded constructor. The schema version passed is determined * in the DAXFactory * * @param bag * @param schemaVersion the schema version specified in the DAX file. */ public DAXParser3( PegasusBag bag, String schemaVersion ) { super( bag ); mSchemaVersion = schemaVersion; mJobPrefix = ( bag.getPlannerOptions() == null ) ? null: bag.getPlannerOptions().getJobnamePrefix(); } /** * Set the DAXCallback for the parser to call out to. * * @param c the callback */ public void setDAXCallback( Callback c ){ this.mCallback = c; } /** * Returns the DAXCallback for the parser * * @return the callback */ public Callback getDAXCallback( ){ return this.mCallback; } /** * The main method that starts the parsing. * * @param file the XML file to be parsed. */ public void startParser( String file ) { mLogger.logEventStart( LoggingKeys.EVENT_PEGASUS_PARSE_DAX, LoggingKeys.DAX_ID, file ); try { //PM-938 set the schema location. we cannot set it in constructor this.setSchemaLocations(); this.testForFile( file ); //PM-831 set up the parser with our own reader //that allows for parameter expansion before //doing any XML processing InputSource is = new InputSource( new VariableExpansionReader( new FileReader( file ) )); mParser.parse( is ); //sanity check if ( mDepth != 0 ){ throw new RuntimeException( "Invalid stack depth at end of parsing " + mDepth ); } } catch ( IOException ioe ) { mLogger.log( "IO Error :" + ioe.getMessage(), LogManager.ERROR_MESSAGE_LEVEL ); } catch ( SAXException se ) { if ( mLocator != null ) { mLogger.log( "Error in " + mLocator.getSystemId() + " at line " + mLocator.getLineNumber() + " at column " + mLocator.getColumnNumber() + " :" + se.getMessage() , LogManager.ERROR_MESSAGE_LEVEL); } } mLogger.logEventCompletion(); } /** * Returns the XML schema namespace that a document being parsed conforms * to. * * @return the schema namespace */ public String getSchemaNamespace( ){ return DAXParser3.SCHEMA_NAMESPACE; } /** * Returns the local path to the XML schema against which to validate. * * @return path to the schema */ public String getSchemaLocation() { // treat URI as File, yes, I know - I need the basename File uri = new File( "dax-" + mSchemaVersion + ".xsd"); // create a pointer to the default local position File dax = new File( this.mProps.getSchemaDir(), uri.getName() ); return this.mProps.getDAXSchemaLocation( dax.getAbsolutePath() ); } /** * Composes the <code>SiteData</code> object corresponding to the element * name in the XML document. * * @param element the element name encountered while parsing. * @param names is a list of attribute names, as strings. * @param values is a list of attribute values, to match the key list. * * @return the relevant SiteData object, else null if unable to construct. * * @exception IllegalArgumentException if the element name is too short. */ public Object createObject( String element, List names, List values ){ if ( element == null || element.length() < 1 ){ throw new IllegalArgumentException("illegal element length"); } switch ( element.charAt(0) ) { // a adag argument case 'a': if ( element.equals( "adag" ) ) { //for now the adag element is just a map of //key value pair Map<String,String> m = new HashMap(); for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); m.put( name, value ); } sanityCheckOnVersion( m.get( "version" ) ); //put the call to the callback this.mCallback.cbDocument( m ); return m; }//end of element adag else if( element.equals( "argument" ) ){ //arguments are constructed from character data //since we don't trim it by default, reset text content //buffer explicitly at start of arguments tag mTextContent.setLength(0); return new Arguments(); } return null; //c child compound case 'c': if( element.equals( "child") ){ this.mParents = new LinkedList<PCRelation>(); PCRelation pc = new PCRelation(); String child = null; for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); if ( name.equals( "ref" ) ) { child = value; } } if( child == null ){ this.complain( element, "child", child ); return null; } pc.setChild( child ); return pc; } else if ( element.equals( "compound") ){ } return null; //d dag dax case 'd': if( element.equals( "dag" ) || element.equals( "dax" ) ){ Job j = new Job( ); //all jobs in the DAX are of type compute j.setUniverse( GridGateway.JOB_TYPE.compute.toString() ); j.setJobType( Job.COMPUTE_JOB ); String file = null; for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); if ( name.equals( "namespace" ) ) { j.setTXNamespace( value ); } else if( name.equals( "name" ) ){ j.setTXName( value ); } else if( name.equals( "version" ) ){ j.setTXVersion( value ); } else if( name.equals( "id" ) ){ j.setLogicalID( value ); } else if( name.equals( "file" ) ){ file = value; } else if( name.equals( "node-label" ) ){ j.setNodeLabel( value ); } else { this.complain( element, name, value ); } } if( file == null ){ this.complain( element, "file", file ); return null; } PegasusFile pf = new PegasusFile( file ); pf.setLinkage( LINKAGE.INPUT ); if( element.equals( "dag" ) ){ DAGJob dagJob = new DAGJob( j ); //we dont want notifications to be inherited dagJob.resetNotifications(); dagJob.setDAGLFN( file ); dagJob.addInputFile( pf ); //the job should always execute on local site //for time being dagJob.hints.construct(Hints.EXECUTION_SITE_KEY, "local"); //also set the executable to be used dagJob.hints.construct(Hints.PFN_HINT_KEY, "/opt/condor/bin/condor-dagman"); //add default name and namespace information dagJob.setTransformation("condor", "dagman", null); dagJob.setDerivation("condor", "dagman", null); dagJob.level = -1; //dagman jobs are always launched without a gridstart dagJob.vdsNS.construct(Pegasus.GRIDSTART_KEY, GridStartFactory.GRIDSTART_SHORT_NAMES[GridStartFactory.NO_GRIDSTART_INDEX]); //set the internal primary id for job //dagJob.setName( constructJobID( dagJob ) ); dagJob.setName( dagJob.generateName( this.mJobPrefix) ); return dagJob; } else if (element.equals( "dax" ) ){ DAXJob daxJob = new DAXJob( j ); //we dont want notifications to be inherited daxJob.resetNotifications(); //the job should be tagged type pegasus daxJob.setTypeRecursive(); //the job should always execute on local site //for time being daxJob.hints.construct(Hints.EXECUTION_SITE_KEY, "local" ); //also set a fake executable to be used daxJob.hints.construct( Hints.PFN_HINT_KEY, "/tmp/pegasus-plan" ); //retrieve the extra attribute about the DAX daxJob.setDAXLFN( file ); daxJob.addInputFile( pf ); //add default name and namespace information daxJob.setTransformation( "pegasus", "pegasus-plan", Version.instance().toString() ); daxJob.setDerivation( "pegasus", "pegasus-plan", Version.instance().toString() ); daxJob.level = -1; //set the internal primary id for job //daxJob.setName( constructJobID( daxJob ) ); daxJob.setName( daxJob.generateName( this.mJobPrefix) ); return daxJob; } }//end of element job return null;//end of j //e executable case 'e': if( element.equals( "executable" ) ){ String namespace = null; String execName = null; String version = null; ARCH arch = null; OS os = null; String os_release = null; String os_version = null; String os_glibc = null; Boolean os_installed = true; // Default is installed for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); if ( name.equals( "namespace" ) ) { namespace = value; } else if( name.equals( "name" ) ){ execName = value; } else if( name.equals( "version" ) ){ version = value; } else if( name.equals( "arch" ) ){ arch = Executable.ARCH.valueOf( value.toLowerCase() ); } else if( name.equals( "os" ) ){ os = Executable.OS.valueOf( value.toLowerCase() ); } else if( name.equals( "osrelease" ) ){ os_release = value; } else if( name.equals( "osversion" ) ){ os_version =value; } else if( name.equals( "glibc" ) ){ os_glibc = value; } else if( name.equals( "installed" ) ){ os_installed = Boolean.parseBoolean( value ); } } Executable executable = new Executable( namespace , execName ,version); executable.setArchitecture(arch); executable.setOS(os); executable.setOSRelease(os_release); executable.setOSVersion(os_version); executable.setGlibc(os_glibc); executable.setInstalled(os_installed); return executable; }//end of element executable return null; //end of e //f file case 'f': if( element.equals( "file" ) ){ //create a FileTransfer Object or shd it be ReplicaLocations? //FileTransfer ft = new FileTransfer(); ReplicaLocation rl = new ReplicaLocation(); for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); if ( name.equals( "name" ) ) { //ft.setLFN( value ); rl.setLFN( value ); } else if( name.equals( "link" ) ){ //ignore dont need to do anything } else if( name.equals( "optional" ) ){ Boolean optional = Boolean.parseBoolean( value ); if( optional ){ //replica location object does not handle //optional attribute right now. // ft.setFileOptional(); } } else { this.complain( element, name, value ); } } return rl; }//end of element file return null; //end of f //i invoke case 'i': if( element.equals( "invoke" ) ){ String when = null; for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); if ( name.equals( "when" ) ) { when = value; this.log( element, name, value ); } else { this.complain( element, name, value ); } } if( when == null ){ this.complain( element, "when", when ); return null; } return new Invoke( WHEN.valueOf( when ) ); }//end of element invoke return null; //j job case 'j': if( element.equals( "job" ) ){ Job j = new Job( ); //all jobs in the DAX are of type compute j.setUniverse( GridGateway.JOB_TYPE.compute.toString() ); j.setJobType( Job.COMPUTE_JOB ); for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); if ( name.equals( "namespace" ) ) { j.setTXNamespace( value ); } else if( name.equals( "name" ) ){ j.setTXName( value ); } else if( name.equals( "version" ) ){ j.setTXVersion( value ); } else if( name.equals( "id" ) ){ j.setLogicalID( value ); } else if( name.equals( "node-label" ) ){ j.setNodeLabel( value ); } else { this.complain( element, name, value ); } } //set the internal primary id for job j.setName( constructJobID( j ) ); return j; }//end of element job return null;//end of j //m metadata case 'm': if( element.equals( "metadata" ) ){ Profile p = new Profile(); p.setProfileNamespace( "metadata" ); for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); if ( name.equals( "namespace" ) ) { p.setProfileNamespace( value ); this.log( element, name, value ); } else if ( name.equals( "key" ) ) { p.setProfileKey( value ); this.log( element, name, value ); } else { this.complain( element, name, value ); } } return p; }//end of element metadata return null;//end of case m //p parent profile pfn case 'p': if( element.equals( "parent" ) ){ String parent = null; for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); if ( name.equals( "ref" ) ) { parent = value; } else if( name.equals( "edge-label" ) ){ this.attributeNotSupported( "parent", "edge-label", value); } else { this.complain( element, name, value ); } } if( parent == null ){ this.complain( element, "parent", parent ); return null; } return parent; } else if( element.equals( "profile" ) ){ Profile p = new Profile(); for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); if ( name.equals( "namespace" ) ) { p.setProfileNamespace( value.toLowerCase() ); this.log( element, name, value ); } else if ( name.equals( "key" ) ) { p.setProfileKey( value ); this.log( element, name, value ); } else { this.complain( element, name, value ); } } return p; }//end of element profile else if( element.equals( "pfn" ) ){ String url = null; String site = UNDEFINED_SITE; for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); if ( name.equals( "url" ) ) { url = value; this.log( element, name, value ); } else if ( name.equals( "site" ) ) { site = value; this.log( element, name, value ); } else { this.complain( element, name, value ); } } if( url == null ){ this.complain( element, "url", url ); return null; } PFN pfn = new PFN( url, site ); return pfn; }//end of element pfn return null;//end of case p //s stdin stdout stderr case 's': if( element.equals( "stdin" ) || element.equals( "stdout" ) || element.equals( "stderr") ){ //we use DAX API File object for this String fileName = null; for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); if ( name.equals( "name" ) ) { fileName = value; this.log( element, name, value ); } else if ( name.equals( "link" ) ) { //we ignore as linkage is fixed for stdout|stderr|stdin this.log( element, name, value ); } else { this.complain( element, name, value ); } } if( fileName == null ){ this.complain( element, "name", fileName ); return null; } return new edu.isi.pegasus.planner.dax.File( fileName ); }//end of stdin|stdout|stderr return null;//end of case s //t transformation case 't': if( element.equals( "transformation" ) ){ String namespace = null,lname = null, version = null; for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); if ( name.equals( "namespace" ) ) { namespace = value; } else if( name.equals( "name" ) ){ lname = value; } else if( name.equals( "version" ) ){ version = value; } } return new CompoundTransformation( namespace, lname, version ); } return null; //u uses case 'u': if( element.equals( "uses" ) ){ PegasusFile pf = new PegasusFile( ); String fName = null; String fNamespace = null; String fVersion = null; for ( int i=0; i < names.size(); ++i ) { String name = (String) names.get( i ); String value = (String) values.get( i ); /* * Name Type Use Default Fixed name xs:string required link LinkageType optional optional xs:boolean optional false register xs:boolean optional true transfer TransferType optional true namespace xs:string optional version VersionPattern optional exectuable xs:boolean optional false */ if ( name.equals( "name" ) ) { pf.setLFN( value ); fName = value; this.log( element, name, value ); } else if ( name.equals( "link" ) ) { if( value != null && value.equals( PegasusFile.CHECKPOINT_TYPE) ){ //introduced in dax 3.5 //cleaner for DAX API to have checkpoint files marked //via linkage. Planner still treats it as a type pf.setType( value ); pf.setLinkage(LINKAGE.INOUT); this.log( element, name, value ); } else{ pf.setLinkage( PegasusFile.LINKAGE.valueOf( value.toUpperCase() ) ); this.log( element, name, value ); } } else if ( name.equals( "optional" ) ) { Boolean bValue = Boolean.parseBoolean( value ); if( bValue ){ pf.setFileOptional(); } this.log( element, name, value ); } else if( name.equals( "register") ){ Boolean bValue = Boolean.parseBoolean( value ); pf.setRegisterFlag( bValue ); } else if ( name.equals( "transfer" ) ) { pf.setTransferFlag( value ); this.log( element, name, value ); } else if ( name.equals( "namespace" ) ) { fNamespace = value; this.log( element, name, value ); } else if ( name.equals( "version" ) ) { fVersion = value; this.log( element, name, value ); } else if ( name.equals( "executable" ) ) { Boolean bValue = Boolean.parseBoolean( value ); if( bValue ){ pf.setType( PegasusFile.EXECUTABLE_FILE ); } this.log( element, name, value ); } else if ( name.equals( "size" ) ) { pf.setSize( value ); this.log( element, name, value ); } else { this.complain( element, name, value ); } } //if executable then update lfn to combo of namespace,name,version if( pf.getType() == PegasusFile.EXECUTABLE_FILE ){ pf.setLFN( Separator.combine(fNamespace, fName, fVersion) ); } return pf; }//end of uses return null;//end of case u default: return null; }//end of switch statement } /** * This method sets the relations between the currently finished XML * element(child) and its containing element in terms of Java objects. * Usually it involves adding the object to the parent's child object * list. * * @param childElement name is the the child element name * @param parent is a reference to the parent's Java object * @param child is the completed child object to connect to the parent * * @return true if the element was added successfully, false, if the * child does not match into the parent. */ public boolean setElementRelation( String childElement, Object parent, Object child ){ switch ( childElement.charAt( 0 ) ) { //a argument adag case 'a': if( child instanceof Arguments ){ Arguments a = (Arguments)child; a.addArgument( mTextContent.toString() ); if( parent instanceof Job ){ //argument appears in job element Job j = (Job)parent; j.setArguments( a.toString() ); return true; } } else if( child instanceof Map && parent == null){ //end of parsing reached mLogger.log( "End of last element </adag> reached ", LogManager.DEBUG_MESSAGE_LEVEL ); this.mCallback.cbDone(); return true; } return false; //c child case 'c': if( parent instanceof Map ){ if( child instanceof PCRelation ){ PCRelation pc = (PCRelation)child; //call the callback this.mCallback.cbParents( pc.getChild(), mParents); return true; } } return false; //d dax dag case 'd': if( parent instanceof Map ){ if( child instanceof DAGJob ){ //dag appears in adag element DAGJob dagJob = ( DAGJob )child; //call the callback function this.mCallback.cbJob(dagJob); return true; } else if( child instanceof DAXJob ){ //dag appears in adag element DAXJob daxJob = ( DAXJob )child; //call the callback function this.mCallback.cbJob( daxJob ); return true; } } return false; //f file case 'f': if( child instanceof ReplicaLocation ){ ReplicaLocation rl = ( ReplicaLocation )child; if( parent instanceof Map ){ //file appears in adag element // this.mReplicaStore.add( rl ); this.mCallback.cbFile( rl ); return true; } else if( parent instanceof Arguments ){ //file appears in the argument element Arguments a = (Arguments)parent; a.addArgument( mTextContent.toString() ); a.addArgument( rl ); return true; } } return false; //e executable case 'e': if( child instanceof Executable ){ if( parent instanceof Map ){ //executable appears in adag element Executable exec = (Executable)child; List<TransformationCatalogEntry> tceList = convertExecutableToTCE(exec); for(TransformationCatalogEntry tce : tceList){ this.mCallback.cbExecutable(Abstract.modifyForFileURLS(tce)); } //moved the callback call to end of pfn //each new pfn is a new transformation //catalog entry //this.mCallback.cbExecutable( tce ); return true; } } return false; //i invoke case 'i': if( child instanceof Invoke ){ Invoke i = (Invoke)child; i.setWhat( mTextContent.toString().trim() ); if( parent instanceof Map ){ this.mCallback.cbWfInvoke(i); return true; } else if(parent instanceof DAXJob ){ //invoke appears in dax element DAXJob daxJob = (DAXJob)parent; daxJob.addNotification(i); return true; }else if(parent instanceof DAGJob ){ //invoke appears in dag element DAGJob dagJob = (DAGJob)parent; dagJob.addNotification(i); return true; }else if( parent instanceof Job ){ //invoke appears in job element Job job = (Job)parent; job.addNotification(i); return true; }else if(parent instanceof Executable ){ //invoke appears in executable element Executable exec = (Executable)parent; exec.addInvoke(i); return true; }else if(parent instanceof CompoundTransformation ){ //invoke appears in transformation element CompoundTransformation ct = (CompoundTransformation)parent; ct.addNotification(i); return true; } } return false; //j job case 'j': if( child instanceof Job && parent instanceof Map ){ //callback for Job this.mCallback.cbJob( (Job)child ); return true; } return false; //m metadata case 'm': if ( child instanceof Profile ) { Profile md = ( Profile )child; md.setProfileValue( mTextContent.toString().trim() ); if( parent instanceof Map ){ //metadata appears in adag element this.mCallback.cbMetadata(md); return true; } else if ( parent instanceof Job ){ //metadata appears in the job element Job j = (Job)parent; j.addProfile( md ); return true; } else if( parent instanceof ReplicaLocation ){ //metadata appears in file element ReplicaLocation rl = (ReplicaLocation) parent; rl.addMetadata( md.getProfileKey(), md.getProfileValue()); return true; } else if( parent instanceof Executable ){ //metadata appears in executable element Executable e = (Executable)parent; e.addMetaData( new MetaData(md.getProfileKey(), md.getProfileValue())); return true; } else if( parent instanceof PegasusFile ){ //metadata appears in uses element PegasusFile pf = (PegasusFile)parent; pf.addMetadata( md.getProfileKey(), md.getProfileValue()); return true; } } return false; //p parent profile pfn case 'p': if( parent instanceof PCRelation ){ if( child instanceof String ){ //parent appears in child element String parentNode = ( String )child; PCRelation pc = (PCRelation) (( PCRelation )parent).clone(); pc.setParent( parentNode ); mParents.add( pc ); return true; } } else if ( child instanceof Profile ){ Profile p = ( Profile ) child; p.setProfileValue( mTextContent.toString().trim() ); mLogger.log( "Set Profile Value to " + p.getProfileValue(), LogManager.TRACE_MESSAGE_LEVEL ); if ( parent instanceof ReplicaLocation ) { //profile appears in file element unSupportedNestingOfElements( "file", "profile" ); return true; } else if ( parent instanceof Executable ) { //profile appears in executable element Executable exec = ( Executable)parent; exec.addProfiles(new edu.isi.pegasus.planner.dax.Profile(p.getProfileNamespace(),p.getProfileKey(),p.getProfileValue())); return true; } else if ( parent instanceof Job ){ //profile appears in the job element Job j = (Job)parent; j.addProfile( p ); return true; } } else if( child instanceof PFN ){ if ( parent instanceof ReplicaLocation ) { //pfn appears in file element ReplicaLocation rl = ( ReplicaLocation )parent; PFN pfn = ( PFN )child; rl.addPFN( pfn ); return true; } else if ( parent instanceof Executable){ //pfn appears in executable element Executable executable = (Executable)parent; PFN pfn = ( PFN )child; //tce.setResourceId( pfn.getSite() ); //tce.setPhysicalTransformation( pfn.getURL() ); executable.addPhysicalFile(pfn); //convert file url appropriately for installed executables //before returning //this.mCallback.cbExecutable( Abstract.modifyForFileURLS(tce) ); return true; } } return false; //s stdin stdout stderr case 's': if( parent instanceof Job ){ Job j = ( Job )parent; if( child instanceof edu.isi.pegasus.planner.dax.File ){ //stdin stdout stderr appear in job element edu.isi.pegasus.planner.dax.File f = ( edu.isi.pegasus.planner.dax.File )child; if( childElement.equals( "stdin" ) ){ j.setStdIn( f.getName() ); return true; } else if( childElement.equals( "stdout" ) ){ j.setStdOut( f.getName() ); return true; } if( childElement.equals( "stderr" ) ){ j.setStdErr( f.getName() ); return true; } } } return false; //t transformation case 't': if( parent instanceof Map ){ if( child instanceof CompoundTransformation ){ this.mCallback.cbCompoundTransformation( (CompoundTransformation)child ); return true; } return true; } return false; //u uses case 'u': if( child instanceof PegasusFile ){ PegasusFile pf = ( PegasusFile )child; if( parent instanceof Job ){ //uses appears in job Job j = ( Job )parent; if( pf.getLinkage().equals( LINKAGE.INPUT ) ){ j.addInputFile(pf); } else if( pf.getLinkage().equals( LINKAGE.OUTPUT ) ){ j.addOutputFile(pf); } else if( pf.getLinkage().equals( LINKAGE.INOUT ) ){ j.addInputFile(pf); j.addOutputFile(pf); } return true; } else if( parent instanceof CompoundTransformation ){ CompoundTransformation compound = (CompoundTransformation)parent; compound.addDependantFile( pf ); return true; } } return false; //default case default: return false; } } /** * Converts the executable into transformation catalog entries * @param executable executable object * @return transformation catalog entries */ public List<TransformationCatalogEntry> convertExecutableToTCE(Executable executable){ List<TransformationCatalogEntry> tceList = new ArrayList <TransformationCatalogEntry> (); TransformationCatalogEntry tce = null; for(PFN pfn : executable.getPhysicalFiles()){ tce = new TransformationCatalogEntry(executable.getNamespace(), executable.getName(), executable.getVersion()); SysInfo sysinfo = new SysInfo(); sysinfo.setArchitecture( SysInfo.Architecture.valueOf( executable.getArchitecture().toString().toLowerCase() ) ); sysinfo.setOS( SysInfo.OS.valueOf( executable.getOS().toString().toLowerCase() ) ); sysinfo.setOSRelease( executable.getOsRelease() ); sysinfo.setOSVersion( executable.getOsVersion() ); sysinfo.setGlibc( executable.getGlibc() ); tce.setSysInfo(sysinfo); tce.setType( executable.getInstalled() ? TCType.INSTALLED : TCType.STAGEABLE ); tce.setResourceId( pfn.getSite() ); tce.setPhysicalTransformation( pfn.getURL() ); Notifications notifications = new Notifications(); for(Invoke invoke : executable.getInvoke()){ notifications.add( new Invoke(invoke) ); } tce.addNotifications(notifications); for(edu.isi.pegasus.planner.dax.Profile profile : executable.getProfiles()){ tce.addProfile(new edu.isi.pegasus.planner.classes.Profile(profile.getNameSpace(),profile.getKey() , profile.getValue())); } for( MetaData md: executable.getMetaData()){ //convert to metadata profile object for planner to use tce.addProfile( new Profile( Profile.METADATA, md.getKey(), md.getValue() )); } tceList.add(tce); } return tceList; } /** * Returns the id for a job * * @param j the job * * @return the id. */ protected String constructJobID( Job j ){ //construct the jobname/primary key for job StringBuffer name = new StringBuffer(); //prepend a job prefix to job if required if (mJobPrefix != null) { name.append(mJobPrefix); } //append the name and id recevied from dax name.append(j.getTXName()); name.append("_"); name.append(j.getLogicalID()); return name.toString(); } /** * Sanity check on the version that this parser works on. * * @param version the version as specified in the DAX */ protected void sanityCheckOnVersion( String version ) { if( version == null ){ mLogger.log( "Version not specified in the adag element " , LogManager.WARNING_MESSAGE_LEVEL ); return ; } //add a 0 suffix String nversion = version + ".0"; long value = CondorVersion.numericValue( nversion) ; if( value < DAXParser3.DAX_VERSION_3_2_0 ){ StringBuffer sb = new StringBuffer(); sb.append( "DAXParser3 Unsupported DAX Version " ).append( version ). append( ". Set pegasus.schema.dax property to load the old DAXParser" ); throw new RuntimeException( sb.toString() ); } //also complain for parsing documents that have version higher if( value > DAXParser3.DAX_VERSION_3_6_0 ){ throw new RuntimeException( "DAXParser3 cannot parse documents conforming to DAX version " + version ); } return; } /** * Private class to handle mix data content for arguments tags. * */ private class Arguments{ /** * Handle to a job arguments to handle mixed content. */ protected StringBuffer mBuffer; /** * The default constructor */ public Arguments(){ reset(); } /** * Resets the internal buffer */ public void reset() { mBuffer = new StringBuffer(); } /** * Adds text to the arguments string * * @param text the text to be added. */ public void addArgument( String text ){ mBuffer.append( text ); } /** * Adds filename to the arguments * * @param rl the ReplicaLocation object */ public void addArgument(ReplicaLocation rl) { mBuffer.append( rl.getLFN() ); } /** * Our own implementation for ignorable whitespace. A String that holds the * contents of data passed as text by the underlying parser. The whitespaces * at the end are replaced by one whitespace. * * @param str The string that contains whitespaces. * * @return String corresponding to the trimmed version. * */ public String ignoreWhitespace(String str) { return ignoreWhitespace(str, mProps.preserveParserLineBreaks()); } /** * Our own implementation for ignorable whitespace. A String that holds the * contents of data passed as text by the underlying parser. The whitespaces * at the end are replaced by one whitespace. * * @param str The string that contains whitespaces. * * @return String corresponding to the trimmed version. * */ public String ignoreWhitespace(String str, boolean preserveLineBreak ){ boolean st = false; boolean end = false; int length = str.length(); boolean sN = false;//start with \n ; boolean eN = false;//end with \n if(length > 0){ sN = str.charAt(0) == '\n'; eN = str.charAt(length -1) == '\n'; //check for whitespace in the //starting if(str.charAt(0) == ' ' || str.charAt(0) == '\t' || str.charAt(0) == '\n'){ st = true; } //check for whitespace in the end if(str.length() > 1 && (str.charAt(length -1) == ' ' || str.charAt(length -1) == '\t' || str.charAt(length -1) == '\n')){ end = true; } //trim the string and add a single whitespace accordingly str = str.trim(); str = st == true ? ' ' + str:str; str = end == true ? str + ' ':str; if( preserveLineBreak ){ str = sN ? '\n' + str:str; str = eN ? str + '\n':str; } } return str; } /** * Returns the arguments as string * * @return the arguments */ public String toString(){ return this.ignoreWhitespace( mBuffer.toString() ); } } /** * * @param args */ public static void main( String[] args ){ LogManagerFactory.loadSingletonInstance().setLevel( 5 ); /*DAXParser3 parser = new DAXParser3( ); if (args.length == 1) { parser.startParser( args[0] ); } else { System.out.println("Usage: SiteCatalogParser <input site catalog xml file>"); }*/ } }