001: /**
002: * This file or a portion of this file is licensed under the terms of
003: * the Globus Toolkit Public License, found at $PEGASUS_HOME/GTPL or
004: * http://www.globus.org/toolkit/download/license.html.
005: * This notice must appear in redistributions of this file
006: * with or without modification.
007: *
008: * Redistributions of this Software, with or without modification, must reproduce
009: * the GTPL in:
010: * (1) the Software, or
011: * (2) the Documentation or
012: * some other similar material which is provided with the Software (if any).
013: *
014: * Copyright 1999-2004
015: * University of Chicago and The University of Southern California.
016: * All rights reserved.
017: */package edu.isi.pegasus.planner.ranking;
018:
019: import org.griphyn.cPlanner.common.PegasusProperties;
020: import org.griphyn.cPlanner.common.LogManager;
021:
022: import org.griphyn.common.util.VDSProperties;
023:
024: import org.griphyn.common.catalog.Catalog;
025:
026: import java.util.Properties;
027: import java.util.Collection;
028: import java.util.Enumeration;
029: import java.util.LinkedList;
030:
031: import java.io.PrintWriter;
032: import java.io.FileWriter;
033: import java.io.IOException;
034: import java.io.File;
035:
036: import java.sql.Connection;
037: import java.sql.PreparedStatement;
038: import java.sql.SQLException;
039: import java.sql.DriverManager;
040: import java.sql.ResultSet;
041:
042: /**
043: * This class is responsible for the fetching the DAX'es on the basis of the
044: * request ID's from the Windward Provenance Tracking Catalog. If there are
045: * more than one way's to get the DAX's then it should be an interface.
046: *
047: * @author Karan Vahi
048: * @version $Revision: 341 $
049: */
050: public class GetDAX {
051: /**
052: * Prefix for the property subset to use with this catalog.
053: */
054: public static final String c_prefix = "pegasus.catalog.provenance.windward";
055:
056: /**
057: * The DB Driver properties prefix.
058: */
059: public static final String DB_PREFIX = "pegasus.catalog.provenance.windward.db";
060:
061: /**
062: * The statement to prepare to slurp attributes.
063: */
064: private static final String mCStatements[] = {
065: // 0:
066: "SELECT dax FROM instances_and_daxes WHERE request_id=?", };
067:
068: /**
069: * Maintains the connection to the database over the lifetime of
070: * this instance.
071: */
072: private Connection mConnection = null;
073:
074: /**
075: * Maintains an essential set of prepared statement, ready to use.
076: */
077: private PreparedStatement mStatements[] = null;
078:
079: /**
080: * The properties passed to the client.
081: */
082: private Properties mProps;
083:
084: /**
085: * The instance to the Logging manager.
086: */
087: private LogManager mLogger;
088:
089: /**
090: * The default constructor.
091: */
092: public GetDAX() {
093: mLogger = LogManager.getInstance();
094: // make connection defunct
095: mConnection = null;
096: mStatements = null;
097:
098: }
099:
100: /**
101: * A convenience method to connect on the basis of PegasusProperties.
102: * Eventually this logic should go in the invoking code or factory.
103: *
104: * @param properties PegasusProperties
105: *
106: * @return boolean
107: */
108: public boolean connect(PegasusProperties properties) {
109: VDSProperties props = properties.getVDSProperties();
110: Properties connect = props.matchingSubset(this .c_prefix, false);
111:
112: //get the default db driver properties in first pegasus.catalog.*.db.driver.*
113: Properties db = props.matchingSubset(Catalog.DB_ALL_PREFIX,
114: false);
115: //now overload with the work catalog specific db properties.
116: //pegasus.catalog.work.db.driver.*
117: db.putAll(props.matchingSubset(this .DB_PREFIX, false));
118:
119: //to make sure that no confusion happens.
120: //add the db prefix to all the db properties
121: for (Enumeration e = db.propertyNames(); e.hasMoreElements();) {
122: String key = (String) e.nextElement();
123: connect.put("db." + key, db.getProperty(key));
124: }
125: return connect(connect);
126: }
127:
128: /**
129: * Establishes a connection to the database from the properties. You
130: * can specify a <tt>driver</tt> property to contain the class name of
131: * the JDBC driver for your database. This property will be removed
132: * before attempting to connect. You must speficy a <tt>url</tt>
133: * property to describe the connection. It will be removed before
134: * attempting to connect.
135: *
136: * @param props is the property table with sufficient settings to
137: * establish a link with the database. The minimum key required key is
138: * "url", and possibly "driver". Any other keys depend on the database
139: * driver.
140: *
141: * @return true if connected, false if failed to connect.
142: *
143: * @see java.sql.DriverManager#getConnection( String, Properties )
144: *
145: * @throws Error subclasses for runtime errors in the class loader.
146: */
147: public boolean connect(Properties props) {
148: boolean result = false;
149: // class loader: Will propagate any runtime errors!!!
150: String driver = (String) props.remove("db.driver");
151: Properties localProps = VDSProperties.matchingSubset(
152: (Properties) props.clone(), "db", false);
153:
154: String url = (String) localProps.remove("url");
155: if (url == null) {
156: //try to construct the jdbc string from the properties
157: url = getJDBCURL(driver, localProps);
158: }
159: if (url == null || url.length() == 0) {
160: return result;
161: }
162:
163: try {
164: if (driver != null) {
165: //only support mysql and postgres for time being
166: if (driver.equalsIgnoreCase("MySQL")) {
167: driver = "com.mysql.jdbc.Driver";
168: } else if (driver.equalsIgnoreCase("Postgres")) {
169: driver = "org.postgresql.Driver";
170: }
171:
172: mLogger.log(
173: "Driver being used to connect to Work Catalog is "
174: + driver,
175: LogManager.DEBUG_MESSAGE_LEVEL);
176:
177: Class.forName(driver);
178: }
179: } catch (Exception e) {
180: mLogger.log(
181: "While connecting to Windward Provenance Catalog",
182: e, LogManager.DEBUG_MESSAGE_LEVEL);
183: return result;
184: }
185:
186: try {
187: mLogger.log("Connecting with jdbc url " + url,
188: LogManager.DEBUG_MESSAGE_LEVEL);
189: mConnection = DriverManager.getConnection(url, localProps);
190: // m_autoinc = mConnection.getMetaData().supportsGetGeneratedKeys();
191:
192: // prepared statements are Singletons -- prepared on demand
193: mStatements = new PreparedStatement[mCStatements.length];
194: for (int i = 0; i < mCStatements.length; ++i) {
195: mStatements[i] = null;
196: }
197:
198: result = true;
199: } catch (SQLException e) {
200: mLogger.log("While Windward Provenance Catalog", e,
201: LogManager.DEBUG_MESSAGE_LEVEL);
202: result = false;
203: }
204:
205: return result;
206: }
207:
208: /**
209: * Constructs the jdbc url on the basis fo the driver and db properties.
210: *
211: * @param driver the driver being used.
212: * @param properties the db properites
213: *
214: * @return the jdbc url, else null if unable to construct
215: */
216: protected String getJDBCURL(String driver, Properties properties) {
217: if (driver == null) {
218: return null;
219: }
220:
221: StringBuffer result = new StringBuffer();
222: result.append("jdbc:").append(driver.toLowerCase()).append(
223: "://");
224:
225: String hostname = (String) properties.remove("hostname");
226: if (hostname == null || hostname.length() == 0) {
227: return null;
228: }
229: result.append(hostname);
230:
231: String database = (String) properties.remove("database");
232: if (database == null || database.length() == 0) {
233: return null;
234: }
235: result.append("/").append(database);
236:
237: return result.toString();
238:
239: }
240:
241: /**
242: * Given a request ID it fetches the DAX's from the DB and writes out to
243: * the directory passed.
244: *
245: * @param id the request id.
246: * @param dir the directory where the DAX'es need to be placed.
247: *
248: * @return a Collection of basenames fo the DAX'es placed in the directory.
249: */
250: public Collection<String> get(String id, String dir) {
251: if (isClosed()) {
252: throw new RuntimeException(
253: "The connection to backend database is closed");
254: }
255:
256: //if
257: if (dir == null) {
258: throw new RuntimeException(
259: "Unable to write out to null directory");
260: }
261:
262: Collection result = new LinkedList();
263:
264: //get the prepared statement
265: int which = 0;
266: try {
267: //do sanity check on dir
268: sanityCheck(new File(dir));
269:
270: PreparedStatement ps = getStatement(which);
271: ps.setString(1, id);
272:
273: mLogger.log("Executing query " + ps.toString(),
274: LogManager.DEBUG_MESSAGE_LEVEL);
275: ResultSet rs = ps.executeQuery();
276: int index = 0;
277: while (rs.next()) {
278: index++;
279: String xml = rs.getString(1);
280:
281: //construct the name of the file on index and
282: //request id only.
283: StringBuffer name = new StringBuffer();
284: name.append(id).append("_").append(index);
285: name.append(".dax");
286:
287: //pipe the dax to the directory.
288: File dax = new File(dir, name.toString());
289: PrintWriter pw = new PrintWriter(new FileWriter(dax));
290: pw.println(xml);
291: pw.close();
292:
293: //add to the result
294: result.add(dax.getAbsolutePath());
295: }
296: rs.close();
297: } catch (SQLException e) {
298: throw new RuntimeException(
299: "Unable to query from Windward Provenance Catalog ",
300: e);
301: } catch (IOException ioe) {
302: throw new RuntimeException(
303: "IOException while trying to write to dir " + dir,
304: ioe);
305: }
306:
307: return result;
308: }
309:
310: /**
311: * Predicate to check, if the connection with the catalog's
312: * implementation is still active. This helps determining, if it makes
313: * sense to call <code>close()</code>.
314: *
315: * @return true, if the implementation is disassociated, false otherwise.
316: * @see #close()
317: */
318: public boolean isClosed() {
319: return (mConnection == null);
320: }
321:
322: /**
323: * Explicitely free resources before the garbage collection hits.
324: */
325: public void close() {
326:
327: if (mConnection != null) {
328: try {
329: if (!mConnection.getAutoCommit()) {
330: mConnection.commit();
331: }
332: } catch (SQLException e) {
333: // ignore
334: }
335: }
336:
337: if (mStatements != null) {
338: try {
339: for (int i = 0; i < mCStatements.length; ++i) {
340: if (mStatements[i] != null) {
341: mStatements[i].close();
342: mStatements[i] = null;
343: }
344: }
345: } catch (SQLException e) {
346: // ignore
347: } finally {
348: mStatements = null;
349: }
350: }
351:
352: if (mConnection != null) {
353: try {
354: mConnection.close();
355: } catch (SQLException e) {
356: // ignore
357: } finally {
358: mConnection = null;
359: }
360: }
361: }
362:
363: /**
364: * Singleton manager for prepared statements. This instruction
365: * checks that a prepared statement is ready to use, and will
366: * create an instance of the prepared statement, if it was unused
367: * previously.
368: *
369: * @param i is the index which prepared statement to check.
370: * @return a handle to the prepared statement.
371: *
372: *
373: * @throws SQLException in case of unable to delete entry.
374: */
375: protected PreparedStatement getStatement(int i) throws SQLException {
376: if (mStatements[i] == null) {
377: mStatements[i] = mConnection
378: .prepareStatement(mCStatements[i]);
379: } else {
380: mStatements[i].clearParameters();
381: }
382:
383: return mStatements[i];
384: }
385:
386: /**
387: * Checks the destination location for existence, if it can
388: * be created, if it is writable etc.
389: *
390: * @param dir is the new base directory to optionally create.
391: *
392: * @throws IOException in case of error while writing out files.
393: */
394: protected static void sanityCheck(File dir) throws IOException {
395: if (dir.exists()) {
396: // location exists
397: if (dir.isDirectory()) {
398: // ok, isa directory
399: if (dir.canWrite()) {
400: // can write, all is well
401: return;
402: } else {
403: // all is there, but I cannot write to dir
404: throw new IOException(
405: "Cannot write to existing directory "
406: + dir.getPath());
407: }
408: } else {
409: // exists but not a directory
410: throw new IOException("Destination " + dir.getPath()
411: + " already "
412: + "exists, but is not a directory.");
413: }
414: } else {
415: // does not exist, try to make it
416: if (!dir.mkdirs()) {
417: throw new IOException("Unable to create directory "
418: + dir.getPath());
419: }
420: }
421: }
422:
423: /**
424: * For Testing purposes only.
425: *
426: * @param args the arguments passed.
427: */
428: public static void main(String[] args) {
429: GetDAX d = new GetDAX();
430: LogManager.getInstance().setLevel(
431: LogManager.DEBUG_MESSAGE_LEVEL);
432:
433: System.out.println("Connecting to database "
434: + d.connect(PegasusProperties.getInstance()));
435:
436: //d.get( "RPaper-ModelerThenClassifier-d3206cf5-b3ad-4c9d-9f08-5d25653d5ccf", null );
437: Collection daxes = d
438: .get(
439: "RPaper-ModelerThenClassifier-a93169ee-ed72-4d4b-be99-f6d69ae29e04",
440: "/tmp/wings");
441: System.out.println("DAX'es written out are " + daxes);
442: d.close();
443: }
444:
445: }
|