001: /*
002: * This file or a portion of this file is licensed under the terms of
003: * the Globus Toolkit Public License, found in file GTPL, or at
004: * http://www.globus.org/toolkit/download/license.html. This notice must
005: * appear in redistributions of this file, with or without modification.
006: *
007: * Redistributions of this Software, with or without modification, must
008: * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
009: * some other similar material which is provided with the Software (if
010: * any).
011: *
012: * Copyright 1999-2004 University of Chicago and The University of
013: * Southern California. All rights reserved.
014: */
015: package org.griphyn.common.catalog;
016:
017: import java.util.*;
018:
019: /**
020: * The entry is a high-level logical structure representing the physical
021: * filename, the site handle, and optional attributes related to the PFN
022: * as one entity.<p>
023: *
024: * The resource handle is the most frequently used attribute. In
025: * reality, the resource handle may be a relational attribute of the
026: * mapping relation between an LFN and a PFN - there is disagreement
027: * among the developers on this issue. For simplicity purposes, it
028: * appears to be sufficient to make the resource handle a regular PFN
029: * attribute.<p>
030: *
031: * @author Jens-S. Vöckler
032: * @author Karan Vahi
033: * @version $Revision: 50 $
034: */
035: public class ReplicaCatalogEntry implements CatalogEntry {
036: /**
037: * The (reserved) attribute name used for the resource handle.
038: */
039: public static final String RESOURCE_HANDLE = "pool";
040:
041: /**
042: * The physical filename.
043: */
044: private String m_pfn;
045:
046: /**
047: * Any optional attributes associated with the PFN.
048: */
049: private Map m_attributeMap;
050:
051: /**
052: * Default constructor for arrays. The PFN is initialized to
053: * <code>null</null>, and thus must be explicitly set later. The map
054: * of attributes associated with the PFN is initialized to be empty.
055: * Thus, no resource handle is available.
056: */
057: public ReplicaCatalogEntry() {
058: m_pfn = null;
059: m_attributeMap = new TreeMap();
060: }
061:
062: /**
063: * Convenience constructor initializes the PFN. The map of attributes
064: * is initialized to be empty. Thus, no resource handle is avaiable.
065: *
066: * @param pfn is the PFN to remember.
067: */
068: public ReplicaCatalogEntry(String pfn) {
069: m_pfn = pfn;
070: m_attributeMap = new TreeMap();
071: }
072:
073: /**
074: * Convenience constructor initializes the PFN and the resource
075: * handle. The resource handle is stored as regular PFN attribute.
076: *
077: * @param pfn is the PFN to remember.
078: * @param handle is the resource handle to remember.
079: */
080: public ReplicaCatalogEntry(String pfn, String handle) {
081: m_pfn = pfn;
082: m_attributeMap = new TreeMap();
083: m_attributeMap.put(RESOURCE_HANDLE, handle);
084: }
085:
086: /**
087: * Standard constructor initializes the PFN and arbitrary attributes.
088: *
089: * @param pfn is the PFN to remember.
090: * @param attributes is a map of arbitrary attributes related to the
091: * PFN.
092: */
093: public ReplicaCatalogEntry(String pfn, Map attributes) {
094: m_pfn = pfn;
095: m_attributeMap = new TreeMap(attributes);
096: }
097:
098: /**
099: * Adds an attribute to the set of attributes. Note, this is identical
100: * to the {@link #setAttribute( String, Object )} method of the same
101: * signature.
102: *
103: * @param key is the key denoting an attribute.
104: * @param value is a value object to store.
105: */
106: public void addAttribute(String key, Object value) {
107: this .m_attributeMap.put(key, value);
108: }
109:
110: /**
111: * Adds attributes to the existing attributes.
112: *
113: * @param attributes is a map of attributes to add.
114: * @see #setAttribute(Map)
115: * @see java.util.Map#putAll( Map )
116: */
117: public void addAttribute(Map attributes) {
118: this .m_attributeMap.putAll(attributes);
119: }
120:
121: /**
122: * Obtains the attribute value for a given key.
123: *
124: * @param key is the key to look up
125: * @return the object stored as value, may be null.
126: * @see java.util.Map#get( Object )
127: */
128: public Object getAttribute(String key) {
129: return this .m_attributeMap.get(key);
130: }
131:
132: /**
133: * Checks for the existence of an attribute key.
134: *
135: * @param key is the key to look up
136: * @return true if the key is known, false otherwise.
137: */
138: public boolean hasAttribute(String key) {
139: return this .m_attributeMap.containsKey(key);
140: }
141:
142: /**
143: * Counts the number of attributes known for the PFN.
144: *
145: * @return number of attributes, may be zero.
146: * @see java.util.Map#size()
147: */
148: public int getAttributeCount() {
149: return this .m_attributeMap.size();
150: }
151:
152: /**
153: * Provides an iterator to traverse the attributes by their keys.
154: *
155: * @return an iterator over the keys to walk the attribute list.
156: */
157: public Iterator getAttributeIterator() {
158: return this .m_attributeMap.keySet().iterator();
159: }
160:
161: /**
162: * Merges the attribute maps of two entries in a controlled fashion.
163: * Entries are only merged with another entry, if the physical
164: * filenames match.
165: *
166: * @param a is one replica catalog entry to merge.
167: * @param b is the other replica catalog entry to merge.
168: * @param overwrite resolves intersections. If true, uses rce's
169: * attribute to remain, if false, the original attribute remains.
170: * @return the merged entry, if the PFNs matched, or <code>null</code>
171: * if the PFN mismatched.
172: */
173: public static ReplicaCatalogEntry merge(ReplicaCatalogEntry a,
174: ReplicaCatalogEntry b, boolean overwrite) {
175: ReplicaCatalogEntry result = null;
176:
177: String pfn1 = a.getPFN();
178: String pfn2 = b.getPFN();
179: if (pfn1 == null && pfn2 == null || pfn1 != null
180: && pfn2 != null && pfn1.equals(pfn2)) {
181: result = new ReplicaCatalogEntry(pfn1, a.m_attributeMap);
182: result.merge(b, overwrite); // result cannot be false
183: }
184:
185: // will return null on PFN mismatch
186: return result;
187: }
188:
189: /**
190: * Merges the attribute maps in a controlled fashion. An entry is only
191: * merged with another entry, if the physical filenames match.
192: *
193: * @param rce is another replica catalog entry to merge with.
194: * @param overwrite resolves intersections. If true, uses rce's
195: * attribute to remain, if false, the original attribute remains.
196: * @return true if a merge was attempted, false if the PFNs did not
197: * match.
198: */
199: public boolean merge(ReplicaCatalogEntry rce, boolean overwrite) {
200: String pfn1 = this .m_pfn;
201: String pfn2 = rce.getPFN();
202: boolean result = (pfn1 == null && pfn2 == null || pfn1 != null
203: && pfn2 != null && pfn1.equals(pfn2));
204:
205: // only merge if PFN match
206: if (result) {
207: String key;
208: Object val;
209:
210: for (Iterator i = rce.getAttributeIterator(); i.hasNext();) {
211: key = (String) i.next();
212: val = rce.getAttribute(key);
213: if (hasAttribute(key)) {
214: if (overwrite)
215: setAttribute(key, val);
216: } else {
217: setAttribute(key, val);
218: }
219: }
220: }
221:
222: return result;
223: }
224:
225: /**
226: * Removes all attributes associated with a PFN.
227: * @see #removeAttribute( String )
228: */
229: public void removeAllAttribute() {
230: this .m_attributeMap.clear();
231: }
232:
233: /**
234: * Removes a specific attribute.
235: *
236: * @param name is the name of the attribute to remove.
237: * @return the value object that was removed, or <code>null</code>,
238: * if the key was not in the map.
239: * @see #removeAllAttribute()
240: */
241: public Object removeAttribute(String name) {
242: return this .m_attributeMap.remove(name);
243: }
244:
245: /**
246: * Adds a new or overwrites an existing attribute. Note, this is
247: * identical to the {@link #addAttribute( String, Object)} method of
248: * the same signature.
249: *
250: * @param key is the name of the attribute
251: * @param value is the value object associated with the attribute.
252: */
253: public void setAttribute(String key, Object value) {
254: this .m_attributeMap.put(key, value);
255: }
256:
257: /**
258: * Replaces all existing attributes with new attributes. Existing
259: * attributes are removed before attempting a shallow copy of the new
260: * attributes.
261: *
262: * @param attributes is the map of new attributes to remember.
263: * @see #addAttribute(Map)
264: */
265: public void setAttribute(Map attributes) {
266: this .m_attributeMap.clear();
267: this .m_attributeMap.putAll(attributes);
268: }
269:
270: /**
271: * Obtains the resource handle from the attributes map. This is a
272: * convenience method. Internally, the PFN attribute map is queried
273: * for the value of the resource handle.
274: *
275: * @return the resource handle, or <code>null</code> if unset.
276: * @see #setResourceHandle( String )
277: */
278: public String getResourceHandle() {
279: return (String) this .m_attributeMap.get(RESOURCE_HANDLE);
280: }
281:
282: /**
283: * Sets a new resource handle to remember as PFN attribute. This is a
284: * convenience method. Internally, the PFN attribute map is changed
285: * to remember the new resource handle.
286: *
287: * @param handle is the new resource handle.
288: * @see #getResourceHandle()
289: */
290: public void setResourceHandle(String handle) {
291: this .m_attributeMap.put(RESOURCE_HANDLE, handle);
292: }
293:
294: /**
295: * Accessor: Obtains the PFN portion from this entry.
296: *
297: * @return the physical filename, or <code>null</code> if unset.
298: * @see #setPFN( String )
299: */
300: public String getPFN() {
301: return m_pfn;
302: }
303:
304: /**
305: * Accessor: Sets a new PFN to remember.
306: *
307: * @param pfn is a new physical filename.
308: * @see #getPFN()
309: */
310: public void setPFN(String pfn) {
311: m_pfn = pfn;
312: }
313:
314: /**
315: * Converts the contents into a string.
316: *
317: * @return a textual representation of the item content.
318: */
319: public String toString() {
320: // return "(" + m_pfn + "," + m_attributeMap.toString() + ")";
321: StringBuffer result = null;
322:
323: // save the formatted map content
324: String save = m_attributeMap.toString();
325:
326: if (m_pfn == null) {
327: result = new StringBuffer(10 + save.length());
328: result.append("((null),");
329: } else {
330: result = new StringBuffer(4 + m_pfn.length()
331: + save.length());
332: result.append('(').append(m_pfn).append(',');
333: }
334:
335: result.append(save);
336: result.append(')');
337:
338: return result.toString();
339: }
340:
341: /**
342: * Matches two ReplicaCatalogEntry objects. The primary key in this case is
343: * the pfn and all the attributes.
344: *
345: * @return true if the pfn and all the attributes match, false otherwise.
346: */
347: public boolean equals(Object obj) {
348: // null check
349: if (obj == null)
350: return false;
351:
352: // see if type of objects match
353: if (!(obj instanceof ReplicaCatalogEntry))
354: return false;
355:
356: ReplicaCatalogEntry rce = (ReplicaCatalogEntry) obj;
357: String pfn1 = this .m_pfn;
358: String pfn2 = rce.getPFN();
359:
360: //rce with null pfns are assumed to match
361: boolean result = (pfn1 == null && pfn2 == null || pfn1 != null
362: && pfn2 != null && pfn1.equals(pfn2)
363: && this .getAttributeCount() == rce.getAttributeCount());
364:
365: if (result) {
366: String key;
367: Object val;
368:
369: //do the matching on attributes now
370: for (Iterator it = rce.getAttributeIterator(); it.hasNext();) {
371: key = (String) it.next();
372: val = rce.getAttribute(key);
373: if (hasAttribute(key)) {
374: if (!(getAttribute(key).equals(val))) {
375: result = false;
376: break;
377: }
378: }
379: }
380: }
381:
382: return result;
383: }
384:
385: }
|