/** * 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.catalog.replica; import edu.isi.pegasus.planner.catalog.classes.CatalogEntry; import java.util.*; /** * The entry is a high-level logical structure representing the physical * filename, the site handle, and optional attributes related to the PFN * as one entity.<p> * * The resource handle is the most frequently used attribute. In * reality, the resource handle may be a relational attribute of the * mapping relation between an LFN and a PFN - there is disagreement * among the developers on this issue. For simplicity purposes, it * appears to be sufficient to make the resource handle a regular PFN * attribute.<p> * * @author Jens-S. Vöckler * @author Karan Vahi * @version $Revision$ */ public class ReplicaCatalogEntry implements CatalogEntry, Cloneable { /** * The (reserved) attribute name used for the resource handle. */ public static final String RESOURCE_HANDLE = "site"; /** * The (reserved) attribute name used for the resource handle. */ public static final String DEPRECATED_RESOURCE_HANDLE = "pool"; /** * The physical filename. */ private String m_pfn; /** * Any optional attributes associated with the PFN. */ private Map m_attributeMap; /** * Default constructor for arrays. The PFN is initialized to * <code>null</null>, and thus must be explicitly set later. The map * of attributes associated with the PFN is initialized to be empty. * Thus, no resource handle is available. */ public ReplicaCatalogEntry() { m_pfn = null; m_attributeMap = new TreeMap(); } /** * Convenience constructor initializes the PFN. The map of attributes * is initialized to be empty. Thus, no resource handle is avaiable. * * @param pfn is the PFN to remember. */ public ReplicaCatalogEntry( String pfn ) { m_pfn = pfn; m_attributeMap = new TreeMap(); } /** * Convenience constructor initializes the PFN and the resource * handle. The resource handle is stored as regular PFN attribute. * * @param pfn is the PFN to remember. * @param handle is the resource handle to remember. */ public ReplicaCatalogEntry( String pfn, String handle ) { m_pfn = pfn; m_attributeMap = new TreeMap(); m_attributeMap.put( RESOURCE_HANDLE, handle ); } /** * Standard constructor initializes the PFN and arbitrary attributes. * * @param pfn is the PFN to remember. * @param attributes is a map of arbitrary attributes related to the * PFN. */ public ReplicaCatalogEntry( String pfn, Map attributes ) { m_pfn = pfn; m_attributeMap = new TreeMap(attributes); this.checkAndUpdateForPoolAttribute(); } /** * Adds an attribute to the set of attributes. Note, this is identical * to the {@link #setAttribute( String, Object )} method of the same * signature. * * @param key is the key denoting an attribute. * @param value is a value object to store. */ public void addAttribute( String key, Object value ) { this.m_attributeMap.put( key, value ); } /** * Adds attributes to the existing attributes. * * @param attributes is a map of attributes to add. * @see #setAttribute(Map) * @see java.util.Map#putAll( Map ) */ public void addAttribute( Map attributes ) { this.m_attributeMap.putAll(attributes); } /** * Obtains the attribute value for a given key. * * @param key is the key to look up * @return the object stored as value, may be null. * @see java.util.Map#get( Object ) */ public Object getAttribute( String key ) { return this.m_attributeMap.get(key); } /** * Checks for the existence of an attribute key. * * @param key is the key to look up * @return true if the key is known, false otherwise. */ public boolean hasAttribute( String key ) { return this.m_attributeMap.containsKey(key); } /** * Counts the number of attributes known for the PFN. * * @return number of attributes, may be zero. * @see java.util.Map#size() */ public int getAttributeCount() { return this.m_attributeMap.size(); } /** * Provides an iterator to traverse the attributes by their keys. * * @return an iterator over the keys to walk the attribute list. */ public Iterator getAttributeIterator() { return this.m_attributeMap.keySet().iterator(); } /** * Merges the attribute maps of two entries in a controlled fashion. * Entries are only merged with another entry, if the physical * filenames match. * * @param a is one replica catalog entry to merge. * @param b is the other replica catalog entry to merge. * @param overwrite resolves intersections. If true, uses rce's * attribute to remain, if false, the original attribute remains. * @return the merged entry, if the PFNs matched, or <code>null</code> * if the PFN mismatched. */ public static ReplicaCatalogEntry merge( ReplicaCatalogEntry a, ReplicaCatalogEntry b, boolean overwrite ) { ReplicaCatalogEntry result = null; String pfn1 = a.getPFN(); String pfn2 = b.getPFN(); if ( pfn1 == null && pfn2 == null || pfn1 != null && pfn2 != null && pfn1.equals(pfn2) ) { result = new ReplicaCatalogEntry( pfn1, a.m_attributeMap ); result.merge( b, overwrite ); // result cannot be false } // will return null on PFN mismatch return result; } /** * Merges the attribute maps in a controlled fashion. An entry is only * merged with another entry, if the physical filenames match. * * @param rce is another replica catalog entry to merge with. * @param overwrite resolves intersections. If true, uses rce's * attribute to remain, if false, the original attribute remains. * @return true if a merge was attempted, false if the PFNs did not * match. */ public boolean merge( ReplicaCatalogEntry rce, boolean overwrite ) { String pfn1 = this.m_pfn; String pfn2 = rce.getPFN(); boolean result = ( pfn1 == null && pfn2 == null || pfn1 != null && pfn2 != null && pfn1.equals(pfn2) ); // only merge if PFN match if ( result ) { String key; Object val; for ( Iterator i=rce.getAttributeIterator(); i.hasNext(); ) { key = (String) i.next(); val = rce.getAttribute(key); if ( hasAttribute(key) ) { if ( overwrite ) setAttribute( key, val ); } else { setAttribute( key, val ); } } } return result; } /** * Removes all attributes associated with a PFN. * @see #removeAttribute( String ) */ public void removeAllAttribute() { this.m_attributeMap.clear(); } /** * Removes a specific attribute. * * @param name is the name of the attribute to remove. * @return the value object that was removed, or <code>null</code>, * if the key was not in the map. * @see #removeAllAttribute() */ public Object removeAttribute( String name ) { return this.m_attributeMap.remove(name); } /** * Adds a new or overwrites an existing attribute. Note, this is * identical to the {@link #addAttribute( String, Object)} method of * the same signature. * * @param key is the name of the attribute * @param value is the value object associated with the attribute. */ public void setAttribute( String key, Object value ) { this.m_attributeMap.put( key, value ); } /** * Replaces all existing attributes with new attributes. Existing * attributes are removed before attempting a shallow copy of the new * attributes. * * @param attributes is the map of new attributes to remember. * @see #addAttribute(Map) */ public void setAttribute( Map attributes ) { this.m_attributeMap.clear(); this.m_attributeMap.putAll(attributes); } /** * Obtains the resource handle from the attributes map. This is a * convenience method. Internally, the PFN attribute map is queried * for the value of the resource handle. * * @return the resource handle, or <code>null</code> if unset. * @see #setResourceHandle( String ) */ public String getResourceHandle() { String site = (String) this.m_attributeMap.get( RESOURCE_HANDLE ); return (site == null )? (String) this.m_attributeMap.get( DEPRECATED_RESOURCE_HANDLE ): site; } /** * Sets a new resource handle to remember as PFN attribute. This is a * convenience method. Internally, the PFN attribute map is changed * to remember the new resource handle. * * @param handle is the new resource handle. * @see #getResourceHandle() */ public void setResourceHandle( String handle ) { this.m_attributeMap.put( RESOURCE_HANDLE, handle ); } /** * Accessor: Obtains the PFN portion from this entry. * * @return the physical filename, or <code>null</code> if unset. * @see #setPFN( String ) */ public String getPFN() { return m_pfn; } /** * Accessor: Sets a new PFN to remember. * * @param pfn is a new physical filename. * @see #getPFN() */ public void setPFN( String pfn ) { m_pfn = pfn; } /** * Converts the contents into a string. * * @return a textual representation of the item content. */ public String toString() { // return "(" + m_pfn + "," + m_attributeMap.toString() + ")"; StringBuffer result = null; // save the formatted map content String save = m_attributeMap.toString(); if ( m_pfn == null ) { result = new StringBuffer( 10 + save.length() ); result.append( "((null)," ); } else { result = new StringBuffer( 4 + m_pfn.length() + save.length() ); result.append( '(' ).append( m_pfn ).append( ',' ); } result.append( save ); result.append( ')' ); return result.toString(); } /** * Matches two ReplicaCatalogEntry objects. The primary key in this case is * the pfn and all the attributes. * * @return true if the pfn and all the attributes match, false otherwise. */ public boolean equals(Object obj){ // null check if (obj == null) return false; // see if type of objects match if (! (obj instanceof ReplicaCatalogEntry)) return false; ReplicaCatalogEntry rce = (ReplicaCatalogEntry)obj; String pfn1 = this.m_pfn; String pfn2 = rce.getPFN(); //rce with null pfns are assumed to match boolean result = ( pfn1 == null && pfn2 == null || pfn1 != null && pfn2 != null && pfn1.equals(pfn2) && this.getAttributeCount() == rce.getAttributeCount()); if(result){ String key; Object val; //do the matching on attributes now for (Iterator it = rce.getAttributeIterator(); it.hasNext(); ) { key = (String) it.next(); val = rce.getAttribute(key); if (hasAttribute(key)) { if(!(getAttribute(key).equals(val))){ result = false; break; } } } } return result; } public Object clone() { ReplicaCatalogEntry r; try { r = (ReplicaCatalogEntry) super.clone(); } 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); } r.m_attributeMap = new TreeMap(); r.setPFN(getPFN()); r.setResourceHandle(getResourceHandle()); r.addAttribute(this.m_attributeMap); return r; } /** * Checks to see if pool attribute is specified. If specified it is set as the site attribute. * In case both are specified an exception is thrown * */ public final void checkAndUpdateForPoolAttribute() { if( m_attributeMap.containsKey( ReplicaCatalogEntry.DEPRECATED_RESOURCE_HANDLE ) ){ String pool = (String) m_attributeMap.remove( ReplicaCatalogEntry.DEPRECATED_RESOURCE_HANDLE ); //PM-813 update the site attribute with the pool value if( m_attributeMap.containsKey( ReplicaCatalogEntry.RESOURCE_HANDLE) ){ throw new RuntimeException( "Both site and pool attribute specified for entry " + this ); } m_attributeMap.put( ReplicaCatalogEntry.RESOURCE_HANDLE, pool ); } } }