0001: /*
0002: * This file or a portion of this file is licensed under the terms of
0003: * the Globus Toolkit Public License, found in file GTPL, or at
0004: * http://www.globus.org/toolkit/download/license.html. This notice must
0005: * appear in redistributions of this file, with or without modification.
0006: *
0007: * Redistributions of this Software, with or without modification, must
0008: * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
0009: * some other similar material which is provided with the Software (if
0010: * any).
0011: *
0012: * Copyright 1999-2004 University of Chicago and The University of
0013: * Southern California. All rights reserved.
0014: */
0015:
0016: package org.griphyn.common.catalog.replica;
0017:
0018: import java.io.*;
0019: import java.util.*;
0020: import java.util.regex.*;
0021: import org.griphyn.common.util.Boolean;
0022: import org.griphyn.common.util.Escape;
0023: import org.griphyn.common.util.Currently;
0024: import org.griphyn.common.catalog.Catalog;
0025: import org.griphyn.common.catalog.ReplicaCatalog;
0026: import org.griphyn.common.catalog.ReplicaCatalogEntry;
0027:
0028: /**
0029: * This class implements a replica catalog on top of a simple file which
0030: * contains two or more columns. It is neither transactionally safe, nor
0031: * advised to use for production purposes in any way. Multiple
0032: * concurrent instances <b>will clobber</b> each other!<p>
0033: *
0034: * The site attribute should be specified whenever possible. The
0035: * attribute key for the site attribute is "pool". For the shell
0036: * planner, its value will always be "local".<p>
0037: *
0038: * The class is permissive in what inputs it accepts. The LFN may or may
0039: * not be quoted. If it contains linear whitespace, quotes, backslash or
0040: * an equality sign, it must be quoted and escaped. Ditto for the PFN.
0041: * The attribute key-value pairs are separated by an equality sign
0042: * without any whitespaces. The value may be in quoted. The LFN
0043: * sentiments about quoting apply.<p>
0044: *
0045: * <pre>
0046: * LFN PFN
0047: * LFN PFN a=b [..]
0048: * LFN PFN a="b" [..]
0049: * "LFN w/LWS" "PFN w/LWS" [..]
0050: * </pre>
0051: *
0052: * The class is strict when producing (storing) results. The LFN and PFN
0053: * are only quoted and escaped, if necessary. The attribute values are
0054: * always quoted and escaped.
0055: *
0056: * @author Jens-S. Vöckler
0057: * @version $Revision: 216 $
0058: */
0059: public class SimpleFile implements ReplicaCatalog {
0060: /**
0061: * Records the quoting mode for LFNs and PFNs. If false, only quote as
0062: * necessary. If true, always quote all LFNs and PFNs.
0063: */
0064: protected boolean m_quote = false;
0065:
0066: /**
0067: * Records the name of the on-disk representation.
0068: */
0069: protected String m_filename = null;
0070:
0071: /**
0072: * Maintains a memory slurp of the file representation.
0073: */
0074: protected Map m_lfn = null;
0075:
0076: /**
0077: * Default empty constructor creates an object that is not yet connected
0078: * to any database. You must use support methods to connect before this
0079: * instance becomes usable.
0080: *
0081: * @see #connect( Properties )
0082: */
0083: public SimpleFile() {
0084: // make connection defunc
0085: m_lfn = null;
0086: m_filename = null;
0087: }
0088:
0089: /**
0090: * Provides the final states and associated messages.
0091: *
0092: * <pre>
0093: * ---+----+--------------------
0094: * F1 | 17 | final state, no record
0095: * F2 | 16 | final state, valid record
0096: * E1 | 18 | premature end
0097: * E2 | 19 | illegal character
0098: * E3 | 20 | incomplete record
0099: * E4 | 21 | unterminated string
0100: * </pre>
0101: */
0102: private static final String c_final[] = { "OK", "noop",
0103: "premature end of record", "illegal character @",
0104: "incomplete record", "missing closing quote" };
0105:
0106: /**
0107: * Contains the state transition tables. The notes a through c mark
0108: * similar states:
0109: * <pre>
0110: * | EOS | lws | = | "" | \\ | else|
0111: * -----+-----+-----+-----+-----+-----+-----+--------------
0112: * 0 | F1,-| 0,-| E2 | 3,-| E2 | 1,Sl| skip initial ws
0113: * a 1 | E3 | 2,Fl| E2 | E2 | E2 | 1,Sl| LFN w/o quotes
0114: * 2 | E3 | 2,-| E2 | 6,-| E2 | 5,Sp| skip ws between LFN and PFN
0115: * b 3 | E4 | 3,Sl| 3,Sl| 2,Fl| 4,-| 3,Sl| LFN in quotes
0116: * c 4 | E4 | 3,Sl| 3,Sl| 3,Sl| 3,Sl| 3,Sl| LFN backslash escape
0117: * -----+-----+-----+-----+-----+-----+-----+--------------
0118: * a 5 |F2,Fp| 8,Fp| E2 | E2 | E2 | 5,Sp| PFN w/o quotes
0119: * b 6 | E4 | 6,Sp| 6,Sp| 8,Fp| 7,-| 6,Sp| PFN in quotes
0120: * c 7 | E4 | 6,Sp| 6,Sp| 6,Sp| 6,Sp| 6,Sp| PFN backslash escape
0121: * 8 | F2,-| 8,-| E2 | E2 | E2 | 9,Sk| skip ws before attributes
0122: * 9 | E1 | E2 |10,Fk| E2 | E2 | 9,Sk| attribute key
0123: * 10 | E1 | E2 | E2 | 12,-| E2 |11,Sv| equals sign
0124: * -----+-----+-----+-----+-----+-----+-----+--------------
0125: * a 11 |F2,Fv| 8,Fv| E2 | E2 | E2 |11,Sv| value w/o quotes
0126: * b 12 | E4 |12,Sv|12,Sv| 8,Fv| 13,-|12,Sv| value in quotes
0127: * c 13 | E4 |12,Sv|12,Sv|12,Sv|12,Sv|12,Sv| value backslash escape
0128: * </pre>
0129: */
0130: private static final short c_state[][] = { { 17, 0, 19, 3, 19, 1 }, // 0
0131: { 20, 2, 19, 19, 19, 1 }, // 1
0132: { 20, 2, 19, 6, 19, 5 }, // 2
0133: { 21, 3, 3, 2, 4, 3 }, // 3
0134: { 21, 3, 3, 3, 3, 3 }, // 4
0135:
0136: { 16, 8, 19, 19, 19, 5 }, // 5
0137: { 21, 6, 6, 8, 7, 6 }, // 6
0138: { 21, 6, 6, 6, 6, 6 }, // 7
0139: { 16, 8, 19, 19, 19, 9 }, // 8
0140: { 18, 19, 10, 19, 19, 9 }, // 9
0141: { 18, 19, 19, 12, 19, 11 }, // 10
0142:
0143: { 16, 8, 19, 19, 19, 11 }, // 11
0144: { 21, 12, 12, 8, 13, 12 }, // 12
0145: { 21, 12, 12, 12, 12, 12 } }; // 13
0146:
0147: /**
0148: * Contains the actions to perform upon each state transition including
0149: * transition into self state.
0150: *
0151: * <pre>
0152: * | |
0153: * ---+---+-------------------------------------------
0154: * - | 0 | no op
0155: * S*| 1 | append to sb
0156: * Fl| 2 | lfn := sb
0157: * Fp| 3 | pfn := sb
0158: * Fk| 4 | key := sb
0159: * Fv| 5 | value := sb
0160: * </pre>
0161: */
0162: private static final short c_action[][] = { { 0, 0, 0, 0, 0, 1 }, // 0
0163: { 0, 2, 0, 0, 0, 1 }, // 1 a
0164: { 0, 0, 0, 0, 0, 1 }, // 2
0165: { 0, 1, 1, 2, 0, 1 }, // 3 b
0166: { 0, 1, 1, 1, 1, 1 }, // 4 c
0167:
0168: { 3, 3, 0, 0, 0, 1 }, // 5 a
0169: { 0, 1, 1, 3, 0, 1 }, // 6 b
0170: { 0, 1, 1, 1, 1, 1 }, // 7 c
0171: { 0, 0, 0, 0, 0, 1 }, // 8
0172: { 0, 0, 4, 0, 0, 1 }, // 9
0173: { 0, 0, 0, 0, 0, 1 }, // 10
0174:
0175: { 5, 5, 0, 0, 0, 1 }, // 11 a
0176: { 0, 1, 1, 5, 0, 1 }, // 12 b
0177: { 0, 1, 1, 1, 1, 1 } }; // 13 c
0178:
0179: /**
0180: * Parses a line from the file replica catalog
0181: *
0182: * @param line is the line to parse
0183: * @param lineno is the line number of this line
0184: * @return true if a valid element was generated
0185: */
0186: public boolean parse(String line, int lineno) {
0187: char ch = ' ';
0188: String lfn = null;
0189: String pfn = null;
0190: String key = null;
0191: Map attr = new TreeMap();
0192: short input, state = 0;
0193: int i = 0;
0194: StringBuffer sb = new StringBuffer();
0195:
0196: while (state < 16) {
0197: if (line.length() <= i) {
0198: ch = ' ';
0199: input = 0;
0200: } // EOS
0201: else
0202: switch ((ch = line.charAt(i))) {
0203: case ' ':
0204: input = 1;
0205: break;
0206: case '\t':
0207: input = 1;
0208: break;
0209: case '=':
0210: input = 2;
0211: break;
0212: case '"':
0213: input = 3;
0214: break;
0215: case '\\':
0216: input = 4;
0217: break;
0218: default:
0219: input = 5;
0220: break;
0221: }
0222: i++;
0223:
0224: // perform action
0225: switch (c_action[state][input]) {
0226: case 0: // noop
0227: break;
0228: case 1: // append to sb
0229: sb.append(ch);
0230: break;
0231: case 2: // sb to lfn
0232: lfn = sb.toString();
0233: sb = new StringBuffer();
0234: break;
0235: case 3: // sb to pfn
0236: pfn = sb.toString();
0237: sb = new StringBuffer();
0238: break;
0239: case 4: // sb to key
0240: key = sb.toString();
0241: sb = new StringBuffer();
0242: break;
0243: case 5: // sb to value
0244: attr.put(key, sb.toString());
0245: sb = new StringBuffer();
0246: break;
0247: }
0248:
0249: // goto new state
0250: state = c_state[state][input];
0251: }
0252:
0253: if (state > 17) {
0254: // error report
0255: sb = new StringBuffer(i + 1);
0256: for (int j = 1; j < i; ++j)
0257: sb.append(' ');
0258: sb.append('^');
0259:
0260: // FIXME: log it somewhere
0261: System.err.println("While parsing line " + lineno + ": "
0262: + c_final[state - 16].replace('@', ch)
0263: + ", ignoring line");
0264: System.err.println(line);
0265: System.err.println(sb);
0266: return false;
0267: } else {
0268: // valid entry
0269: if (state == 16)
0270: insert(lfn, new ReplicaCatalogEntry(pfn, attr));
0271: return true;
0272: }
0273: }
0274:
0275: /**
0276: * Reads the on-disk map file into memory.
0277: *
0278: * @param filename is the name of the file to read.
0279: * @return true, if the in-memory data structures appear sound.
0280: */
0281: public boolean connect(String filename) {
0282: // sanity check
0283: if (filename == null)
0284: return false;
0285: m_filename = filename;
0286: m_lfn = new LinkedHashMap();
0287:
0288: try {
0289: File f = new File(filename);
0290: if (f.exists()) {
0291: LineNumberReader lnr = new LineNumberReader(
0292: new FileReader(f));
0293: String line;
0294: while ((line = lnr.readLine()) != null) {
0295: if (line.length() == 0 || line.charAt(0) == '#')
0296: continue;
0297: parse(line, lnr.getLineNumber());
0298: }
0299:
0300: lnr.close();
0301: }
0302: } catch (IOException ioe) {
0303: m_lfn = null;
0304: m_filename = null;
0305: throw new RuntimeException(ioe); // re-throw
0306: }
0307:
0308: return true;
0309: }
0310:
0311: /**
0312: * Establishes a connection to the database from the properties.
0313: * You will need to specify a "file" property to point to the
0314: * location of the on-disk instance. If the property "quote" is
0315: * set to a true value, LFNs and PFNs are always quoted. By default,
0316: * and if false, LFNs and PFNs are only quoted as necessary.
0317: *
0318: * @param props is the property table with sufficient settings to
0319: * establish a link with the database.
0320: * @return true if connected, false if failed to connect.
0321: *
0322: * @throws Error subclasses for runtime errors in the class loader.
0323: */
0324: public boolean connect(Properties props) {
0325: // quote mode
0326: m_quote = Boolean.parse(props.getProperty("quote"));
0327:
0328: if (props.containsKey("file"))
0329: return connect(props.getProperty("file"));
0330: return false;
0331: }
0332:
0333: /**
0334: * Quotes a string only if necessary. This methods first determines,
0335: * if a strings requires quoting, because it contains whitespace, an
0336: * equality sign, quotes, or a backslash. If not, the string is not
0337: * quoted. If the input contains forbidden characters, it is placed
0338: * into quotes and quote and backslash are backslash escaped.<p>
0339: * However, if the property "quote" had a <code>true</code> value
0340: * when connecting to the database, output will always be quoted.
0341: *
0342: * @param e is the Escape instance used to escape strings.
0343: * @param s is the string that may require quoting
0344: * @return either the original string, or a newly allocated instance
0345: * to an escaped string.
0346: */
0347: public String quote(Escape e, String s) {
0348: String result = null;
0349:
0350: if (s == null || s.length() == 0) {
0351: // empty string short-cut
0352: result = (m_quote ? "\"\"" : s);
0353: } else {
0354: // string has content
0355: boolean flag = m_quote;
0356: for (int i = 0; i < s.length() && !flag; ++i) {
0357: // Note: loop will never trigger, if m_quote is true
0358: char ch = s.charAt(i);
0359: flag = (ch == '"' || ch == '\\' || ch == '=' || Character
0360: .isWhitespace(ch));
0361: }
0362:
0363: result = (flag ? '"' + e.escape(s) + '"' : s);
0364: }
0365:
0366: // single point of exit
0367: return result;
0368: }
0369:
0370: /**
0371: * This operation will dump the in-memory representation back onto
0372: * disk. The store operation is strict in what it produces. The LFN
0373: * and PFN records are only quoted, if they require quotes, because
0374: * they contain special characters. The attributes are <b>always</b>
0375: * quoted and thus quote-escaped.
0376: */
0377: public void close() {
0378: String newline = System.getProperty("line.separator", "\r\n");
0379: Escape e = new Escape("\"\\", '\\');
0380:
0381: // sanity check
0382: if (m_lfn == null)
0383: return;
0384:
0385: try {
0386: // open
0387: Writer out = new BufferedWriter(new FileWriter(m_filename));
0388:
0389: // write header
0390: out.write("# file-based replica catalog: "
0391: + Currently.iso8601(false, true, true, new Date()));
0392: out.write(newline);
0393:
0394: // write data
0395: for (Iterator i = m_lfn.keySet().iterator(); i.hasNext();) {
0396: String lfn = (String) i.next();
0397: Collection c = (Collection) m_lfn.get(lfn);
0398: if (c != null) {
0399: for (Iterator j = c.iterator(); j.hasNext();) {
0400: ReplicaCatalogEntry rce = (ReplicaCatalogEntry) j
0401: .next();
0402: out.write(quote(e, lfn));
0403: out.write(' ');
0404: out.write(quote(e, rce.getPFN()));
0405: for (Iterator k = rce.getAttributeIterator(); k
0406: .hasNext();) {
0407: String key = (String) k.next();
0408: String value = (String) rce
0409: .getAttribute(key);
0410: out.write(' ');
0411: out.write(key);
0412: out.write("=\"");
0413: out.write(e.escape(value));
0414: out.write('"');
0415: }
0416:
0417: // finalize record/line
0418: out.write(newline);
0419: }
0420: }
0421: }
0422:
0423: // close
0424: out.close();
0425: } catch (IOException ioe) {
0426: // FIXME: blurt message somewhere sane
0427: System.err.println(ioe.getMessage());
0428: } finally {
0429: m_lfn.clear();
0430: m_lfn = null;
0431: m_filename = null;
0432: }
0433: }
0434:
0435: /**
0436: * Predicate to check, if the connection with the catalog's
0437: * implementation is still active. This helps determining, if it makes
0438: * sense to call <code>close()</code>.
0439: *
0440: * @return true, if the implementation is disassociated, false otherwise.
0441: * @see #close()
0442: */
0443: public boolean isClosed() {
0444: return (m_lfn == null);
0445: }
0446:
0447: /**
0448: * Retrieves the entry for a given filename and site handle from the
0449: * replica catalog.
0450: *
0451: * @param lfn is the logical filename to obtain information for.
0452: * @param handle is the resource handle to obtain entries for.
0453: * @return the (first) matching physical filename, or
0454: * <code>null</code> if no match was found.
0455: */
0456: public String lookup(String lfn, String handle) {
0457: Collection c = (Collection) m_lfn.get(lfn);
0458: if (c == null)
0459: return null;
0460:
0461: for (Iterator i = c.iterator(); i.hasNext();) {
0462: ReplicaCatalogEntry rce = (ReplicaCatalogEntry) i.next();
0463: String pool = rce.getResourceHandle();
0464: if (pool == null && handle == null || pool != null
0465: && handle != null && pool.equals(handle))
0466: return rce.getPFN();
0467: }
0468: return null;
0469: }
0470:
0471: /**
0472: * Retrieves all entries for a given LFN from the replica catalog.
0473: * Each entry in the result set is a tuple of a PFN and all its
0474: * attributes.
0475: *
0476: * @param lfn is the logical filename to obtain information for.
0477: * @return a collection of replica catalog entries
0478: * @see ReplicaCatalogEntry
0479: */
0480: public Collection lookup(String lfn) {
0481: Collection c = (Collection) m_lfn.get(lfn);
0482: if (c == null)
0483: return new ArrayList();
0484: else
0485: return new ArrayList(c);
0486: }
0487:
0488: /**
0489: * Retrieves all entries for a given LFN from the replica catalog.
0490: * Each entry in the result set is just a PFN string. Duplicates
0491: * are reduced through the set paradigm.
0492: *
0493: * @param lfn is the logical filename to obtain information for.
0494: * @return a set of PFN strings
0495: */
0496: public Set lookupNoAttributes(String lfn) {
0497: Set result = new TreeSet();
0498: Collection c = (Collection) m_lfn.get(lfn);
0499:
0500: if (c != null) {
0501: for (Iterator i = c.iterator(); i.hasNext();) {
0502: result.add(((ReplicaCatalogEntry) i.next()).getPFN());
0503: }
0504: }
0505:
0506: // done
0507: return result;
0508: }
0509:
0510: /**
0511: * Retrieves multiple entries for a given logical filename, up to the
0512: * complete catalog. Retrieving full catalogs should be harmful, but
0513: * may be helpful in an online display or portal.
0514: *
0515: * @param lfns is a set of logical filename strings to look up.
0516: * @return a map indexed by the LFN. Each value is a collection
0517: * of replica catalog entries for the LFN.
0518: * @see org.griphyn.common.catalog.ReplicaCatalogEntry
0519: */
0520: public Map lookup(Set lfns) {
0521: Map result = new HashMap();
0522: if (lfns == null || lfns.size() == 0)
0523: return result;
0524:
0525: for (Iterator i = lfns.iterator(); i.hasNext();) {
0526: String lfn = (String) i.next();
0527: Collection c = (Collection) m_lfn.get(lfn);
0528: if (c == null)
0529: result.put(lfn, new ArrayList());
0530: else
0531: result.put(lfn, new ArrayList(c));
0532: }
0533:
0534: // done
0535: return result;
0536: }
0537:
0538: /**
0539: * Retrieves multiple entries for a given logical filename, up to the
0540: * complete catalog. Retrieving full catalogs should be harmful, but
0541: * may be helpful in an online display or portal.
0542: *
0543: * @param lfns is a set of logical filename strings to look up.
0544: * @return a map indexed by the LFN. Each value is a set
0545: * of PFN strings.
0546: */
0547: public Map lookupNoAttributes(Set lfns) {
0548: Map result = new HashMap();
0549: if (lfns == null || lfns.size() == 0)
0550: return result;
0551:
0552: for (Iterator i = lfns.iterator(); i.hasNext();) {
0553: Set value = new TreeSet();
0554: String lfn = (String) i.next();
0555: Collection c = (Collection) m_lfn.get(lfn);
0556: if (c != null) {
0557: for (Iterator j = c.iterator(); j.hasNext();) {
0558: value
0559: .add(((ReplicaCatalogEntry) j.next())
0560: .getPFN());
0561: }
0562: }
0563: result.put(lfn, value);
0564: }
0565:
0566: // done
0567: return result;
0568: }
0569:
0570: /**
0571: * Retrieves multiple entries for a given logical filename, up to the
0572: * complete catalog. Retrieving full catalogs should be harmful, but
0573: * may be helpful in online display or portal.<p>
0574: *
0575: * @param lfns is a set of logical filename strings to look up.
0576: * @param handle is the resource handle, restricting the LFNs.
0577: * @return a map indexed by the LFN. Each value is a collection
0578: * of replica catalog entries (all attributes).
0579: * @see ReplicaCatalogEntry
0580: */
0581: public Map lookup(Set lfns, String handle) {
0582: Map result = new HashMap();
0583: if (lfns == null || lfns.size() == 0)
0584: return result;
0585:
0586: for (Iterator i = lfns.iterator(); i.hasNext();) {
0587: String lfn = (String) i.next();
0588: Collection c = (Collection) m_lfn.get(lfn);
0589: if (c != null) {
0590: List value = new ArrayList();
0591:
0592: for (Iterator j = c.iterator(); j.hasNext();) {
0593: ReplicaCatalogEntry rce = (ReplicaCatalogEntry) j
0594: .next();
0595: String pool = rce.getResourceHandle();
0596: if (pool == null && handle == null || pool != null
0597: && handle != null && pool.equals(handle))
0598: value.add(rce);
0599: }
0600:
0601: // only put found LFNs into result
0602: result.put(lfn, value);
0603: }
0604: }
0605:
0606: // done
0607: return result;
0608: }
0609:
0610: /**
0611: * Retrieves multiple entries for a given logical filename, up to the
0612: * complete catalog. Retrieving full catalogs should be harmful, but
0613: * may be helpful in online display or portal.<p>
0614: *
0615: * @param lfns is a set of logical filename strings to look up.
0616: * @param handle is the resource handle, restricting the LFNs.
0617: * @return a map indexed by the LFN. Each value is a set of
0618: * physical filenames.
0619: */
0620: public Map lookupNoAttributes(Set lfns, String handle) {
0621: Map result = new HashMap();
0622: if (lfns == null || lfns.size() == 0)
0623: return result;
0624:
0625: for (Iterator i = lfns.iterator(); i.hasNext();) {
0626: String lfn = (String) i.next();
0627: Collection c = (Collection) m_lfn.get(lfn);
0628: if (c != null) {
0629: List value = new ArrayList();
0630:
0631: for (Iterator j = c.iterator(); j.hasNext();) {
0632: ReplicaCatalogEntry rce = (ReplicaCatalogEntry) j
0633: .next();
0634: String pool = rce.getResourceHandle();
0635: if (pool == null && handle == null || pool != null
0636: && handle != null && pool.equals(handle))
0637: value.add(rce.getPFN());
0638: }
0639:
0640: // only put found LFNs into result
0641: result.put(lfn, value);
0642: }
0643: }
0644:
0645: // done
0646: return result;
0647: }
0648:
0649: /**
0650: * Retrieves multiple entries for a given logical filename, up to the
0651: * complete catalog. Retrieving full catalogs should be harmful, but
0652: * may be helpful in online display or portal.
0653: *
0654: * @param constraints is mapping of keys 'lfn', 'pfn', or any
0655: * attribute name, e.g. the resource handle 'pool', to a string that
0656: * has some meaning to the implementing system. This can be a SQL
0657: * wildcard for queries, or a regular expression for Java-based memory
0658: * collections. Unknown keys are ignored. Using an empty map requests
0659: * the complete catalog.
0660: * @return a map indexed by the LFN. Each value is a collection
0661: * of replica catalog entries.
0662: * @see ReplicaCatalogEntry
0663: */
0664: public Map lookup(Map constraints) {
0665: if (constraints == null || constraints.size() == 0) {
0666: // return everything
0667: return Collections.unmodifiableMap(m_lfn);
0668:
0669: } else if (constraints.size() == 1
0670: && constraints.containsKey("lfn")) {
0671: // return matching LFNs
0672: Pattern p = Pattern
0673: .compile((String) constraints.get("lfn"));
0674: Map result = new HashMap();
0675: for (Iterator i = m_lfn.entrySet().iterator(); i.hasNext();) {
0676: Map.Entry e = (Map.Entry) i.next();
0677: String lfn = (String) e.getKey();
0678: if (p.matcher(lfn).matches())
0679: result.put(lfn, e.getValue());
0680: }
0681: return result;
0682:
0683: } else {
0684: // FIXME: Implement!
0685: throw new RuntimeException("method not implemented");
0686: }
0687: }
0688:
0689: /**
0690: * Lists all logical filenames in the catalog.
0691: *
0692: * @return A set of all logical filenames known to the catalog.
0693: */
0694: public Set list() {
0695: return new TreeSet(m_lfn.keySet());
0696: }
0697:
0698: /**
0699: * Lists a subset of all logical filenames in the catalog.
0700: *
0701: * @param constraint is a constraint for the logical filename only. It
0702: * is a string that has some meaning to the implementing system. This
0703: * can be a SQL wildcard for queries, or a regular expression for
0704: * Java-based memory collections.
0705: * @return A set of logical filenames that match. The set may be empty
0706: */
0707: public Set list(String constraint) {
0708: Set result = new TreeSet();
0709: Pattern p = Pattern.compile(constraint);
0710:
0711: for (Iterator i = m_lfn.keySet().iterator(); i.hasNext();) {
0712: String lfn = (String) i.next();
0713: if (p.matcher(lfn).matches())
0714: result.add(lfn);
0715: }
0716:
0717: // done
0718: return result;
0719: }
0720:
0721: /**
0722: * Inserts a new mapping into the replica catalog. Any existing
0723: * mapping of the same LFN and PFN will be replaced, including all its
0724: * attributes.
0725: *
0726: * @param lfn is the logical filename under which to book the entry.
0727: * @param tuple is the physical filename and associated PFN attributes.
0728: *
0729: * @return number of insertions, should always be 1. On failure,
0730: * throw an exception, don't use zero.
0731: */
0732: public int insert(String lfn, ReplicaCatalogEntry tuple) {
0733: if (lfn == null || tuple == null)
0734: throw new NullPointerException();
0735:
0736: Collection c = null;
0737: if (m_lfn.containsKey(lfn)) {
0738: boolean seen = false;
0739: String pfn = tuple.getPFN();
0740: c = (Collection) m_lfn.get(lfn);
0741: for (Iterator i = c.iterator(); i.hasNext() && !seen;) {
0742: ReplicaCatalogEntry rce = (ReplicaCatalogEntry) i
0743: .next();
0744: if ((seen = pfn.equals(rce.getPFN()))) {
0745: try {
0746: i.remove();
0747: } catch (UnsupportedOperationException uoe) {
0748: return 0;
0749: }
0750: }
0751: }
0752: } else {
0753: c = new ArrayList();
0754: m_lfn.put(lfn, c);
0755: }
0756: c.add(tuple);
0757:
0758: return 1;
0759: }
0760:
0761: /**
0762: * Inserts a new mapping into the replica catalog. This is a
0763: * convenience function exposing the resource handle. Internally, the
0764: * <code>ReplicaCatalogEntry</code> element will be contructed, and
0765: * passed to the appropriate insert function.
0766: *
0767: * @param lfn is the logical filename under which to book the entry.
0768: * @param pfn is the physical filename associated with it.
0769: * @param handle is a resource handle where the PFN resides.
0770: * @return number of insertions, should always be 1. On failure,
0771: * throw an exception, don't use zero.
0772: * @see #insert( String, ReplicaCatalogEntry )
0773: * @see ReplicaCatalogEntry
0774: */
0775: public int insert(String lfn, String pfn, String handle) {
0776: if (lfn == null || pfn == null || handle == null)
0777: throw new NullPointerException();
0778: return insert(lfn, new ReplicaCatalogEntry(pfn, handle));
0779: }
0780:
0781: /**
0782: * Inserts multiple mappings into the replica catalog. The input is a
0783: * map indexed by the LFN. The value for each LFN key is a collection
0784: * of replica catalog entries. Note that this operation will replace
0785: * existing entries.
0786: *
0787: * @param x is a map from logical filename string to list of replica
0788: * catalog entries.
0789: * @return the number of insertions.
0790: * @see org.griphyn.common.catalog.ReplicaCatalogEntry
0791: */
0792: public int insert(Map x) {
0793: int result = 0;
0794:
0795: // shortcut sanity
0796: if (x == null || x.size() == 0)
0797: return result;
0798:
0799: for (Iterator i = x.keySet().iterator(); i.hasNext();) {
0800: String lfn = (String) i.next();
0801: Object val = x.get(lfn);
0802: if (val instanceof ReplicaCatalogEntry) {
0803: // permit misconfigured clients
0804: result += insert(lfn, (ReplicaCatalogEntry) val);
0805: } else {
0806: // this is how it should have been
0807: for (Iterator j = ((Collection) val).iterator(); j
0808: .hasNext();) {
0809: ReplicaCatalogEntry rce = (ReplicaCatalogEntry) j
0810: .next();
0811: result += insert(lfn, rce);
0812: }
0813: }
0814: }
0815:
0816: return result;
0817: }
0818:
0819: /**
0820: * Deletes a specific mapping from the replica catalog. We don't care
0821: * about the resource handle. More than one entry could theoretically
0822: * be removed. Upon removal of an entry, all attributes associated
0823: * with the PFN also evaporate (cascading deletion).
0824: *
0825: * @param lfn is the logical filename in the tuple.
0826: * @param pfn is the physical filename in the tuple.
0827: * @return the number of removed entries.
0828: */
0829: public int delete(String lfn, String pfn) {
0830: int result = 0;
0831: if (lfn == null || pfn == null)
0832: return result;
0833:
0834: Collection c = (Collection) m_lfn.get(lfn);
0835: if (c == null)
0836: return result;
0837:
0838: List l = new ArrayList();
0839: for (Iterator i = c.iterator(); i.hasNext();) {
0840: ReplicaCatalogEntry rce = (ReplicaCatalogEntry) i.next();
0841: if (!rce.getPFN().equals(pfn))
0842: l.add(rce);
0843: }
0844:
0845: // anything removed?
0846: if (l.size() != c.size()) {
0847: result = c.size() - l.size();
0848: m_lfn.put(lfn, l);
0849: }
0850:
0851: // done
0852: return result;
0853: }
0854:
0855: /**
0856: * Deletes multiple mappings into the replica catalog. The input is a
0857: * map indexed by the LFN. The value for each LFN key is a collection
0858: * of replica catalog entries. On setting matchAttributes to false, all entries
0859: * having matching lfn pfn mapping to an entry in the Map are deleted.
0860: * However, upon removal of an entry, all attributes associated with the pfn
0861: * also evaporate (cascaded deletion).
0862: *
0863: * @param x is a map from logical filename string to list of
0864: * replica catalog entries.
0865: * @param matchAttributes whether mapping should be deleted only if all
0866: * attributes match.
0867: *
0868: * @return the number of deletions.
0869: * @see ReplicaCatalogEntry
0870: */
0871: public int delete(Map x, boolean matchAttributes) {
0872: throw new java.lang.UnsupportedOperationException(
0873: "delete(Map,boolean) not implemented as yet");
0874: }
0875:
0876: /**
0877: * Attempts to see, if all keys in the partial replica catalog entry are
0878: * contained in the full replica catalog entry.
0879: *
0880: * @param full is the full entry to check against.
0881: * @param part is the partial entry to check with.
0882: * @return true, if contained, false if not contained.
0883: */
0884: private boolean matchMe(ReplicaCatalogEntry full,
0885: ReplicaCatalogEntry part) {
0886: if (full.getPFN().equals(part.getPFN())) {
0887: for (Iterator i = part.getAttributeIterator(); i.hasNext();) {
0888: if (!full.hasAttribute((String) i.next()))
0889: return false;
0890: }
0891: return true;
0892: } else {
0893: return false;
0894: }
0895: }
0896:
0897: /**
0898: * Deletes a very specific mapping from the replica catalog. The LFN
0899: * must be matches, the PFN, and all PFN attributes specified in the
0900: * replica catalog entry. More than one entry could theoretically be
0901: * removed. Upon removal of an entry, all attributes associated with
0902: * the PFN also evaporate (cascading deletion).
0903: *
0904: * @param lfn is the logical filename in the tuple.
0905: * @param tuple is a description of the PFN and its attributes.
0906: * @return the number of removed entries, either 0 or 1.
0907: */
0908: public int delete(String lfn, ReplicaCatalogEntry tuple) {
0909: int result = 0;
0910: if (lfn == null || tuple == null)
0911: return result;
0912:
0913: Collection c = (Collection) m_lfn.get(lfn);
0914: if (c == null)
0915: return result;
0916:
0917: List l = new ArrayList();
0918: for (Iterator i = c.iterator(); i.hasNext();) {
0919: ReplicaCatalogEntry rce = (ReplicaCatalogEntry) i.next();
0920: if (!matchMe(rce, tuple))
0921: l.add(rce);
0922: }
0923:
0924: // anything removed?
0925: if (l.size() != c.size()) {
0926: result = c.size() - l.size();
0927: m_lfn.put(lfn, l);
0928: }
0929:
0930: // done
0931: return result;
0932: }
0933:
0934: /**
0935: * Looks for a match of an attribute value in a replica catalog
0936: * entry.
0937: *
0938: * @param rce is the replica catalog entry
0939: * @param name is the attribute key to match
0940: * @param value is the value to match against
0941: * @return true, if a match was found.
0942: */
0943: private boolean hasMatchingAttr(ReplicaCatalogEntry rce,
0944: String name, Object value) {
0945: if (rce.hasAttribute(name))
0946: return rce.getAttribute(name).equals(value);
0947: else
0948: return value == null;
0949: }
0950:
0951: /**
0952: * Deletes all PFN entries for a given LFN from the replica catalog
0953: * where the PFN attribute is found, and matches exactly the object
0954: * value. This method may be useful to remove all replica entries that
0955: * have a certain MD5 sum associated with them. It may also be harmful
0956: * overkill.
0957: *
0958: * @param lfn is the logical filename to look for.
0959: * @param name is the PFN attribute name to look for.
0960: * @param value is an exact match of the attribute value to match.
0961: * @return the number of removed entries.
0962: */
0963: public int delete(String lfn, String name, Object value) {
0964: int result = 0;
0965: if (lfn == null || name == null)
0966: return result;
0967:
0968: Collection c = (Collection) m_lfn.get(lfn);
0969: if (c == null)
0970: return result;
0971:
0972: List l = new ArrayList();
0973: for (Iterator i = c.iterator(); i.hasNext();) {
0974: ReplicaCatalogEntry rce = (ReplicaCatalogEntry) i.next();
0975: if (!hasMatchingAttr(rce, name, value))
0976: l.add(rce);
0977: }
0978:
0979: // anything removed?
0980: if (l.size() != c.size()) {
0981: result = c.size() - l.size();
0982: m_lfn.put(lfn, l);
0983: }
0984:
0985: // done
0986: return result;
0987: }
0988:
0989: /**
0990: * Deletes all PFN entries for a given LFN from the replica catalog
0991: * where the resource handle is found. Karan requested this
0992: * convenience method, which can be coded like
0993: * <pre>
0994: * delete( lfn, RESOURCE_HANDLE, handle )
0995: * </pre>
0996: *
0997: * @param lfn is the logical filename to look for.
0998: * @param handle is the resource handle
0999: * @return the number of entries removed.
1000: */
1001: public int deleteByResource(String lfn, String handle) {
1002: return delete(lfn, ReplicaCatalogEntry.RESOURCE_HANDLE, handle);
1003: }
1004:
1005: /**
1006: * Removes all mappings for an LFN from the replica catalog.
1007: *
1008: * @param lfn is the logical filename to remove all mappings for.
1009: * @return the number of removed entries.
1010: */
1011: public int remove(String lfn) {
1012: Collection c = (Collection) m_lfn.remove(lfn);
1013: if (c == null)
1014: return 0;
1015: else
1016: return c.size();
1017: }
1018:
1019: /**
1020: * Removes all mappings for a set of LFNs.
1021: *
1022: * @param lfns is a set of logical filename to remove all mappings for.
1023: * @return the number of removed entries.
1024: * @see #remove( String )
1025: */
1026: public int remove(Set lfns) {
1027: int result = 0;
1028:
1029: // sanity checks
1030: if (lfns == null || lfns.size() == 0)
1031: return result;
1032:
1033: for (Iterator i = lfns.iterator(); i.hasNext();) {
1034: String lfn = (String) i.next();
1035: result += remove(lfn);
1036: }
1037:
1038: // done
1039: return result;
1040: }
1041:
1042: /**
1043: * Removes all entries from the replica catalog where the PFN attribute
1044: * is found, and matches exactly the object value.
1045: *
1046: * @param name is the PFN attribute key to look for.
1047: * @param value is an exact match of the attribute value to match.
1048: * @return the number of removed entries.
1049: */
1050: public int removeByAttribute(String name, Object value) {
1051: int result = 0;
1052:
1053: for (Iterator i = m_lfn.keySet().iterator(); i.hasNext();) {
1054: String lfn = (String) i.next();
1055: Collection c = (Collection) m_lfn.get(lfn);
1056: if (c != null) {
1057: List l = new ArrayList();
1058: for (Iterator j = c.iterator(); j.hasNext();) {
1059: ReplicaCatalogEntry rce = (ReplicaCatalogEntry) j
1060: .next();
1061: if (!hasMatchingAttr(rce, name, value))
1062: l.add(rce);
1063: }
1064: if (l.size() != c.size()) {
1065: result += (c.size() - l.size());
1066: m_lfn.put(lfn, l);
1067: }
1068: }
1069: }
1070:
1071: // done
1072: return result;
1073: }
1074:
1075: /**
1076: * Removes all entries associated with a particular resource handle.
1077: * This is useful, if a site goes offline. It is a convenience method,
1078: * which calls the generic <code>removeByAttribute</code> method.
1079: *
1080: * @param handle is the site handle to remove all entries for.
1081: * @return the number of removed entries.
1082: * @see #removeByAttribute( String, Object )
1083: */
1084: public int removeByAttribute(String handle) {
1085: return removeByAttribute(ReplicaCatalogEntry.RESOURCE_HANDLE,
1086: handle);
1087: }
1088:
1089: /**
1090: * Removes everything. Use with caution!
1091: *
1092: * @return the number of removed entries.
1093: */
1094: public int clear() {
1095: int result = m_lfn.size();
1096: m_lfn.clear();
1097: return result;
1098: }
1099: }
|