001: package net.sourceforge.squirrel_sql.client.gui.db;
002:
003: /*
004: * Copyright (C) 2001-2003 Colin Bell
005: * colbell@users.sourceforge.net
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: import java.io.File;
022: import java.io.FileNotFoundException;
023: import java.io.IOException;
024: import java.io.InputStreamReader;
025: import java.net.MalformedURLException;
026: import java.net.URL;
027: import java.util.ArrayList;
028: import java.util.Iterator;
029:
030: import net.sourceforge.squirrel_sql.client.IApplication;
031: import net.sourceforge.squirrel_sql.client.session.schemainfo.SchemaInfoCacheSerializer;
032: import net.sourceforge.squirrel_sql.fw.id.IHasIdentifier;
033: import net.sourceforge.squirrel_sql.fw.id.IIdentifier;
034: import net.sourceforge.squirrel_sql.fw.persist.ValidationException;
035: import net.sourceforge.squirrel_sql.fw.sql.ISQLAlias;
036: import net.sourceforge.squirrel_sql.fw.sql.ISQLDriver;
037: import net.sourceforge.squirrel_sql.fw.sql.SQLDriver;
038: import net.sourceforge.squirrel_sql.fw.sql.SQLDriverManager;
039: import net.sourceforge.squirrel_sql.fw.util.DuplicateObjectException;
040: import net.sourceforge.squirrel_sql.fw.util.IMessageHandler;
041: import net.sourceforge.squirrel_sql.fw.util.IObjectCacheChangeListener;
042: import net.sourceforge.squirrel_sql.fw.util.NullMessageHandler;
043: import net.sourceforge.squirrel_sql.fw.util.StringManager;
044: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
045: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
046: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
047: import net.sourceforge.squirrel_sql.fw.xml.XMLException;
048: import net.sourceforge.squirrel_sql.fw.xml.XMLObjectCache;
049:
050: /**
051: * XML cache of JDBC drivers and aliases.
052: *
053: * @author <A HREF="mailto:colbell@users.sourceforge.net">Colin Bell</A>
054: */
055: public class DataCache {
056: /** Internationalized strings for this class. */
057: private final static StringManager s_stringMgr = StringManagerFactory
058: .getStringManager(DataCache.class);
059:
060: /** Class for objects that define aliases to JDBC data sources. */
061: private final static Class<SQLAlias> SQL_ALIAS_IMPL = SQLAlias.class;
062:
063: /** Class for objects that define JDBC drivers. */
064: private final static Class<SQLDriver> SQL_DRIVER_IMPL = SQLDriver.class;
065:
066: /** Logger for this class. */
067: private final static ILogger s_log = LoggerController
068: .createLogger(DataCache.class);
069:
070: /** Driver manager. */
071: private final SQLDriverManager _driverMgr;
072:
073: /** Cache that contains data. */
074: private final XMLObjectCache _cache = new XMLObjectCache();
075:
076: private IApplication _app;
077:
078: /**
079: * Ctor. Loads drivers and aliases from the XML document.
080: *
081: * @param driverMgr Manages JDBC drivers.
082: * @param driversFile <TT>File</TT> to load drivers from.
083: * @param aliasesFile <TT>File</TT> to load aliases from.
084: * @param dftDriversURL URL that the default rivers can be loaded from.
085: * @param msgHandler Message handler to report on errors in this object.
086: *
087: * @throws IllegalArgumentException
088: * Thrown if null <TT>SQLDriverManager</TT>, <TT>driversFile</TT>,
089: * <TT>aliasesFile</TT> or <TT>dftDriversURL</TT> passed.
090: */
091: public DataCache(SQLDriverManager driverMgr, File driversFile,
092: File aliasesFile, URL dftDriversURL, IApplication app) {
093: super ();
094: if (driverMgr == null) {
095: throw new IllegalArgumentException(
096: "SQLDriverManager == null");
097: }
098: if (driversFile == null) {
099: throw new IllegalArgumentException("driversFile == null");
100: }
101: if (aliasesFile == null) {
102: throw new IllegalArgumentException("aliasesFile == null");
103: }
104: if (dftDriversURL == null) {
105: throw new IllegalArgumentException("dftDriversURL == null");
106: }
107:
108: _driverMgr = driverMgr;
109:
110: _app = app;
111:
112: loadDrivers(driversFile, dftDriversURL, NullMessageHandler
113: .getInstance());
114: loadAliases(aliasesFile, NullMessageHandler.getInstance());
115: }
116:
117: /**
118: * Save JDBC drivers to the passed file as XML.
119: *
120: * @param file File to save drivers to.
121: *
122: * @throws IllegalArgumentException
123: * Thrown if <TT>null</TT> <TT>File</TT> passed.
124: * @throws IOException
125: * Thrown if an I/O error occurs saving.
126: * @throws XMLException
127: * Thrown if an error occurs translating drivers to XML.
128: */
129: public void saveDrivers(File file) throws IOException, XMLException {
130: if (file == null) {
131: throw new IllegalArgumentException("File == null");
132: }
133:
134: saveSecure(file, SQL_DRIVER_IMPL);
135: }
136:
137: /**
138: * Save aliases to the passed file as XML.
139: *
140: * @param file File to save aliases to.
141: *
142: * @throws IllegalArgumentException
143: * Thrown if <TT>null</TT> <TT>File</TT> passed.
144: * @throws IOException
145: * Thrown if an I/O error occurs saving.
146: * @throws XMLException
147: * Thrown if an error occurs translating aliases to XML.
148: */
149: public void saveAliases(File file) throws IOException, XMLException {
150: if (file == null) {
151: throw new IllegalArgumentException("File == null");
152: }
153: saveSecure(file, SQL_ALIAS_IMPL);
154: }
155:
156: private void saveSecure(File file,
157: Class<? extends IHasIdentifier> forClass)
158: throws IOException, XMLException {
159: File tempFile = new File(file.getPath() + "~");
160: try {
161: tempFile.delete();
162: } catch (Exception e) {
163: }
164:
165: _cache.saveAllForClass(tempFile.getPath(), forClass);
166: if (false == tempFile.renameTo(file)) {
167: File doubleTemp = new File(file.getPath() + "~~");
168: try {
169: doubleTemp.delete();
170: } catch (Exception e) {
171: }
172: File buf = new File(file.getPath());
173:
174: if (false == buf.renameTo(doubleTemp)) {
175: throw new IllegalStateException("Cannot rename file "
176: + buf.getPath() + " to " + doubleTemp.getPath()
177: + ". New File will not be saved.");
178: }
179:
180: try {
181: tempFile.renameTo(file);
182: doubleTemp.delete();
183: } catch (Exception e) {
184: doubleTemp.renameTo(file);
185: }
186: }
187: }
188:
189: /**
190: * Retrieve the <TT>ISQLDriver</TT> for the passed identifier.
191: *
192: * @param id Identifier to retrieve driver for.
193: *
194: * @return the <TT>ISQLDriver</TT> for the passed identifier.
195: *
196: * @throws IllegalArgumentException
197: * Thrown if <TT>null</TT> <TT>ISQLDriver</TT> passed.
198: */
199: public ISQLDriver getDriver(IIdentifier id) {
200: if (id == null) {
201: throw new IllegalArgumentException("ISQLDriver == null");
202: }
203:
204: return (ISQLDriver) _cache.get(SQL_DRIVER_IMPL, id);
205: }
206:
207: /**
208: * Add a driver to the cache.
209: *
210: * @param sqlDriver The driver to add.
211: *
212: * @param messageHandler
213: * @throws IllegalArgumentException
214: * Thrown if <TT>ISQLDriver</TT> is null.
215: */
216: public void addDriver(ISQLDriver sqlDriver,
217: IMessageHandler messageHandler)
218: throws ClassNotFoundException, IllegalAccessException,
219: InstantiationException, DuplicateObjectException,
220: MalformedURLException {
221: if (sqlDriver == null) {
222: throw new IllegalArgumentException("ISQLDriver == null");
223: }
224: if (messageHandler != null) {
225: registerDriver(sqlDriver, messageHandler, true);
226: }
227: _cache.add(sqlDriver);
228: }
229:
230: public void removeDriver(ISQLDriver sqlDriver) {
231: _cache.remove(SQL_DRIVER_IMPL, sqlDriver.getIdentifier());
232: _driverMgr.unregisterSQLDriver(sqlDriver);
233: }
234:
235: public Iterator<ISQLDriver> drivers() {
236: return _cache.getAllForClass(SQL_DRIVER_IMPL);
237: }
238:
239: public void addDriversListener(IObjectCacheChangeListener lis) {
240: _cache.addChangesListener(lis, SQL_DRIVER_IMPL);
241: }
242:
243: public void removeDriversListener(IObjectCacheChangeListener lis) {
244: _cache.removeChangesListener(lis, SQL_DRIVER_IMPL);
245: }
246:
247: public ISQLAlias getAlias(IIdentifier id) {
248: return (ISQLAlias) _cache.get(SQL_ALIAS_IMPL, id);
249: }
250:
251: public Iterator<ISQLAlias> aliases() {
252: return _cache.getAllForClass(SQL_ALIAS_IMPL);
253: }
254:
255: public void addAlias(ISQLAlias alias)
256: throws DuplicateObjectException {
257: _cache.add(alias);
258: }
259:
260: public void removeAlias(SQLAlias alias) {
261: SchemaInfoCacheSerializer.aliasRemoved(alias);
262: _app.getPluginManager().aliasRemoved(alias);
263: _cache.remove(SQL_ALIAS_IMPL, alias.getIdentifier());
264: }
265:
266: public Iterator<ISQLAlias> getAliasesForDriver(ISQLDriver driver) {
267: ArrayList<ISQLAlias> data = new ArrayList<ISQLAlias>();
268: for (Iterator<ISQLAlias> it = aliases(); it.hasNext();) {
269: ISQLAlias alias = it.next();
270: if (driver.equals(getDriver(alias.getDriverIdentifier()))) {
271: data.add(alias);
272: }
273: }
274: return data.iterator();
275: }
276:
277: public void addAliasesListener(IObjectCacheChangeListener lis) {
278: _cache.addChangesListener(lis, SQL_ALIAS_IMPL);
279: }
280:
281: public void removeAliasesListener(IObjectCacheChangeListener lis) {
282: _cache.removeChangesListener(lis, SQL_ALIAS_IMPL);
283: }
284:
285: /**
286: * Load <TT>IISqlDriver</TT> objects from the XML file <TT>driversFile</TT>.
287: * If file not found then load from the default drivers.
288: *
289: * @param driversFile <TT>File</TT> to load drivers from.
290: * @param dftDriversURL <TT>URL</TT> to load default drivers from.
291: * @param msgHandler Message handler to write any errors to.
292: *
293: *@throws IllegalArgumentException
294: * Thrown if <TT>null</TT> <TT>driversFile</TT>,
295: * <TT>dftDriversURL</TT>, or <TT>msgHandler</TT> passed.
296: */
297: private void loadDrivers(File driversFile, URL dftDriversURL,
298: IMessageHandler msgHandler) {
299: if (driversFile == null) {
300: throw new IllegalArgumentException("driversFile == null");
301: }
302: if (dftDriversURL == null) {
303: throw new IllegalArgumentException("dftDriversURL == null");
304: }
305: if (msgHandler == null) {
306: throw new IllegalArgumentException("msgHandler == null");
307: }
308:
309: try {
310: try {
311: _cache.load(driversFile.getPath());
312: if (!drivers().hasNext()) {
313: loadDefaultDrivers(dftDriversURL);
314: } else {
315: fixupDrivers();
316: mergeDefaultWebsites(dftDriversURL);
317: }
318: } catch (FileNotFoundException ex) {
319: loadDefaultDrivers(dftDriversURL); // first time user has run pgm.
320: } catch (Exception ex) {
321: String msg = s_stringMgr.getString(
322: "DataCache.error.loadingdrivers", driversFile
323: .getPath());
324: s_log.error(msg, ex);
325: msgHandler.showErrorMessage(msg);
326: msgHandler.showErrorMessage(ex, null);
327: loadDefaultDrivers(dftDriversURL);
328: }
329: } catch (XMLException ex) {
330: s_log.error("Error loading drivers", ex);
331: } catch (IOException ex) {
332: s_log.error("Error loading drivers", ex);
333: }
334:
335: for (Iterator<ISQLDriver> it = drivers(); it.hasNext();) {
336: registerDriver(it.next(), msgHandler, false);
337: }
338: }
339:
340: public SQLAlias createAlias(IIdentifier id) {
341: return new SQLAlias(id);
342: }
343:
344: public ISQLDriver createDriver(IIdentifier id) {
345: return new SQLDriver(id);
346: }
347:
348: /**
349: * Tests the currently cached driver definitions to see if any default
350: * drivers are missing and returns an array of ISQLDrivers that represent
351: * default drivers that were not found.
352: *
353: * @param url the url of the file containing the default driver definitions.
354: * @return a list of default ISQLDriver instances if any or found; null is
355: * returned otherwise.
356: *
357: * @throws IOException
358: * @throws XMLException
359: */
360: public ISQLDriver[] findMissingDefaultDrivers(URL url)
361: throws IOException, XMLException {
362: ISQLDriver[] result = null;
363: InputStreamReader isr = new InputStreamReader(url.openStream());
364: ArrayList<ISQLDriver> missingDrivers = new ArrayList<ISQLDriver>();
365: try {
366: XMLObjectCache tmp = new XMLObjectCache();
367: tmp.load(isr, null, true);
368:
369: for (Iterator<ISQLDriver> iter = tmp
370: .getAllForClass(SQL_DRIVER_IMPL); iter.hasNext();) {
371: ISQLDriver defaultDriver = iter.next();
372: if (!containsDriver(defaultDriver)) {
373: missingDrivers.add(defaultDriver);
374: }
375: }
376: } catch (DuplicateObjectException ex) {
377: // If this happens then this is a programming error as we said
378: // in the above call to ingore these errors.
379: s_log.error(
380: "Received an unexpected DuplicateObjectException",
381: ex);
382: } finally {
383: isr.close();
384: }
385: if (missingDrivers.size() > 0) {
386: result = missingDrivers
387: .toArray(new ISQLDriver[missingDrivers.size()]);
388: }
389: return result;
390: }
391:
392: /**
393: * Returns a boolean value indicating whether or not the specified driver
394: * is contained in the cache.
395: *
396: * @param driver the ISQLDriver to search for.
397: * @return true if the specified driver was found; false otherwise.
398: */
399: public boolean containsDriver(ISQLDriver driver) {
400: boolean result = false;
401: for (Iterator<ISQLDriver> iter = _cache
402: .getAllForClass(SQL_DRIVER_IMPL); iter.hasNext();) {
403: ISQLDriver cachedDriver = iter.next();
404: if (cachedDriver.equals(driver)) {
405: result = true;
406: break;
407: }
408: }
409: return result;
410: }
411:
412: public void loadDefaultDrivers(URL url) throws IOException,
413: XMLException {
414: InputStreamReader isr = new InputStreamReader(url.openStream());
415: try {
416: _cache.load(isr, null, true);
417: } catch (DuplicateObjectException ex) {
418: // If this happens then this is a programming error as we said
419: // in the above call to ingore these errors.
420: s_log.error(
421: "Received an unexpected DuplicateObjectException",
422: ex);
423: } finally {
424: isr.close();
425: }
426: }
427:
428: private void registerDriver(ISQLDriver sqlDriver,
429: IMessageHandler msgHandler, boolean extendedMessaging) {
430: boolean registrationSucessfully = false;
431: try {
432: _driverMgr.registerSQLDriver(sqlDriver);
433: registrationSucessfully = true;
434: } catch (ClassNotFoundException cnfe) {
435: if (extendedMessaging) {
436: Object[] params = new Object[] {
437: sqlDriver.getDriverClassName(),
438: sqlDriver.getName(), cnfe };
439:
440: String msg = s_stringMgr.getString(
441: "DataCache.error.driverClassNotFound", params);
442: // i18n[DataCache.msg.driverClassNotFound=Could not find class {0} in neither
443: // the Java class path nor the Extra class path of the {1} driver definition:\n{2}]
444:
445: s_log.error(msg, cnfe);
446: msgHandler.showErrorMessage(msg);
447: }
448: } catch (Throwable th) {
449: String msg = s_stringMgr.getString(
450: "DataCache.error.registerdriver", sqlDriver
451: .getName());
452: s_log.error(msg, th);
453: msgHandler.showErrorMessage(msg);
454: msgHandler.showErrorMessage(th, null);
455: }
456:
457: if (extendedMessaging && registrationSucessfully) {
458: Object[] params = new Object[] {
459: sqlDriver.getDriverClassName(),
460: sqlDriver.getName(), };
461:
462: String msg = s_stringMgr
463: .getString(
464: "DataCache.msg.driverRegisteredSucessfully",
465: params);
466: // i18n[DataCache.msg.driverRegisteredSucessfully=Driver class {0} sucessfully registered
467: // for driver definition: {1}]
468: msgHandler.showMessage(msg);
469: }
470: }
471:
472: private void loadAliases(File aliasesFile,
473: IMessageHandler msgHandler) {
474: try {
475: _cache.load(aliasesFile.getPath());
476: } catch (FileNotFoundException ignore) {
477: // first time user has run pgm.
478: } catch (Exception ex) {
479: String msg = s_stringMgr.getString(
480: "DataCache.error.loadingaliases", aliasesFile
481: .getPath());
482: s_log.error(msg, ex);
483: msgHandler.showErrorMessage(msg);
484: msgHandler.showErrorMessage(ex, null);
485: }
486: }
487:
488: /**
489: * In 1.1beta? the jar file for a driver was changed from only one allowed
490: * to multiple ones allowed. This method changes the driver from the old
491: * version to the new one to allow for loading old versions of the
492: * SQLDrivers.xml file.
493: */
494: @SuppressWarnings("deprecation")
495: private void fixupDrivers() {
496: for (Iterator<ISQLDriver> it = drivers(); it.hasNext();) {
497: ISQLDriver driver = it.next();
498: String[] fileNames = driver.getJarFileNames();
499: if (fileNames == null || fileNames.length == 0) {
500: String fileName = driver.getJarFileName();
501: if (fileName != null && fileName.length() > 0) {
502: driver.setJarFileNames(new String[] { fileName });
503: try {
504: driver.setJarFileName(null);
505: } catch (ValidationException ignore) {
506: // Ignore
507: }
508: }
509: }
510: }
511: }
512:
513: /**
514: * In 2.1 final(+), we introduced a new property for drivers which is the
515: * website that hosts the driver and/or the relational database associated
516: * with a driver. To populate existing driver definitions in SQLDrivers.xml
517: * with this value, it is necessary to read in the default defs, then scan
518: * the currently loaded drivers and set the website url. That is what this
519: * method does.
520: */
521: private void mergeDefaultWebsites(URL defaultDriversUrl) {
522: InputStreamReader isr = null;
523: try {
524: isr = new InputStreamReader(defaultDriversUrl.openStream());
525: XMLObjectCache tmp = new XMLObjectCache();
526: tmp.load(isr, null, true);
527:
528: for (Iterator<ISQLDriver> iter = tmp
529: .getAllForClass(SQL_DRIVER_IMPL); iter.hasNext();) {
530:
531: ISQLDriver defaultDriver = iter.next();
532: ISQLDriver cachedDriver = getDriver(defaultDriver
533: .getIdentifier());
534: if (cachedDriver != null) {
535: if (cachedDriver.getWebSiteUrl() == null
536: || "".equals(cachedDriver.getWebSiteUrl())) {
537: if (defaultDriver.getWebSiteUrl() != null) {
538: cachedDriver.setWebSiteUrl(defaultDriver
539: .getWebSiteUrl());
540: }
541: }
542: }
543: }
544: } catch (Exception ex) {
545: s_log.error("Received an unexpected Exception", ex);
546: } finally {
547: if (isr != null) {
548: try {
549: isr.close();
550: } catch (Exception e) {/* Do Nothing */
551: }
552: }
553: }
554: }
555:
556: public void refreshDriver(ISQLDriver driver,
557: IMessageHandler messageHandler) {
558: registerDriver(driver, messageHandler, true);
559: }
560: }
|