/** * 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.impl; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.Set; import edu.isi.pegasus.common.util.Escape; import edu.isi.pegasus.planner.catalog.ReplicaCatalog; import edu.isi.pegasus.planner.catalog.replica.ReplicaCatalogEntry; /** * This class implements a replica catalog which directly writes to the output * file. This is done so that the Cache file generation would not require * storing all RC entries in memory. * * The implementation does not maintain any internal state, and is not aware if * same entries are being written multiple times. * * The class using this implementation needs to ensure only valid entries are * passed through to the insert method, and that entries are not being * duplicated. * * @author Rajiv Mayani * @version $Revision: 5907 $ */ public class FlushedCache implements ReplicaCatalog { /** * A buffered writer, to buffer all file writes being performed. */ protected BufferedWriter m_out; /** * No. of entries to buffer. */ private int m_buffer_size = 1000; /** * No. of entries to buffer. */ private static final int m_avg_line_size = 100; /** * No. of un-flushed entries. */ private int m_line_count = 0; /** * Set buffer size i.e. The no of lines that the implementation will buffer. */ public void setBufferSize(int bufferSize) { m_buffer_size = bufferSize; } /** * Opens the file for writing. * * @param filename is the name of the file to write too. * @return true, if the file can be opened for writing */ public boolean connect(String filename) { // sanity check if (filename == null) { return false; } try { // Create a buffered writer with a buffer size of m_buffer_size // lines, with each line averaging a m_avg_line_size bytes m_out = new BufferedWriter(new FileWriter(filename), m_buffer_size * m_avg_line_size); } catch (IOException ioe) { System.err.println(ioe.getMessage()); return false; } return true; } /** * Establishes a connection to the database from the properties. You will * need to specify a "file" property to point to the location of the on-disk * instance. * * @param props is the property table with sufficient settings to establish * a link with the database. * @return true if connected, false if failed to connect. * */ public boolean connect(Properties props) { if (props.containsKey("file")) { return connect(props.getProperty("file")); } return false; } /** * Quotes a string only if necessary. This methods first determines, if a * strings requires quoting, because it contains whitespace, an equality * sign, quotes, or a backslash. If not, the string is not quoted. If the * input contains forbidden characters, it is placed into quotes and quote * and backslash are backslash escaped. * <p> * However, if the property "quote" had a <code>true</code> value when * connecting to the database, output will always be quoted. * * @param e is the Escape instance used to escape strings. * @param s is the string that may require quoting * @return either the original string, or a newly allocated instance to an * escaped string. */ public String quote(Escape e, String s) { String result = null; if (s == null || s.length() == 0) { // empty string short-cut result = s; } else { // string has content boolean flag = false; for (int i = 0; i < s.length() && !flag; ++i) { // Note: loop will never trigger, if m_quote is true char ch = s.charAt(i); flag = (ch == '"' || ch == '\\' || ch == '=' || Character .isWhitespace(ch)); } result = (flag ? '"' + e.escape(s) + '"' : s); } // single point of exit return result; } /** * * The method generate a String representation of the replica catalog entry. * The String representation follows the format expected by SimpleFile * replica catalog implementation. * * @param lfn The logical file name * @param rce The replica catalog entry consisting of the physical file name * name and key, value pairs. * @return A String representation of the replica-catalog entry. */ protected String writeReplicaCatalogEntry(String lfn, ReplicaCatalogEntry rce) { Escape e = new Escape("\"\\", '\\'); StringBuilder s = new StringBuilder(); s.append(quote(e, lfn)); s.append(' '); s.append(quote(e, rce.getPFN())); for (Iterator k = rce.getAttributeIterator(); k.hasNext();) { String key = (String) k.next(); String value = (String) rce.getAttribute(key); s.append(' '); s.append(key); s.append("=\""); s.append(e.escape(value)); s.append('"'); } m_line_count++; s.append(System.getProperty("line.separator", "\r\n")); return s.toString(); } /** * Check if the buffer is full i.e. No. of unwritten lines is same as * m_buffer_size * * @see setBufferSize */ private void flush() { if (m_buffer_size - m_line_count == 0) { try { m_line_count = 0; m_out.flush(); } catch (IOException ioe) { System.err.println(ioe.getMessage()); } } } /** * This operation will dump the in-memory representation back onto disk. The * store operation is strict in what it produces. The LFN and PFN records * are only quoted, if they require quotes, because they contain special * characters. The attributes are <b>always</b> quoted and thus * quote-escaped. */ public void close() { try { if (m_out != null) { m_out.flush(); } } catch (IOException ioe) { System.err.println(ioe.getMessage()); } finally { if (m_out != null) { try { if (m_out != null) { m_out.close(); } } catch (IOException ioe) { System.err.println(ioe.getMessage()); } m_out = null; } } } /** * Predicate to check, if the connection with the catalog's implementation * is still active. This helps determining, if it makes sense to call * <code>close()</code>. * * @return true, if the implementation is disassociated, false otherwise. * @see #close() */ public boolean isClosed() { return (m_out == null); } public String lookup(String lfn, String handle) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public Collection lookup(String lfn) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public Set lookupNoAttributes(String lfn) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public Map lookup(Set lfns) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public Map lookupNoAttributes(Set lfns) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public Map lookup(Set lfns, String handle) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public Map lookupNoAttributes(Set lfns, String handle) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public Map lookup(Map constraints) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public Set list() { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public Set list(String constraint) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } /** * Inserts a new mapping into the replica catalog. Any existing mapping of * the same LFN, PFN, and HANDLE will be replaced, including all of its * attributes. * * @param lfn is the logical filename under which to book the entry. * @param tuple is the physical filename and associated PFN attributes. * * @return number of insertions, should always be 1. On failure, throw an * exception, don't use zero. */ public int insert(String lfn, ReplicaCatalogEntry tuple) { if (lfn == null || tuple == null) { throw new NullPointerException(); } write(writeReplicaCatalogEntry(lfn, tuple)); return 1; } /** * Inserts a new mapping into the replica catalog. This is a convenience * function exposing the resource handle. Internally, the * <code>ReplicaCatalogEntry</code> element will be constructed, and passed * to the appropriate insert function. * * @param lfn is the logical filename under which to book the entry. * @param pfn is the physical filename associated with it. * @param handle is a resource handle where the PFN resides. * @return number of insertions, should always be 1. On failure, throw an * exception, don't use zero. * @see #insert(String, ReplicaCatalogEntry ) * @see ReplicaCatalogEntry */ public int insert(String lfn, String pfn, String handle) { if (lfn == null || pfn == null || handle == null) { throw new NullPointerException(); } write(writeReplicaCatalogEntry(lfn, new ReplicaCatalogEntry(pfn, handle))); return 1; } /** * Inserts multiple mappings into the replica catalog. The input is a map * indexed by the LFN. The value for each LFN key is a collection of replica * catalog entries. Note that this operation will not replace existing * entries. * * @param x is a map from logical filename string to list of replica catalog * entries. * @return the number of insertions. * @see org.griphyn.common.catalog.ReplicaCatalogEntry */ public int insert(Map x) { int result = 0; // shortcut sanity if (x == null || x.isEmpty()) { return result; } for (Iterator i = x.keySet().iterator(); i.hasNext();) { String lfn = (String) i.next(); Object val = x.get(lfn); if (val instanceof ReplicaCatalogEntry) { write(writeReplicaCatalogEntry(lfn, (ReplicaCatalogEntry) val)); } else { // this is how it should have been for (Iterator j = ((Collection) val).iterator(); j.hasNext();) { ReplicaCatalogEntry rce = (ReplicaCatalogEntry) j.next(); write(writeReplicaCatalogEntry(lfn, (ReplicaCatalogEntry) val)); } } } return result; } private void write(String content) { try { m_out.write(content); flush(); } catch (IOException ioe) { System.err.println(ioe.getMessage()); } } public int delete(String lfn, String pfn) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public int delete(Map x, boolean matchAttributes) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public int delete(String lfn, ReplicaCatalogEntry tuple) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public int delete(String lfn, String name, Object value) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public int deleteByResource(String lfn, String handle) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public int remove(String lfn) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public int remove(Set lfns) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public int removeByAttribute(String name, Object value) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public int removeByAttribute(String handle) { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } public int clear() { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } /** * Returns the file source. * * @return the file source if it exists , else null */ public java.io.File getFileSource() { throw new java.lang.UnsupportedOperationException( "Method not implemented"); } }