001: /*
002: * <copyright>
003: *
004: * Copyright 2001-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.tools.csmart.core.db;
027:
028: import org.cougaar.tools.csmart.core.property.Property;
029: import org.cougaar.tools.csmart.core.property.name.CompositeName;
030: import org.cougaar.tools.csmart.recipe.ComplexRecipeComponent;
031: import org.cougaar.tools.csmart.recipe.RecipeComponent;
032: import org.cougaar.tools.csmart.ui.viewer.CSMART;
033: import org.cougaar.util.ConfigFinder;
034: import org.cougaar.util.DBConnectionPool;
035: import org.cougaar.util.DBProperties;
036: import org.cougaar.util.Parameters;
037: import org.cougaar.util.log.Logger;
038:
039: import java.io.File;
040: import java.io.FileNotFoundException;
041: import java.io.FileWriter;
042: import java.io.IOException;
043: import java.io.ObjectInputStream;
044: import java.io.PrintWriter;
045: import java.sql.Connection;
046: import java.sql.PreparedStatement;
047: import java.sql.ResultSet;
048: import java.sql.SQLException;
049: import java.sql.Statement;
050: import java.text.DateFormat;
051: import java.text.DecimalFormat;
052: import java.text.SimpleDateFormat;
053: import java.util.Date;
054: import java.util.HashMap;
055: import java.util.Iterator;
056: import java.util.Map;
057:
058: /**
059: * This class takes a structure of ComponentData objects and populates
060: * the configuration database with some or all of the components
061: * described by the data. The selection of applicable components is
062: * still an issue.
063: * @property csmart.PopulateDb.log.enable if true enables the logging
064: * of executed queries to a file named PopulateDb<datetime>.log.
065: **/
066: public class PDbBase {
067: public static final int RECIPE_STATUS_ABSENT = 0;
068: public static final int RECIPE_STATUS_EXISTS = 1;
069: public static final int RECIPE_STATUS_DIFFERS = 2;
070:
071: public static final String QUERY_FILE = "PopulateDb.q";
072:
073: private static final String PROP_LOG_QUERIES = "csmart.PopulateDb.log.enable";
074: private static final String DFLT_LOG_QUERIES = "false";
075: private static boolean logQueries = System.getProperty(
076: PROP_LOG_QUERIES, DFLT_LOG_QUERIES).equals("true");
077:
078: private transient Logger log;
079:
080: public static final String COMPLEX_RECIPE_DESC = "Complex Recipe Component";
081:
082: protected Map substitutions = new HashMap() {
083: public Object put(Object key, Object val) {
084: if (val == null)
085: throw new IllegalArgumentException("Null value for "
086: + key);
087: return super .put(key, val);
088: }
089: };
090: protected DBProperties dbp;
091:
092: protected Connection dbConnection;
093: private Statement stmt;
094: protected Statement updateStmt;
095: protected boolean debug = false;
096: protected PrintWriter pwlog;
097:
098: protected static long rQFileLastMod = 0l; // When was recipeQueries.q last modified
099:
100: /**
101: * Constructor
102: **/
103: public PDbBase() throws SQLException, IOException {
104:
105: createLogger();
106: if (logQueries)
107: pwlog = new PrintWriter(new FileWriter(getLogName()));
108: dbp = DBProperties.readQueryFile(QUERY_FILE, "csmart").unlock();
109:
110: // When was the RQ file last modified?
111: File rqfile = ConfigFinder.getInstance("csmart").locateFile(
112: RecipeComponent.RECIPE_QUERY_FILE);
113: long newMod = 0l;
114: if (rqfile != null) {
115: try {
116: newMod = rqfile.lastModified();
117: } catch (SecurityException se) {
118: }
119: }
120:
121: // Only read in the RQ file if it was modified since we last read it in
122:
123: // Hack for 10.4.1: Since rQFileLastMod is static and DBP.unlock returns a copy
124: // of the original without the recipeQueries.q, the second time we create a PDbBase,
125: // we wont reparse the recipeQueries.q and will have a DBP that does not include
126: // the queries in your recipeQueries.q
127: // This hack means you will _always_ re-parse the recipeQueries.q file
128: if (true) {
129: //if (newMod != rQFileLastMod) {
130: try {
131: // If this wasnt our first read
132: // But the recipeQueries.q file has changed,
133: // Then force a re-read of everything, thus getting rid of any old
134: // queries under old names
135: if (rQFileLastMod != 0l) {
136: if (log.isDebugEnabled()) {
137: log
138: .debug("Doing forced reread of query files.");
139: }
140: dbp = DBProperties.reReadQueryFile(QUERY_FILE,
141: "csmart").unlock();
142: }
143:
144: // This next line _always_ re-parses the query file.
145: dbp.addQueryFile(RecipeComponent.RECIPE_QUERY_FILE,
146: "csmart");
147: rQFileLastMod = newMod;
148: } catch (FileNotFoundException e) {
149: // This is normal if a user has no separate recipe query file.
150: if (log.isDebugEnabled()) {
151: log.debug("No " + RecipeComponent.RECIPE_QUERY_FILE
152: + " file found.");
153: }
154: }
155: }
156:
157: // dbp.setDebug(true);
158: String database = dbp.getProperty("database");
159: String username = dbp.getProperty("username");
160: String password = dbp.getProperty("password");
161: String dbtype = dbp.getDBType();
162: String driverParam = "driver." + dbtype;
163: String driverClass = Parameters.findParameter(driverParam);
164: if (driverClass == null)
165: throw new SQLException("Unknown driver " + driverParam);
166: try {
167: Class.forName(driverClass);
168: } catch (ClassNotFoundException cnfe) {
169: throw new SQLException("Driver class not found: "
170: + driverClass);
171: }
172: dbConnection = DBConnectionPool.getConnection(database,
173: username, password);
174: dbConnection.setAutoCommit(false);
175: stmt = dbConnection.createStatement();
176: updateStmt = dbConnection.createStatement();
177: }
178:
179: DateFormat logDateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
180:
181: private String getLogName() {
182: return "PopulateDb" + logDateFormat.format(new Date()) + ".log";
183: }
184:
185: protected Statement getStatement() throws SQLException {
186: return dbConnection.createStatement();
187: }
188:
189: /**
190: * Check the status of a recipe in the database.
191: * @param rc the RecipeComponent to check
192: * @return RECIPE_STATUS_ABSENT -- Recipe not in database<br>
193: * RECIPE_STATUS_EXISTS -- Recipe already in database with same
194: * value<br> RECIPE_STATUS_DIFFERS -- Recipe already in database
195: * with different value
196: **/
197: public int recipeExists(RecipeComponent rc) {
198: String[] recipeIdAndClass = getRecipeIdAndClass(rc
199: .getRecipeName());
200: if (recipeIdAndClass == null)
201: return RECIPE_STATUS_ABSENT;
202: else {
203: if (isRecipeEqual(recipeIdAndClass, rc))
204: return RECIPE_STATUS_EXISTS;
205: else
206: return RECIPE_STATUS_DIFFERS;
207: }
208: }
209:
210: /**
211: * Checks if the given recipe matches the recipe in the database
212: * specified by the id and class.
213: * @param String[] id and class from recipe in database
214: * @param rc the recipe component to compare against
215: * @return true if recipe class, properties, and values are the same
216: **/
217: private boolean isRecipeEqual(String[] recipeIdAndClass,
218: RecipeComponent rc) {
219: if (!recipeIdAndClass[1].equals(rc.getClass().getName())) {
220: if (log.isDebugEnabled()) {
221: log.debug("isRecipeEqual got new class "
222: + rc.getClass().getName() + " compared to "
223: + recipeIdAndClass[1]);
224: }
225: return false; // different class
226: }
227:
228: // The bulk of a Complex recipe is in the Assembly.
229: // Although inefficient, to be safe, pretend they are always different
230: if (rc instanceof ComplexRecipeComponent) {
231: if (log.isDebugEnabled()) {
232: log.debug("isRecipeEqual got a ComplexRecipe: "
233: + rc.getClass().getName());
234: }
235: return false;
236: }
237:
238: // Build up a HashMap of the current properties of the recipe.
239: // This must match how the properties are saved below in
240: // insertLibRecipe
241: Map newProps = new HashMap();
242: for (Iterator j = rc.getLocalPropertyNames(); j.hasNext();) {
243: CompositeName pname = (CompositeName) j.next();
244: Property prop = rc.getProperty(pname);
245: if (prop == null) {
246: prop = rc.getInvisibleProperty(pname);
247: if (prop == null) {
248: continue;
249: }
250: }
251: Object val = prop.getValue();
252: if (val == null)
253: continue; // Don't write null values
254: String sval = val.toString();
255: if (sval.equals(""))
256: continue; // Don't write empty values
257: String name = pname.last().toString();
258: newProps.put(name, sval);
259: }
260:
261: // Now build up a map of the current properties of the recipe.
262: // This must match OrganizerHelper.getRecipeProperties
263: Map oldProps = new HashMap();
264: substitutions.put(":recipe_id:", recipeIdAndClass[0]);
265: ResultSet rs = null;
266: try {
267: rs = executeQuery(stmt, dbp.getQuery("queryLibRecipeProps",
268: substitutions));
269: while (rs.next()) {
270: oldProps.put(rs.getString(1), rs.getString(2));
271: }
272: } catch (SQLException sqle) {
273: if (log.isErrorEnabled()) {
274: log.error("SQL Exception: ", sqle);
275: }
276: return false;
277: } finally {
278: try {
279: if (rs != null)
280: rs.close();
281: } catch (SQLException sqle2) {
282: if (log.isErrorEnabled()) {
283: log.error("SQL Exception: ", sqle2);
284: }
285: return false;
286: }
287: }
288:
289: // if (log.isDebugEnabled()) {
290: // log.debug("isRecipeEqual comparing hashes. Old is size: " + oldProps.size() + " and new is " + newProps.size());
291: // log.debug("and to equals we get: " + oldProps.equals(newProps));
292: // }
293:
294: return oldProps.equals(newProps);
295: }
296:
297: /**
298: * Is the given recipe in use by any trial in the DB.
299: * Returns true even if the contents of the recipe
300: * differ in some form.
301: * Returns false if the recipe is not in the DB at all.
302: *
303: * @param rc a <code>RecipeComponent</code> to look for
304: * @return a <code>boolean</code>, true if the recipe is in use
305: * @exception SQLException if an error occurs
306: */
307: public boolean isRecipeUsed(RecipeComponent rc) throws SQLException {
308: String[] recipeIdAndClass = getRecipeIdAndClass(rc
309: .getRecipeName());
310: return isRecipeUsed(recipeIdAndClass[0]);
311: }
312:
313: private boolean isRecipeUsed(String recipeId) throws SQLException {
314: boolean used = true;
315: substitutions.put(":recipe_id:", recipeId);
316: Statement stmt = getStatement();
317: ResultSet rs = executeQuery(stmt, dbp.getQuery(
318: "queryRecipeUsed", substitutions));
319: if (!rs.next())
320: used = false;
321: rs.close();
322: stmt.close();
323: if (log.isDebugEnabled()) {
324: log.debug("isRecipeUsed for id " + recipeId + " returning "
325: + used);
326: }
327: return used;
328: }
329:
330: /**
331: * Get the ID for the given Recipe, if it is in the DB. To be used
332: * when a save is not desired or possible.
333: * @param rc the Recipe whose ID to get
334: * @return the ID of the recipe, <code>null</code> if not in DB.
335: * @exception SQLException on error
336: **/
337: public String getLibRecipeId(RecipeComponent rc)
338: throws SQLException {
339: String[] recipeIdAndClass = getRecipeIdAndClass(rc
340: .getRecipeName());
341: if (recipeIdAndClass != null) {
342: return recipeIdAndClass[0];
343: } else
344: return null;
345: }
346:
347: /**
348: * Insures that the given recipe is in the database.
349: * If the recipe is not in the database, it writes it to the database
350: * with a new id. If the recipe is in the database, it updates
351: * the database entries if necessary.
352: * @param rc the recipe to save in the database
353: * @return the id of the recipe in the database
354: */
355: public String insureLibRecipe(RecipeComponent rc)
356: throws SQLException {
357: String[] recipeIdAndClass = getRecipeIdAndClass(rc
358: .getRecipeName());
359: if (recipeIdAndClass != null) {
360: if (isRecipeEqual(recipeIdAndClass, rc))
361: return recipeIdAndClass[0];
362: else
363: return insertLibRecipe(rc, recipeIdAndClass[0]);
364: } else
365: return insertLibRecipe(rc, null);
366: }
367:
368: public void removeLibRecipe(RecipeComponent rc) throws SQLException {
369: String[] recipeIdAndClass = getRecipeIdAndClass(rc
370: .getRecipeName());
371: if (recipeIdAndClass != null) {
372: deleteRecipeAssemblies(recipeIdAndClass[0]);
373: substitutions.put(":recipe_id:", recipeIdAndClass[0]);
374: executeUpdate(dbp.getQuery("deleteLibRecipeArgs",
375: substitutions));
376: executeUpdate(dbp
377: .getQuery("deleteLibRecipe", substitutions));
378: }
379: }
380:
381: public void removeLibRecipeNamed(String recipeName)
382: throws SQLException {
383: String[] recipeIdAndClass = getRecipeIdAndClass(recipeName);
384: if (recipeIdAndClass != null) {
385: deleteRecipeAssemblies(recipeIdAndClass[0]);
386: substitutions.put(":recipe_id:", recipeIdAndClass[0]);
387: executeUpdate(dbp.getQuery("deleteLibRecipeArgs",
388: substitutions));
389: executeUpdate(dbp
390: .getQuery("deleteLibRecipe", substitutions));
391: }
392: }
393:
394: private void deleteRecipeAssemblies(String recipeId) {
395: // Get the Recipe Assembly Id.
396: try {
397: Statement stmt = getStatement();
398: ResultSet rs = executeQuery(stmt, dbp.getQuery(
399: "queryRecipeAssemblyId", substitutions));
400: if (rs.next()) {
401: try {
402: PopulateDb.deleteSociety(rs.getString(1));
403: } catch (IOException ioe) {
404: if (log.isErrorEnabled()) {
405: log.error("Error deleting recipe assembly: "
406: + rs.getString(1) + " for recipe: "
407: + recipeId);
408: }
409: }
410: }
411:
412: rs.close();
413: stmt.close();
414:
415: } catch (SQLException sqle) {
416: if (log.isErrorEnabled()) {
417: log.error("Error deleting assembly for recipe: "
418: + recipeId);
419: }
420: }
421:
422: }
423:
424: /**
425: * Inserts the specified recipe into the database.
426: * If the given recipe id already exists in the database,
427: * then just update the database recipe (by removing it
428: * and inserting the new recipe using the same id).
429: **/
430: private String insertLibRecipe(RecipeComponent rc, String recipeId) {
431: try {
432: if (recipeId != null) {
433: // The recipe is already in the DB. We are replacing the old definition.
434: substitutions.put(":recipe_id:", recipeId);
435: substitutions.put(":java_class:", rc.getClass()
436: .getName());
437:
438: if (log.isDebugEnabled()) {
439: log.debug("insertLibRecipe updating old ID "
440: + recipeId + " with new class "
441: + rc.getClass().getName() + " and name "
442: + rc.getRecipeName());
443: }
444:
445: // Clean out the old arguments
446: executeUpdate(dbp.getQuery("deleteLibRecipeArgs",
447: substitutions));
448:
449: // Make sure the class and name are correc
450: executeUpdate(dbp.getQuery("updateLibRecipe",
451: substitutions));
452: } else {
453: // Creating a new recipe
454: recipeId = getNextId("queryMaxRecipeId", "RECIPE-");
455: substitutions.put(":recipe_id:", recipeId);
456: substitutions.put(":java_class:", rc.getClass()
457: .getName());
458: substitutions.put(":description:",
459: "No description available");
460: executeUpdate(dbp.getQuery("insertLibRecipe",
461: substitutions));
462: }
463:
464: // substitutions.put(":description:", COMPLEX_RECIPE_DESC);
465: int order = 0;
466: for (Iterator j = rc.getLocalPropertyNames(); j.hasNext();) {
467: CompositeName pname = (CompositeName) j.next();
468: Property prop = rc.getProperty(pname);
469: if (prop == null) {
470: prop = rc.getInvisibleProperty(pname);
471: if (prop == null) {
472: if (log.isErrorEnabled()) {
473: log.error("Saving recipe "
474: + rc.getRecipeName() + " under ID "
475: + recipeId
476: + " couldn't find property "
477: + pname + ". Will continue.",
478: new Throwable());
479: }
480: continue;
481: }
482: }
483: Object val = prop.getValue();
484: if (val == null)
485: continue; // Don't write null values
486: String sval = val.toString();
487: if (sval.equals(""))
488: continue; // Don't write empty values
489: String name = pname.last().toString();
490: substitutions.put(":arg_name:", name);
491: substitutions.put(":arg_value:", sval);
492: substitutions.put(":arg_order:", String
493: .valueOf(order++));
494: if (log.isDebugEnabled()) {
495: log.debug("Write prop: " + name + " and val: "
496: + sval + " to database");
497: }
498: executeUpdate(dbp.getQuery("insertLibRecipeProp",
499: substitutions));
500: }
501: return recipeId;
502: } catch (SQLException sqle) {
503: log.error("Exception saving recipe " + rc.getRecipeName()
504: + " in insertLibRecipe", sqle);
505: return null;
506: }
507: }
508:
509: private String[] getRecipeIdAndClass(String recipeName) {
510: substitutions.put(":recipe_name:", recipeName);
511: ResultSet rs = null;
512: try {
513: rs = executeQuery(stmt, dbp.getQuery(
514: "queryLibRecipeByName", substitutions));
515: if (rs.next()) {
516: String[] result = new String[] { rs.getString(1),
517: rs.getString(2) };
518: rs.close();
519: return result;
520: } else {
521: return null;
522: }
523: } catch (SQLException sqle) {
524: if (log.isErrorEnabled()) {
525: log.error("SQL Exception: ", sqle);
526: }
527: return null;
528: } finally {
529: try {
530: if (rs != null)
531: rs.close();
532: } catch (SQLException sqle2) {
533: if (log.isErrorEnabled()) {
534: log.error("SQL Exception: ", sqle2);
535: }
536: return null;
537: }
538: }
539: }
540:
541: /**
542: * Change the name of a Recipe
543: *
544: * @param oldName a <code>String</code> name of a recipe to change
545: * @param newName a <code>String</code> new name, not empty
546: * @exception SQLException if an error occurs
547: * @exception IOException if an error occurs
548: */
549: public static void changeRecipeName(String oldName, String newName)
550: throws SQLException, IOException {
551: if (oldName == null || oldName.equals("") || newName == null
552: || newName.equals("") || oldName.equals(newName))
553: return;
554: PDbBase pdb = new PDbBase();
555: try {
556: pdb.reallyChangeRecipeName(oldName, newName);
557: } finally {
558: if (pdb != null) {
559: pdb.close();
560: pdb = null;
561: }
562: }
563: }
564:
565: /**
566: * Store all the map values in the database.
567: *
568: * @param rc RecipeComponent to store values
569: * @param targets Values to store
570: * @return a <code>boolean</code> value
571: */
572: public static void saveTargetOverrides(RecipeComponent rc,
573: Map targets) throws SQLException, IOException {
574: PDbBase pdb = new PDbBase();
575:
576: pdb.saveTargets(rc, targets);
577: }
578:
579: private void saveTargets(RecipeComponent rc, Map targets)
580: throws SQLException {
581:
582: String[] recipeIdAndClass = getRecipeIdAndClass(rc
583: .getRecipeName());
584:
585: substitutions.put(":recipe_id:", recipeIdAndClass[0]);
586: Statement stmt = getStatement();
587:
588: // Get the current count of arguments, to set the arg_order correctly
589: ResultSet rs = null;
590: int index = 2; // HACK: ComponentCollectionRecipes in parent have 2 ARGS
591: int nind = 0;
592: try {
593: rs = executeQuery(stmt, dbp.getQuery("queryLibRecipeProps",
594: substitutions));
595: while (rs.next())
596: nind++;
597: } catch (SQLException sqe) {
598: } finally {
599: if (rs != null) {
600: try {
601: rs.close();
602: } catch (SQLException sqe) {
603: }
604: }
605: }
606:
607: if (log.isDebugEnabled()) {
608: log.debug("saveTargets: recipe " + recipeIdAndClass[0]
609: + " had " + nind + " args already in DB");
610: }
611: index = nind;
612:
613: Iterator keys = targets.keySet().iterator();
614: while (keys.hasNext()) {
615: Object key = keys.next();
616: Object value = (Object) targets.get(key);
617: substitutions.put(":arg_name:", (String) key);
618: substitutions.put(":arg_value:", (String) value);
619: // FIXME: Bug 1748 -- arg_order must start with <cur max> + 1
620: substitutions.put(":arg_order:", String.valueOf(index++));
621: executeUpdate(dbp.getQuery("insertLibRecipeProp",
622: substitutions));
623: }
624: stmt.close();
625: }
626:
627: private void reallyChangeRecipeName(String oldName, String newName)
628: throws SQLException, IOException {
629: substitutions.put(":old_name:", oldName);
630: substitutions.put(":new_name:", newName);
631: executeUpdate(dbp.getQuery("updateRecipeName", substitutions));
632: }
633:
634: protected String getNextId(String queryName, String prefix) {
635: DecimalFormat format = new DecimalFormat("0000");
636: format.setPositivePrefix(prefix);
637: substitutions.put(":max_id_pattern:", prefix + "____");
638: String id = format.format(1); // Default
639: try {
640: Statement stmt = dbConnection.createStatement();
641: try {
642: String query = dbp.getQuery(queryName, substitutions);
643: ResultSet rs = executeQuery(stmt, query);
644: try {
645: if (rs.next()) {
646: String maxId = rs.getString(1);
647: if (maxId != null) {
648: int n = format.parse(maxId).intValue();
649: id = format.format(n + 1);
650: }
651: }
652: } finally {
653: rs.close();
654: }
655: } finally {
656: stmt.close();
657: }
658: } catch (Exception e) {
659: if (log.isErrorEnabled()) {
660: log.error("Exception: ", e);
661: }
662: // Ignore exceptions and use default
663: }
664: return id;
665: }
666:
667: /**
668: * Utility method to perform an executeUpdate statement. Also
669: * additional code to be added for each executeUpdate for
670: * debugging purposes.
671: **/
672: protected int executeUpdate(String query) throws SQLException {
673: if (query == null)
674: throw new IllegalArgumentException(
675: "executeUpdate: null query");
676: try {
677: long startTime = 0;
678: if (pwlog != null)
679: startTime = System.currentTimeMillis();
680: if (log.isDebugEnabled()) {
681: log.debug("executeUpdate: " + query);
682: }
683: int result = updateStmt.executeUpdate(query);
684: if (pwlog != null) {
685: long endTime = System.currentTimeMillis();
686: pwlog.println((endTime - startTime) + " " + query);
687: }
688: return result;
689: } catch (SQLException sqle) {
690: if (log.isErrorEnabled()) {
691: log.error("SQLException query: " + query, sqle);
692: }
693: if (pwlog != null) {
694: pwlog.println("SQLException query: " + query);
695: pwlog.flush();
696: }
697: throw sqle;
698: }
699: }
700:
701: /**
702: * Utility method to perform an executeUpdate statement. Also
703: * additional code to be added for each executeUpdate for
704: * debugging purposes.
705: **/
706: protected ResultSet executeQuery(PreparedStatement pstmt,
707: String query) throws SQLException {
708: if (query == null)
709: throw new IllegalArgumentException(
710: "executeUpdate: null query");
711: try {
712: long startTime = 0;
713: if (pwlog != null)
714: startTime = System.currentTimeMillis();
715: ResultSet result = pstmt.executeQuery();
716: if (pwlog != null) {
717: long endTime = System.currentTimeMillis();
718: pwlog.println((endTime - startTime) + " " + query);
719: }
720: return result;
721: } catch (SQLException sqle) {
722: if (log.isErrorEnabled()) {
723: log.error("SQLException query: " + query, sqle);
724: }
725: if (pwlog != null) {
726: pwlog.println("SQLException query: " + query);
727: pwlog.flush();
728: }
729: throw sqle;
730: }
731: }
732:
733: /**
734: * Utility method to perform an executeUpdate statement. Also
735: * additional code to be added for each executeUpdate for
736: * debugging purposes.
737: **/
738: protected int executeUpdate(PreparedStatement pstmt, String query)
739: throws SQLException {
740: if (query == null)
741: throw new IllegalArgumentException(
742: "executeUpdate: null query");
743: try {
744: long startTime = 0;
745: if (pwlog != null)
746: startTime = System.currentTimeMillis();
747: int result = pstmt.executeUpdate();
748: if (pwlog != null) {
749: long endTime = System.currentTimeMillis();
750: pwlog.println((endTime - startTime) + " " + query);
751: }
752: return result;
753: } catch (SQLException sqle) {
754: if (log.isErrorEnabled()) {
755: log.error("SQLException query: " + query, sqle);
756: }
757: if (pwlog != null) {
758: pwlog.println("SQLException query: " + query);
759: pwlog.flush();
760: }
761: throw sqle;
762: }
763: }
764:
765: /**
766: * Utility method to perform an executeQuery statement. Also
767: * additional code to be added for each executeQuery for
768: * debugging purposes.
769: **/
770: protected ResultSet executeQuery(Statement stmt, String query)
771: throws SQLException {
772: if (query == null)
773: throw new IllegalArgumentException(
774: "executeQuery: null query");
775: try {
776: long startTime = 0;
777: if (pwlog != null)
778: startTime = System.currentTimeMillis();
779: ResultSet rs = stmt.executeQuery(query);
780: if (pwlog != null) {
781: long endTime = System.currentTimeMillis();
782: pwlog.println((endTime - startTime) + " " + query);
783: }
784: return rs;
785: } catch (SQLException sqle) {
786: if (log.isErrorEnabled()) {
787: log.error("SQLException query: " + query, sqle);
788: }
789: if (pwlog != null) {
790: pwlog.println("SQLException query: " + query);
791: pwlog.flush();
792: }
793: throw sqle;
794: }
795: }
796:
797: /**
798: * Enables debugging
799: **/
800: public void setDebug(boolean newDebug) {
801: debug = newDebug;
802: dbp.setDebug(newDebug);
803: }
804:
805: /**
806: * Indicates that this is no longer needed. Closes the database
807: * connection. Well-behaved users of this class will close when
808: * done. Otherwise, the finalizer will close it.
809: **/
810: public synchronized void close() throws SQLException {
811: if (pwlog != null) {
812: pwlog.flush();
813: pwlog.close();
814: pwlog = null;
815: }
816: if (dbConnection != null) {
817: if (dbConnection.isClosed()) {
818: if (log.isDebugEnabled()) {
819: log
820: .debug("Connection is closed when about to commit & close it");
821: }
822: } else {
823: if (!dbConnection.getAutoCommit())
824: dbConnection.commit();
825: dbConnection.close();
826: }
827: dbConnection = null;
828: }
829: }
830:
831: private void createLogger() {
832: log = CSMART.createLogger(this .getClass().getName());
833: }
834:
835: protected void finalize() {
836: try {
837: if (dbConnection != null)
838: close();
839: } catch (SQLException sqle) {
840: if (log.isErrorEnabled()) {
841: log.error("Exception", sqle);
842: }
843: }
844: }
845:
846: /**
847: * Quote a string for SQL. We don't double quotes that appear in
848: * strings because we have no cases where such quotes occur.
849: **/
850: protected static String sqlQuote(String s) {
851: if (s == null)
852: return "null";
853: String ret = null;
854: int quoteIndex = s.indexOf('\'');
855: // If the string already starts & ends with a single quote, we're done
856: if (quoteIndex == 0 && s.lastIndexOf('\'') == s.length() - 1)
857: return s;
858: ret = new String(s);
859: // Check to see if the String is a path and ends with a '\'. If so, escape it.
860: if (ret.endsWith("\\")) {
861: ret += "\\";
862: }
863: while (quoteIndex >= 0) {
864: ret = ret.substring(0, quoteIndex) + "''"
865: + ret.substring(quoteIndex + 1);
866: quoteIndex = ret.indexOf('\'', quoteIndex + 2);
867: }
868: return "'" + ret + "'";
869: }
870:
871: private void readObject(ObjectInputStream ois) throws IOException,
872: ClassNotFoundException {
873: ois.defaultReadObject();
874: createLogger();
875: }
876:
877: }
|