/**
* 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;
import edu.isi.pegasus.planner.parser.dax.*;
import edu.isi.pegasus.common.logging.LogManager;
import edu.isi.pegasus.common.util.CondorVersion;
import edu.isi.pegasus.planner.common.PegasusProperties;
import edu.isi.pegasus.common.util.DynamicLoader;
import edu.isi.pegasus.planner.classes.PegasusBag;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
/**
* A factory class to load the appropriate DAX Parser and Callback implementations that need
* to be passed to the DAX Parser.
*
* @author Karan Vahi
* @version $Revision$
*/
public class DAXParserFactory {
/**
* The default callback for label partitioning.
*/
public static String LABEL_CALLBACK_CLASS = "DAX2LabelGraph";
/**
* Package to prefix "just" class names with.
*/
public static final String DEFAULT_PARSER_PACKAGE_NAME =
"edu.isi.pegasus.planner.parser.dax";
/**
* Package to prefix "just" class names with.
*/
public static final String DEFAULT_CALLBACK_PACKAGE_NAME =
"edu.isi.pegasus.planner.parser.dax";
/*
* Predefined Constant for DAX version 3.2.0
*/
public static final long DAX_VERSION_3_2_0 = CondorVersion.numericValue( "3.2.0" );
/**
* The default DAXParser classname
*/
public static final String DEFAULT_DAX_PARSER_CLASS = "DAXParser3";
/**
* The DAXParser3 classname
*/
public static final String DAX_PARSER2_CLASS = "DAXParser2";
/**
* The DAXParser3 classname
*/
public static final String DAX_PARSER3_CLASS = "DAXParser3";
/**
* Loads the appropriate DAXParser looking at the dax schema that is specified by
* the user.
*
* @param bag bag of Pegasus intialization objects
* @param callbackClass the dax callback class
* @param dax file the dax file
*
* @return the DAXParser loaded.
*
* @exception DAXParserFactoryException that nests any error that
* might occur during the instantiation
*
* @see #DEFAULT_CALLBACK_PACKAGE_NAME
*/
public static DAXParser loadDAXParser( PegasusBag bag, String callbackClass, String daxFile ) throws DAXParserFactoryException{
PegasusProperties properties = bag.getPegasusProperties();
//sanity check
if( properties == null){
throw new RuntimeException("Invalid properties passed");
}
//load the callback
Callback c = DAXParserFactory.loadDAXParserCallback( bag, daxFile, callbackClass );
return DAXParserFactory.loadDAXParser( bag, c, daxFile );
}
/**
* Loads the appropriate DAXParser looking at the dax schema that is specified
* in the DAX file.
*
* @param bag bag of Pegasus intialization objects
* @param c the dax callback.
* @param daxFile the dax file to parser
*
* @return the DAXParser loaded.
*
* @exception DAXParserFactoryException that nests any error that
* might occur during the instantiation
*
* @see #DEFAULT_CALLBACK_PACKAGE_NAME
*/
public static DAXParser loadDAXParser( PegasusBag bag, Callback c , String daxFile ) throws DAXParserFactoryException{
String daxClass = DAXParserFactory.DEFAULT_DAX_PARSER_CLASS;
LogManager logger = bag.getLogger();
PegasusProperties properties = bag.getPegasusProperties();
String daxSchema = properties.getDAXSchemaLocation();
//sanity check
if( properties == null){
throw new RuntimeException("Invalid properties passed");
}
if( logger == null){
throw new RuntimeException("Invalid logger passed");
}
//try to figure out the schema version by parsing the dax file
String schemaVersion = null;
try{
if( daxFile != null && !daxFile.isEmpty() ){
Map m = getDAXMetadata( bag, daxFile );
if( m.containsKey( "version" ) && (schemaVersion = (String)m.get( "version" )) != null ){
logger.log( "DAX Version as determined from DAX file " + schemaVersion,
LogManager.DEBUG_MESSAGE_LEVEL );
}
}
//try to figure out the schema from the schema in properties
//in case unable to determine from the dax file
if( schemaVersion == null && daxSchema != null ){
//try to determin the version of dax schema
daxSchema = new File( daxSchema ).getName();
if( daxSchema.startsWith( "dax-" ) && daxSchema.endsWith( ".xsd" ) ){
schemaVersion = daxSchema.substring( daxSchema.indexOf( "dax-" ) + 4,
daxSchema.lastIndexOf(".xsd") );
logger.log( "DAX Version as determined from schema property " + schemaVersion,
LogManager.DEBUG_MESSAGE_LEVEL );
}
}
if( schemaVersion == null ){
throw new DAXParserFactoryException( "Unable to determine the DAX version from the DAX " + daxFile );
}
//append .0 to the version number
//to be able to convert to numberic value
if( CondorVersion.numericValue( schemaVersion + ".0" ) < DAXParserFactory.DAX_VERSION_3_2_0 ){
daxClass = DAXParserFactory.DAX_PARSER2_CLASS;
}
else{
daxClass = DAXParserFactory.DAX_PARSER3_CLASS;
}
}
catch( Exception e ){
logger.log( "Problem while determining the version of dax" , e,
LogManager.ERROR_MESSAGE_LEVEL );
}
logger.log( "DAX Parser Class to be loaded is " + daxClass,
LogManager.CONFIG_MESSAGE_LEVEL );
return loadDAXParser( daxClass, schemaVersion, bag, c );
}
/**
* Loads the appropriate DAXParser looking at the dax schema that is specified by
* the user.
*
* @param classname the classname of the parser class that needs to be loaded
* @param schemaVersion the schema version as determined from the DAX
* @param bag bag of Pegasus intialization objects
* @param c the DAX Callback to use
*
* @return the DAXParser loaded.
*
* @exception DAXParserFactoryException that nests any error that
* might occur during the instantiation
*
* @see #DEFAULT_CALLBACK_PACKAGE_NAME
*/
private static final DAXParser loadDAXParser( String classname, String schemaVersion, PegasusBag bag, Callback c ){
DAXParser daxParser = null;
try{
//load the DAX Parser class
//prepend the package name
String daxClass = ( classname.indexOf('.') == -1)?
//pick up from the default package
DEFAULT_PARSER_PACKAGE_NAME + "." + classname:
//load directly
classname;
DynamicLoader dl = new DynamicLoader( daxClass );
Object argList[] = new Object[2];
argList[0] = bag;
argList[1] = schemaVersion;
daxParser = (DAXParser)dl.instantiate( argList );
//set the callback for the DAX Parser
((DAXParser)daxParser).setDAXCallback( c );
}
catch(Exception e){
throw new DAXParserFactoryException( "Instantiating DAXParser ",
classname, e);
}
return daxParser;
}
/**
* Loads the implementing class corresponding to the type specified by the user.
* The properties object passed should not be null. The callback that is
* loaded, is the one referred to in the properties by the user, unless the
* type of partitioning is label. In that case DAX2LabelGraph is loaded always.
*
* @param properties the <code>PegasusProperties</code> object containing all
* the properties required by Pegasus.
* @param type the type of partitioning the user specified.
* @param dax the path to the DAX file that has to be parsed.
*
* @return the instance of the class implementing this interface.
*
* @exception DAXParserFactoryException that nests any error that
* might occur during the instantiation
*
* @see #DEFAULT_CALLBACK_PACKAGE_NAME
* @see edu.isi.pegasus.planner.common.PegasusProperties#getPartitionerDAXCallback()
*/
public static Callback loadDAXParserCallback( String type,
PegasusBag bag,
String dax )
throws DAXParserFactoryException{
String callbackClass = null;
PegasusProperties properties = bag.getPegasusProperties();
//for type label always load DAX2LabelGraph
if ( type.equalsIgnoreCase("label") ){
callbackClass = LABEL_CALLBACK_CLASS; //graph with labels populated
}else{
//pick up the value passed in properties
callbackClass = properties.getPartitionerDAXCallback();
}
return loadDAXParserCallback( bag, dax, callbackClass );
}
/**
* Returns the metadata stored in the root adag element in the DAX
*
* @param bag the bag of initialization objects
* @param dax the dax file.
*
* @return Map containing the metadata, else an empty map
*/
public static Map getDAXMetadata( PegasusBag bag, String dax ){
Callback cb = DAXParserFactory.loadDAXParserCallback( bag, dax, "DAX2Metadata" );
LogManager logger = bag.getLogger();
if( logger != null ){
logger.log( "Retrieving Metadata from the DAX file " + dax ,
LogManager.DEBUG_MESSAGE_LEVEL );
}
try{
Parser p = (Parser)DAXParserFactory.loadDAXParser( DAXParserFactory.DAX_PARSER2_CLASS,
"2.0",
bag,
cb );
//while determining the metadata we are just parsing adag element
//we want the parser validation to be turned off.
p.setParserFeature("http://xml.org/sax/features/validation", false);
p.setParserFeature("http://apache.org/xml/features/validation/schema", false);
p.startParser( dax );
}
catch( RuntimeException e ){
//check explicity for file not found exception
if( e.getCause() != null && e.getCause() instanceof java.io.IOException){
//rethrow
throw e;
}
}
Map result = ( Map ) cb.getConstructedObject();
return ( result == null ) ? new HashMap() : result;
}
/**
* Loads the implementing class corresponding to the type specified by the user.
* The properties object passed should not be null. The callback that is
* loaded, is the one referred to by the className parameter passed.
*
* @param bag the bag of initialization objects containing the logger
* and the properties handler
* @param dax the path to the DAX file that has to be parsed.
* @param className the name of the implementing class.
*
* @return the instance of the class implementing this interface.
*
* @exception DAXParserFactoryException that nests any error that
* might occur during the instantiation
*
* @see #DEFAULT_CALLBACK_PACKAGE_NAME
*/
public static Callback loadDAXParserCallback( PegasusBag bag,
String dax,
String className)
throws DAXParserFactoryException{
//try loading the class dynamically
Callback callback = null;
try{
//sanity check
if( bag == null ){
throw new RuntimeException("Invalid PegasusBag passed");
}
PegasusProperties properties= bag.getPegasusProperties();
if(properties == null){
throw new RuntimeException("Invalid properties passed");
}
if(className == null){
throw new RuntimeException("Invalid class specified to load");
}
//prepend the package name
className = (className.indexOf('.') == -1)?
//pick up from the default package
DEFAULT_CALLBACK_PACKAGE_NAME + "." + className:
//load directly
className;
DynamicLoader dl = new DynamicLoader( className);
Object argList[] = new Object[0];
callback = (Callback)dl.instantiate(argList);
callback.initialize( bag, dax);
}
catch(Exception e){
throw new DAXParserFactoryException("Instantiating DAXCallback ",
className, e);
}
return callback;
}
}