/**
* 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.namespace;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import edu.isi.pegasus.planner.classes.Data;
import edu.isi.pegasus.planner.classes.Profile;
import edu.isi.pegasus.common.logging.LogManager;
import edu.isi.pegasus.common.logging.LogManagerFactory;
import edu.isi.pegasus.planner.catalog.classes.Profiles;
import edu.isi.pegasus.planner.common.PegasusProperties;
import edu.isi.pegasus.planner.catalog.transformation.TransformationCatalogEntry;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* The base namespace class that all the othernamepsace handling classes extend.
* Some constants are defined.
*
* @author Karan Vahi
* @author Gaurang Mehta
* @version $Revision$
*/
public abstract class Namespace /*extends Data*/{
/**
* The LogManager object which is used to log all the messages.
*
*/
public LogManager mLogger = LogManagerFactory.loadSingletonInstance( );
/**
* The version number associated with this API of Profile Namespaces.
*/
public static final String VERSION = "1.2";
//constants for whether the key
//is valid in the namespace or not
/**
* Either the key or the value specified is null or malformed.
*/
public static final int MALFORMED_KEY = -1;
/**
* The key is a valid key and can be put in the profiles.
*/
public static final int VALID_KEY = 0;
/**
* The key is unknown. Upto the profile namespace implementation whether to
* use it or not.
*/
public static final int UNKNOWN_KEY = 1;
/**
* The key is not permitted in as it clashes with default Pegasus constructs.
*/
public static final int NOT_PERMITTED_KEY = 2;
/**
* The key is deprecated. Support is for a limited time.
*/
public static final int DEPRECATED_KEY = 3;
/**
* The key value is empty .
*/
public static final int EMPTY_KEY = 4;
/**
* The key value is valid but contents should be merged to existing value if
* it exists
*/
public static final int MERGE_KEY = 5;
/**
* The Map object that contains the profiles for a particular namespace.
* The Map is indexed by profile key. Each value, is a profile value.
*/
@Expose @SerializedName( "profiles" )
protected Map mProfileMap;
/**
* Checks if the namespace specified is valid or not.
*
* @param namespace The namespace you want to check
*/
public static boolean isNamespaceValid(String namespace){
return Profile.namespaceValid( namespace );
}
/**
* This checks the whether a key value pair specified is valid in the current
* namespace or not, and whether it clashes with other key value pairs that
* might have been set by Pegasus internally.
*
* @return MALFORMED_KEY
* VALID_KEY
* UNKNOWN_KEY
* NOT_PERMITTED_KEY
*/
public abstract int checkKey(String key, String value);
/**
* Merge the profiles in the namespace in a controlled manner.
* The profiles should be merged only if the namespace object containing them
* matches to the current namespace.
*
* @param profiles the <code>Namespace</code> object containing the profiles.
*/
public abstract void merge( Namespace profiles );
/**
* Returns the name of the namespace associated with the profile implementations.
*
* @return the namespace name.
*/
public abstract String namespaceName();
/**
* Returns the contents as String. Currently, it returns condor compatible
* string that can be put in the condor submit file
*
* @return textual description
*/
public String toString(){
return this.toCondor();
}
/**
* Returns a condor description that can be used to put the contents of the
* namespace into the condor submit file during code generation.
*
* @return String
*/
public abstract String toCondor();
/**
* Provides an iterator to traverse the profiles by their keys.
*
* @return an iterator over the keys to walk the profile list.
*/
public Iterator getProfileKeyIterator()
{
return ( mProfileMap == null )? new EmptyIterator() : this.mProfileMap.keySet().iterator();
}
/**
* Singleton access to the deprecated table that holds the deprecated keys,
* and the keys that replace them. It should be overriden in the namespaces,
* that have deprecated keys.
*
* @return Map
*/
public Map deprecatedTable() {
throw new UnsupportedOperationException("No Deprecation support in the namespace " +
namespaceName());
}
/**
* It puts in the namespaces keys from another namespace instance.
*
* @param nm the namespace to be assimilated
*/
public void checkKeyInNS( Namespace nm ) {
if( !nm.namespaceName().equals( this.namespaceName() ) ){
//mismatch of namespaces
throw new RuntimeException( "Mismatch of namespaces " + this.namespaceName() + " " + nm.namespaceName() );
}
for( Iterator it = nm.getProfileKeyIterator(); it.hasNext() ; ){
String key = ( String )it.next();
this.checkKeyInNS( key, (String) nm.get(key) );
}
}
/**
* It puts in the namespace specific information from the Transformation
* Catalog into the namespace.
*
* @param entry the <code>TCEntry</code> object containing the result from
* the Transformation Catalog.
*/
public void checkKeyInNS(TransformationCatalogEntry entry){
//sanity check
if(entry == null ) {
return;
}
//pass down the list of Profile objects to be sucked in.
checkKeyInNS(entry.getProfiles(this.namespaceName()));
}
/**
* It takes in a Profiles object and puts them into the namespace after
* checking if the namespace in the Profile object is same as the namepsace
* implementation.
*
* @param profile the <code>Profile</code> object containing the key and
* value.
*
* @exception IllegalArgumentException if the namespace in the profile
* is not the same as the profile namepsace in which the profile
* is being incorporated.
*
* @see org.griphyn.cPlanner.classes.Profile
*/
public void checkKeyInNS(Profile profile) throws IllegalArgumentException{
if(profile.getProfileNamespace().equals(this.namespaceName())){
checkKeyInNS(profile.getProfileKey(),
profile.getProfileValue());
}
else{
//throw an exception for the illegal Profile Argument
throw new IllegalArgumentException("Illegal Profile " + profile);
}
}
/**
* It takes in a list of profiles and puts them into the namespace after
* checking if they are valid or not. Note, there are no checks on the
* namespace however. The user should ensure that each Profile object in
* the list is of the same namespace type.
*
* @param vars List of <code>Profile</code> objects, each referring
* to a key value for the profile.
*
*
* @see org.griphyn.cPlanner.classes.Profile
*/
public void checkKeyInNS(List vars){
if(vars == null || vars.isEmpty()){
//no variables to insert
return;
}
Profile p = null;
for( Iterator it = vars.iterator(); it.hasNext(); ){
p = (Profile)it.next();
checkKeyInNS(p.getProfileKey(),p.getProfileValue());
}
}
/**
* It puts in the namespace specific information specified in the properties
* file into the namespace. The name of the pool is also passed, as many of
* the properties specified in the properties file are on a per pool basis.
*
* @param properties the <code>PegasusProperties</code> object containing
* all the properties that the user specified at various
* places (like .chimerarc, properties file, command line).
* @param pool the pool name where the job is scheduled to run.
*/
public abstract void checkKeyInNS(PegasusProperties properties, String pool);
/**
* This checks the whether a key value pair specified is valid in the current
* namespace or not by calling the checkKey function and then on the basis of
* the values returned puts them into the associated map in the class.
*
* @param key key that needs to be checked in the namespace for validity.
* @param value value of the key
*
*/
public void checkKeyInNS(String key, String value){
int action = checkKey(key,value);
switch (action){
case Namespace.MALFORMED_KEY:
//key is malformed ignore
malformedKey(key,value);
break;
case Namespace.NOT_PERMITTED_KEY:
notPermitted(key);
break;
case Namespace.UNKNOWN_KEY:
unknownKey(key, value);
break;
case Namespace.VALID_KEY:
construct(key, value);
break;
case Namespace.DEPRECATED_KEY:
deprecatedKey(key,value);
break;
case Namespace.EMPTY_KEY:
emptyKey( key );
break;
case Namespace.MERGE_KEY:
mergeKey( key , value );
break;
default:
throw new RuntimeException( "Invalid return type for checkKey " + key + " " + value );
}
}
/**
* Assimilate the profiles in the namespace in a controlled manner.
* In case of intersection, the new profile value overrides, the existing
* profile value.
*
* @param profiles the <code>Namespace</code> object containing the profiles.
* @param namespace the namespace for which the profiles need to be assimilated.
*/
public void assimilate( PegasusProperties properties, Profiles.NAMESPACES namespace ){
Namespace profiles = properties.getProfiles( namespace ) ;
for ( Iterator it = profiles.getProfileKeyIterator(); it.hasNext(); ){
String key = (String)it.next();
//profiles assimilated from properties have lowest priority
if( !this.containsKey(key) ){
this.checkKeyInNS( key, (String)profiles.get( key ) );
}
}
}
/**
* Returns true if the namespace contains a mapping for the specified key.
* More formally, returns true if and only if this map contains at a mapping
* for a key k such that (key==null ? k==null : key.equals(k)).
* (There can be at most one such mapping.)
*
* @param key The key that you want to search for
* in the namespace.
*/
public boolean containsKey(Object key){
return (mProfileMap == null)?
false:
mProfileMap.containsKey(key);
}
/**
* Constructs a new element of the format (key=value).
*
* @param key is the left-hand-side
* @param value is the right hand side
*/
public void construct(String key, String value) {
mProfileMap.put(key, value);
}
/**
* Removes the key from the namespace.
*
* @param key The key you want to remove.
*
* @return the value object if it exists.
* null if the key does not exist in the namespace.
*/
public Object removeKey(Object key){
return mProfileMap.remove(key);
}
/**
* Returns the key set associated with the namespace.
*
* @return key set if the mProfileMap is populated.
* null if the associated mProfileMap is not populated.
*/
public Set keySet(){
return (mProfileMap == null) ? null: mProfileMap.keySet();
}
/**
* Returns the number of profiles in the namespace
*
* @return the count of keys
*/
public int size(){
return (mProfileMap == null) ? 0: mProfileMap.keySet().size();
}
/**
* Returns a boolean indicating if the object is empty.
*
* The object is empty if the underlying map's key set is empty.
*
* @return
*/
public boolean isEmpty(){
return ( mProfileMap == null )? true : mProfileMap.keySet().isEmpty();
}
/**
* Returns the value to which this namespace maps the specified key.
* Returns null if the map contains no mapping for this key. A return value
* of null does not necessarily indicate that the map contains no mapping for
* the key; it's also possible that the map explicitly maps the key to null.
* The containsKey operation may be used to distinguish these two cases.
*
* @param key The key whose value you want.
*
*/
public Object get(Object key){
return mProfileMap.get(key);
}
/**
* Returns a int value, that a particular key is mapped to in this
* namespace. If the key is mapped to a non int
* value or the key is not populated in the namespace , then default value is
* returned.
*
* @param key The key whose boolean value you desire.
*
* @return boolean
*/
public int getIntValue(Object key, int deflt ){
int value = deflt;
if(mProfileMap != null && mProfileMap.containsKey(key)){
try{
value = Integer.parseInt( (String) mProfileMap.get(key));
}catch( Exception e ){
}
}
return value;
}
/**
* Returns a long value, that a particular key is mapped to in this
* namespace. If the key is mapped to a non long
* value or the key is not populated in the namespace , then default value is
* returned.
*
* @param key The key whose boolean value you desire.
*
* @return long value
*/
public long getLongValue(Object key, long deflt ){
long value = deflt;
if(mProfileMap != null && mProfileMap.containsKey(key)){
try{
value = Long.parseLong( (String) mProfileMap.get(key));
}catch( Exception e ){
}
}
return value;
}
/**
* Warns about an unknown profile key and constructs it anyway.
* Constructs a new RSL element of the format (key=value).
*
* @param key is the left-hand-side
* @param value is the right hand side
*/
public void unknownKey(String key, String value) {
mLogger.log("unknown profile " + namespaceName() + "." + key +
", using anyway", LogManager.WARNING_MESSAGE_LEVEL);
construct(key, value);
}
/**
* Warns about a deprecated profile key. It constructs the corresponding
* replacement key.
*
* @param key is the left-hand-side
* @param value is the right hand side
*
* @see #deprecatedTable()
*/
public void deprecatedKey(String key, String value) {
String replacement = (String)deprecatedTable().get(key);
if(replacement == null){
//no replacement key for the deprecated
//profile! Fatal Internal Error
StringBuffer error = new StringBuffer();
error.append( "No replacement key exists for deprecated profile ").
append( namespaceName() ).append( "." ).append( key );
throw new RuntimeException( error.toString() );
}
mLogger.log(
"profile " + namespaceName() + "." + key +
" is deprecated. Replacing with " + namespaceName() + "." + replacement,
LogManager.WARNING_MESSAGE_LEVEL);
if(containsKey(replacement)){
//replacement key already exists.
//use that ! might break profile overriding ??
}
else{
construct(replacement,value);
}
}
/**
* Warns about a namespace profile key that cannot be permitted.
*
* @param key is the key that induced the warning.
*/
public void notPermitted(String key) {
mLogger.log(
"profile " + namespaceName() + "." + key +
" is not permitted, ignoring!", LogManager.WARNING_MESSAGE_LEVEL);
}
/**
* Deletes the key from the namespace.
*
* @param key the key with empty value
*/
public void emptyKey( String key ) {
mLogger.log(
"profile " + namespaceName() + "." + key +
" is empty, Removing!",
LogManager.WARNING_MESSAGE_LEVEL);
this.removeKey( key );
}
/**
* Warns about a namespace profile key-value pair that is malformed.
*
* @param key is the key that induced the warning.
* @param value is the corresponding value of the key.
*/
public void malformedKey(String key, String value) {
mLogger.log(
"profile " + namespaceName() + "." + key +
" with value " + value + " is malformed, ignoring!",
LogManager.WARNING_MESSAGE_LEVEL);
}
/**
* Merges key value to an existing value if it exists
*
* @param key
* @param value
*/
public void mergeKey(String key, String value) {
throw new UnsupportedOperationException( "Function mergeKey(String,String not supported for namespace " + this.namespaceName());
}
/**
* Resets the namespace, removing all profiles associated
*/
public void reset(){
this.mProfileMap.clear();
}
/**
* Returns the clone of the object.
*
* @return the clone
*/
public Object clone(){
Namespace obj;
try{
obj = ( Namespace ) super.clone();
for( Iterator it = this.getProfileKeyIterator(); it.hasNext(); ){
String key = ( String )it.next();
obj.construct( key, (String)this.get( key ));
}
}
catch( CloneNotSupportedException e ){
//somewhere in the hierarch chain clone is not implemented
throw new RuntimeException("Clone not implemented in the base class of " + this.getClass().getName(),
e );
}
return obj;
}
/**
* An empty iterator that allows me to traverse in case of null objects.
*/
protected class EmptyIterator implements Iterator{
/**
* Always returns false, as an empty iterator.
*
* @return false
*/
public boolean hasNext(){
return false;
}
/**
* Returns a null as we are iterating over nothing.
*
* @return null
*/
public Object next(){
return null;
}
/**
* Returns a false, as no removal
*
*/
public void remove(){
}
}
}