001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026: package org.cougaar.glm.ldm;
027:
028: import org.cougaar.core.mts.MessageAddress;
029: import org.cougaar.core.service.LoggingService;
030: import org.cougaar.mlm.plugin.ldm.LDMEssentialPlugin;
031: import org.cougaar.planning.ldm.asset.Asset;
032: import org.cougaar.planning.ldm.asset.NewTypeIdentificationPG;
033: import org.cougaar.util.DBConnectionPool;
034: import org.cougaar.util.Parameters;
035:
036: import java.io.BufferedReader;
037: import java.io.InputStreamReader;
038: import java.sql.Connection;
039: import java.sql.ResultSet;
040: import java.sql.ResultSetMetaData;
041: import java.sql.SQLException;
042: import java.sql.Statement;
043: import java.util.Enumeration;
044: import java.util.Hashtable;
045: import java.util.Vector;
046:
047: /**
048: * Provides wrappers around the query routines. Takes care of a few LDMPlugin calls. Expects to have one Plugin argument
049: * - the name of the file describing the queries ex. SpecialQueryLDMPlugin(specialQueryDescription.q)
050: * @see #parseQueryFile(String queryFile) for a description of the query file.
051: */
052: public abstract class QueryLDMPlugin extends LDMEssentialPlugin {
053: protected Hashtable fileParameters_ = new Hashtable();
054: protected String url_, user_, password_;
055: protected MessageAddress clusterId_;
056: protected Vector cannotHandle_ = new Vector();
057: protected String className_ = null;
058: protected Hashtable myParams_ = new Hashtable();
059: protected LoggingService logger;
060: //WAN the type of database we are connecting to (Oracle, MySQL...)
061: protected String dbType; //WAN
062:
063: /**
064: * Parse plugIn arguments, initialize driver, get cluster identifier.
065: */
066: // Get the logger service via reflection
067: public void setLoggingService(LoggingService loggingService) {
068: logger = loggingService;
069: }
070:
071: protected void setupSubscriptions() {
072: parseArguments();
073: initializeDriver();
074: clusterId_ = getMessageAddress();
075: className_ = this .getClass().getName();
076: int indx = className_.lastIndexOf(".");
077: if (indx > -1) {
078: className_ = className_.substring(indx + 1);
079: }
080:
081: }
082:
083: /**
084: * quick check for all things it should be able to handle
085: */
086: abstract public boolean canHandle(String typeid, Class class_hint);
087:
088: /**
089: * Called by getPrototype() to actually create the prototype.
090: */
091: abstract public Asset makePrototype(java.lang.String aTypeName,
092: java.lang.Class anAssetClassHint);
093:
094: /**
095: * calls makePrototype(). calls fillProperties(). caches result
096: */
097: public Asset getPrototype(String aTypeName, Class anAssetClassHint) {
098: if (!canHandle(aTypeName, anAssetClassHint))
099: return null;
100: // check if thought it could handle it, but previously failed
101: if (cannotHandle_.contains(aTypeName)) {
102: return null;
103: }
104: Asset asset = makePrototype(aTypeName, anAssetClassHint);
105: if (asset == null) {
106: cannotHandle_.add(aTypeName);
107: return null;
108: }
109: // SHOULD NOT NEED TO BE CALLED!
110: fillProperties(asset);
111: getLDM().cachePrototype(aTypeName, asset);
112: return asset;
113: }
114:
115: /**
116: * Calls LDM factory to create prototype and set nomenclature and alternameTypeIdentification.
117: * @param typeid type identification of desired asset
118: * @param type the class name of the desired asset
119: * @param nomenclature *
120: */
121: protected Asset newAsset(String typeid, String type,
122: String nomenclature) {
123: Asset proto;
124: try {
125: if (typeid == null)
126: throw new IllegalArgumentException("newResource : "
127: + typeid + " nomenclature: " + nomenclature);
128: proto = getLDM().getFactory().createPrototype(type, typeid);
129: // GLMDebug.DEBUG(className_, "newResource(), typeid "+typeid+", nomenclature "+
130: // nomenclature);
131: if (proto == null) {
132: if (logger.isDebugEnabled()) {
133: logger
134: .debug("newResource(), could not create prototype: "
135: + typeid);
136: }
137: return null;
138: }
139: if (nomenclature != null) {
140: NewTypeIdentificationPG pg = (NewTypeIdentificationPG) proto
141: .getTypeIdentificationPG();
142: pg.setNomenclature(nomenclature);
143: pg.setAlternateTypeIdentification(pg
144: .getTypeIdentification());
145: } else {
146: if (logger.isDebugEnabled()) {
147: logger.debug("newResource: " + typeid
148: + " no nomenclature ");
149: }
150: }
151: } catch (Exception ee) {
152: if (logger.isErrorEnabled()) {
153: logger.error("newResource() could not make " + typeid
154: + " " + ee.toString());
155: }
156: ee.printStackTrace();
157: return null;
158: }
159: return proto;
160: }
161:
162: private String getDBType(String databaseVal) {
163: int colonIndex1 = databaseVal.indexOf(':');
164: int colonIndex2 = databaseVal.indexOf(':', colonIndex1 + 1);
165: return databaseVal.substring(colonIndex1 + 1, colonIndex2);
166: }
167:
168: // retrieve and parse the arguments
169: // Currently expecting the query file as the only argument.
170: protected void parseArguments() {
171: // first, initialize the global table with some basics
172: Vector pv = getParameters();
173: if (pv == null) {
174: throw new RuntimeException(
175: "QueryLDMPlugin requires at least one parameter");
176: } else {
177: String s;
178: for (Enumeration ps = pv.elements(); ps.hasMoreElements();) {
179: s = (String) ps.nextElement();
180: if (s.indexOf('=') != -1) {
181: int i = s.indexOf('=');
182: String key = new String(s.substring(0, i));
183: String value = new String(s.substring(i + 1, s
184: .length()));
185: myParams_.put(key.trim(), value.trim());
186: } else if (s.charAt(0) == '+') {
187: myParams_.put(new String(s.substring(1)),
188: new Boolean(true));
189: } else if (s.charAt(0) == '-') {
190: myParams_.put(new String(s.substring(1)),
191: new Boolean(false));
192: } else {
193: // If it was not a key/value pair then it is a query file
194: parseQueryFile(s);
195: }
196: }
197: }
198: }
199:
200: /**
201: * parses query file into local hashtable
202: * @param queryFile name of query file The query file is expected to be in the format param_name=param_value The
203: * necessary parameters are: Database, Driver, Username, Password Some parameters are not currently
204: * used, but may be in the future. These inclue MIN_IN_POOL, MAX_IN_POOL, TIMEOUT, and
205: * NUMBER_OF_TRIES. The rest of the lines are expected to be the query statements. Here is an example
206: * from icisParts.q. Database = ${org.cougaar.database} Username = ${icis.database.user} Password =
207: * ${icis.database.password} MIN_IN_POOL= 1 MAX_IN_POOL= 4 TIMEOUT= 1 NUMBER_OF_TRIES= 2
208: * headerQuery=select commodity, nsn, nomenclature, ui, ssc, price, icc, alt, plt, pcm, boq, diq,
209: * iaq, nso, qfd, rop, owrmrp, weight, cube, aac, slq from header where NSN = :nsns
210: * assetsQuery=select nsn, ric, purpose, condition, iaq from assets where NSN = :nsns nomen=select
211: * nomenclature from header where NSN = :nsns cost=select price from header where NSN = :nsns
212: */
213: protected void parseQueryFile(String queryFile) {
214: try {
215: BufferedReader in = new BufferedReader(
216: new InputStreamReader(getConfigFinder().open(
217: queryFile)));
218: for (String line = in.readLine(); line != null; line = in
219: .readLine()) {
220: line = line.trim();
221: // skip empty lines
222: if (line.length() == 0)
223: continue;
224:
225: // handle continuation lines
226: int len;
227: while ((len = line.length()) > 0
228: && '\\' == line.charAt(len - 1)) {
229: line = (line.substring(0, len - 1))
230: + (in.readLine().trim());
231: }
232:
233: char c = line.charAt(0);
234:
235: // skip comments
236: if (c == '#')
237: continue;
238: else if (line.indexOf("select ") != -1) { //WAN if the word "select " is found process as if query
239: //String queryType = line.substring(1).trim(); // skip the <
240: int equalsIndex = line.indexOf('='); //find the =
241: String queryType = line.substring(0, equalsIndex);
242: int dotIndex = queryType.indexOf(".");
243: if (dotIndex != -1)
244: queryType = queryType.substring(dotIndex + 1)
245: .trim();
246: else
247: queryType = "default";
248:
249: //only process query if it is a default query or it is a query for the type of database that is being used
250: if (queryType.equals("default")) {
251: // I don't think this needs to be done -- llg
252: //line = Parameters.replaceParameters(line);
253: parseQueryLine(line);
254: } else if (queryType.equalsIgnoreCase(dbType)) {
255: String startQuery = line.substring(0, dotIndex);
256: String endQuery = line.substring(equalsIndex);
257: String newLine = startQuery + endQuery;
258: //newLine = Parameters.replaceParameters(line);
259: // I don't think this needs to be done -- llg
260: parseQueryLine(newLine, true);
261: } else
262: //skip if this query does not match this database type and is not a default query (WAN)
263: continue;
264: } else {
265: // should be a param=value line
266: line = Parameters.replaceParameters(line);
267: parseQueryLine(line);
268: }
269: }
270: in.close();
271:
272: } catch (Exception e) {
273: if (logger.isErrorEnabled()) {
274: logger.error("Error reading '" + queryFile + "': " + e);
275: }
276: e.printStackTrace();
277: throw new RuntimeException("No QueryFile: " + e);
278: }
279: }
280:
281: protected void parseQueryLine(String s) {
282: int i = s.indexOf('=');
283: if (i < 1) {
284: if (logger.isErrorEnabled()) {
285: logger
286: .error(" parseQueryLine cannot parse <" + s
287: + ">");
288: }
289: return;
290: }
291: String p = s.substring(0, i).trim();
292: String v = s.substring(i + 1).trim();
293: if (p.equalsIgnoreCase("database")) {//WAN added
294: String realVal = Parameters.replaceParameters(v); //WAN added
295: dbType = getDBType(realVal); //WAN
296: }
297: if (!fileParameters_.containsKey(p)) {
298: fileParameters_.put(p, v);
299: }
300: }
301:
302: // force the database specific query
303: protected void parseQueryLine(String s, boolean dbSpecific) {
304: int i = s.indexOf('=');
305: if (i < 1) {
306: if (logger.isErrorEnabled()) {
307: logger
308: .error(" parseQueryLine cannot parse <" + s
309: + ">");
310: }
311: return;
312: }
313: String p = s.substring(0, i).trim();
314: String v = s.substring(i + 1).trim();
315: if (dbSpecific)
316: fileParameters_.put(p, v);
317: }
318:
319: // document fileParameters_ hashtable
320: protected String getParm(String name) {
321: String value = (String) fileParameters_.get(name);
322: if (value == null) {
323: throw new RuntimeException(this .toString()
324: + ": Couldn't initialize Driver need to specify "
325: + name);
326: }
327: return value;
328: }
329:
330: // initialize driver and obtain info to execute a query
331: protected void initializeDriver() {
332: //String driverName = getParm("Driver"); //WAN - removed this to get Driver from database name
333: url_ = getParm("Database");
334: user_ = getParm("Username");
335: password_ = getParm("Password");
336: // int colonIndex1 = url_.indexOf(':'); //WAN
337: // int colonIndex2 = url_.indexOf(':', colonIndex1+1); //WAN
338:
339: //String dbtype = url_.substring(colonIndex1+1, colonIndex2); //delete later
340: if (dbType == null) {
341: throw new RuntimeException(
342: "No Connection parameter. - No dbtype");
343: }
344:
345: String driverName = Parameters
346: .findParameter("driver." + dbType);//WAN must go directly to cougaar.rc now
347:
348: if (driverName != null) {
349: try {
350: DBConnectionPool.registerDriver(driverName);
351: } catch (Exception e) {
352: System.err.println("Could not register driver "
353: + driverName + ":");
354: e.printStackTrace();
355: }
356: }
357: url_ = getParm("Database");
358: user_ = getParm("Username");
359: password_ = getParm("Password");
360:
361: // PAS MIK - COUGAAR Node scope for connection pools
362: // int minPoolSize= Integer.parseInt(getParm("MIN_IN_POOL"));
363: // int maxPoolSize= Integer.parseInt(getParm("MAX_IN_POOL"));
364: // int timeout= Integer.parseInt(getParm("TIMEOUT"));
365: // String queryFile = "";
366: // int nTries= Integer.parseInt(getParm("NUMBER_OF_TRIES"));
367: //try {
368:
369: }
370:
371: /**
372: * @return Vector<Object[]> of results or null (on failure)
373: */
374: public Vector executeQuery(String query) {
375: Vector result = new Vector();
376: ResultSet rs = null;
377: Connection conn = null;
378: try {
379: conn = getConnection();
380: Statement statement = conn.createStatement();
381: rs = statement.executeQuery(query);
382: ResultSetMetaData md = rs.getMetaData();
383: int ncols = md.getColumnCount();
384: Object row[] = new Object[ncols];
385: while (rs.next()) {
386: for (int i = 0; i < ncols; i++) {
387: row[i] = rs.getObject(i + 1);
388: }
389: result.add((Object[]) row.clone());
390: }
391: /*
392: if ((ncols*result.size())>100) {
393: System.err.println("Excessively large query ("+(ncols*result.size())+"): ");
394: Thread.dumpStack();
395: }
396: */
397: statement.close();
398: } catch (java.sql.SQLException sqle) {
399: if (logger.isErrorEnabled()) {
400: logger.error("executeQuery failed: " + sqle);
401: }
402: sqle.printStackTrace();
403: return null;
404: } finally {
405: if (conn != null)
406: releaseConnection(conn);
407: }
408: return result;
409: }
410:
411: public interface RowHandler {
412: /**
413: * Called by executeQuery per row.
414: */
415: void execute(ResultSetMetaData md, ResultSet rs)
416: throws SQLException;
417: }
418:
419: public static class QueryComplete extends RuntimeException {
420: }
421:
422: /**
423: * Less expensive variation on executeQuery(String) *
424: */
425: public void executeQuery(String query, RowHandler rh) {
426: Connection conn = null;
427: Statement statement = null;
428: try {
429: conn = getConnection();
430: statement = conn.createStatement();
431: ResultSet rs = statement.executeQuery(query);
432: /*
433: try {
434: rs.setFetchDirection(ResultSet.FETCH_FORWARD);
435: rs.setFetchSize(1000);
436: } catch (AbstractMethodError re) {
437: // System.err.println("Driver is not JDBC 2.0 compatible");
438: }
439: */
440:
441: ResultSetMetaData md = rs.getMetaData();
442: try {
443: while (rs.next()) {
444: rh.execute(md, rs);
445: }
446: } catch (QueryComplete qc) {
447: // early exit.
448: }
449: //statement.close(); // close it in finally clause
450: } catch (java.sql.SQLException sqle) {
451: if (logger.isErrorEnabled()) {
452: logger.error("executeQuery failed: " + sqle);
453: }
454: sqle.printStackTrace();
455: } finally {
456: if (statement != null) {
457: try {
458: statement.close();
459: } catch (SQLException squeal) {
460: }
461: }
462: if (conn != null)
463: releaseConnection(conn);
464: }
465: }
466:
467: protected Connection getConnection() throws SQLException {
468: return DBConnectionPool.getConnection(url_, user_, password_);
469: }
470:
471: protected void releaseConnection(Connection conn) {
472: try {
473: conn.close();
474: } catch (java.sql.SQLException sqle) {
475: if (logger.isErrorEnabled()) {
476: logger.error("releaseConnection " + sqle);
477: }
478: }
479: }
480: }
|