001: /*
002: * The contents of this file are subject to the Mozilla Public License
003: * Version 1.1 (the "License"); you may not use this file except in
004: * compliance with the License. You may obtain a copy of the License at
005: * http://www.mozilla.org/MPL/
006: *
007: * Software distributed under the License is distributed on an "AS IS"
008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
009: * License for the specific language governing rights and limitations
010: * under the License.
011: *
012: * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
013: *
014: * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
015: * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
016: *
017: * Contributor(s):
018: * Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
019: *
020: * If you didn't download this code from the following link, you should check
021: * if you aren't using an obsolete version: http://www.isqlviewer.com
022: */
023: package org.isqlviewer.sql;
024:
025: import java.beans.PropertyChangeListener;
026: import java.beans.PropertyChangeSupport;
027: import java.io.File;
028: import java.io.Serializable;
029: import java.net.MalformedURLException;
030: import java.net.URL;
031: import java.net.URLClassLoader;
032: import java.util.Collections;
033: import java.util.Enumeration;
034: import java.util.Properties;
035: import java.util.TreeSet;
036:
037: import org.isqlviewer.util.UserProperties;
038:
039: /**
040: * Connection profile to control some statement and connection behavior with the JdbcService class.
041: * <p>
042: * This is a somewhat typical bean class, with getters/setters for the respective parameters. Each connection profile
043: * can be chained together using the {@link #ConnectionProfile(ConnectionProfile)} constructor.
044: * <p>
045: * The parameters of this class historically were embedded into the actually connection wrapper class. This proved to be
046: * extremely cumbersome and fragile. This implementation should proved to be more extensible and does not force to
047: * expose any internals of the connection wrapper.
048: * <p>
049: * This implementation also allows for changes on the fly to the profile or parent profiles without having a reference
050: * to the actual connection, and still having a live change event.
051: *
052: * @author Mark A. Kobold <mkobold at isqlviewer dot com>
053: * @version 1.0
054: */
055: public final class ConnectionProfile implements Serializable {
056:
057: /**
058: * Boolean property for enabling JDBC tracing through the java.sql.DriverManager mechanisim.
059: * <p>
060: *
061: * @see java.sql.DriverManager#setLogWriter(java.io.PrintWriter)
062: */
063: public static final String JDBC_TRACE_ENABLED = "jdbc.trace-enabled";
064: /**
065: * Boolean property to tell database connection that you want genereated keys if available from ResultSets.
066: * <p>
067: *
068: * @see java.sql.Statement#getGeneratedKeys()
069: */
070: public static final String JDBC_GENERATE_KEYS = "jdbc.resultsetkeys-enabled";
071: /**
072: * Integer property for configuring resultset holdability.
073: *
074: * @see java.sql.Connection#setHoldability(int)
075: */
076: public static final String JDBC_HOLDABILITY = "jdbc.resultset-holdability";
077: /**
078: * Boolean property to turn on/off escaping processing in the JDBC driver.
079: * <p>
080: *
081: * @see java.sql.Statement#setEscapeProcessing(boolean)
082: */
083: public static final String JDBC_ESCAPE_PROC = "jdbc.escapeProc-enabled";
084: /**
085: * Boolean property for enabling reverse fetching of ResultSet data.
086: * <p>
087: *
088: * @see java.sql.Statement#setFetchDirection(int)
089: */
090: public static final String JDBC_REVERSE_FETCHING = "jdbc.reverseFetch-enabled";
091: /**
092: * Integer property to configure the maximum number of rows a Statement object should return.
093: * <p>
094: *
095: * @see java.sql.Statement#setMaxRows(int)
096: */
097: public static final String JDBC_MAX_ROWS = "jdbc.max-rows";
098: /**
099: * Integer property to configure the maximum field size a Statement object should return.
100: * <p>
101: *
102: * @see java.sql.Statement#setMaxFieldSize(int)
103: */
104: public static final String JDBC_MAX_FIELD_SIZE = "jdbc.max-fieldsize";
105: /**
106: * Boolean property to ensure that the connection is valid and reconnects automatically when queries are executed.
107: */
108: public static final String CONNECTION_KEEPALIVE = "jdbc.keepalive-enabled";
109: /**
110: * Integer property for the number of seconds that should pass before a connection times-out when connecting.
111: */
112: public static final String LOGIN_TIMEOUT = "jdbc.timeout-login";
113: /**
114: * Integer property for the number of seconds that should pass before a statement execution times-out.
115: */
116: public static final String QUERY_TIMEOUT = "jdbc.timeout-query";
117: /**
118: * Boolean property to debug JDBC activity by printing stack traces on exceptions.
119: */
120: public static final String PRINT_STACKTRACES = "jdbc.debug-enabled";
121: /**
122: * Integer property to determine how deep to process chained SQLExceptions & SQLWarnings.
123: */
124: public static final String MAX_EXCEPTION_CHAIN_LENGTH = "jdbc.max-warnings";
125: /**
126: * String property for declaring a specifc schema to use for schema sensitive operations.
127: * <p>
128: * By default on most RDBMS the default schema is the user-name for the connection.
129: */
130: public static final String PREFERRED_SCHEMA = "jdbc.preferred-schema";
131: /**
132: * String property for declaring a specifc catalog to use for catalog sensitive operations.
133: * <p>
134: * This will also cause the connection to for a change to this catalog if is not a <tt>null</tt> value.
135: */
136: public static final String PREFERRED_CATALOG = "jdbc.preferred-catalog";
137: /**
138: * Boolean property that indicates wheter the storage of the service credentials are un-encrypted.
139: */
140: public static final String USE_SECURE_CREDENTIALS = "jdbc.encrypt-credentials";
141: /**
142: * Boolean property that indicates wheter the storage of the service credentials actually occurs.
143: */
144: public static final String STORE_CREDENTIALS = "jdbc.store-credentials";
145:
146: // backing store for settings this makes it easier to export/import in the end //
147: // though it doesn't follow the true bean convention, I don't think it matters //
148: private UserProperties profileSettings = new UserProperties();
149: //
150: private TreeSet<File> resources = new TreeSet<File>();
151: // notification mechanisim when settings are changed.
152: private transient PropertyChangeSupport propertyListeners = new PropertyChangeSupport(
153: this );
154: // Classloader reference from the parent for creating hierical classloaders as expected.
155: private transient ClassLoader parentClassLoader = ConnectionProfile.class
156: .getClassLoader();
157:
158: // UID for serialization compatability purposes.
159: private static final long serialVersionUID = 8092402597793653452L;
160:
161: /**
162: * Creates default connection profile with no default settings.
163: * <p>
164: */
165: public ConnectionProfile() {
166:
167: this (null);
168: }
169:
170: /**
171: * Creates a connection profile with default profile.
172: * <p>
173: * If a profile setting is not available in this instance an attempt to defaults profile will be made.
174: *
175: * @param defaults parent profile for settings if not available in this profile; can be <tt>null</tt>.
176: */
177: public ConnectionProfile(ConnectionProfile defaults) {
178:
179: if (defaults != null) {
180: profileSettings = new UserProperties(
181: defaults.profileSettings);
182: parentClassLoader = defaults.toClassLoader();
183: }
184: }
185:
186: /**
187: * Helper method for determining if this profile contains any settings.
188: * <p>
189: *
190: * @return <tt>true</tt> if all settings from this profile are coming from the defaults.
191: */
192: public boolean containsSettings() {
193:
194: return profileSettings.isEmpty();
195: }
196:
197: /**
198: * Get the maximum number of chained exceptions to show for SQLExceptions.
199: * <p>
200: * This settings defaults to <tt>10</tt>
201: *
202: * @return depth of chained exceptions to expose for SQLExceptions
203: * @see java.sql.SQLException#getNextException()
204: */
205: public long getMaxExceptionLength() {
206:
207: return profileSettings.getLong(MAX_EXCEPTION_CHAIN_LENGTH, 10);
208: }
209:
210: /**
211: * Sets the maximum number of chained exceptions to display for SQLExceptions.
212: * <p>
213: * This method will fire a property change event for {@link #MAX_EXCEPTION_CHAIN_LENGTH}.
214: *
215: * @param maxExceptionLength depth of chained exceptions to expose.
216: */
217: public void setMaxExceptionLength(int maxExceptionLength) {
218:
219: int previous = profileSettings
220: .getInt(MAX_EXCEPTION_CHAIN_LENGTH);
221: profileSettings.putInt(MAX_EXCEPTION_CHAIN_LENGTH,
222: maxExceptionLength);
223: propertyListeners.firePropertyChange(
224: MAX_EXCEPTION_CHAIN_LENGTH, previous,
225: maxExceptionLength);
226: }
227:
228: /**
229: * Sets the preferred escape processing flag for configuring statements.
230: * <p>
231: * This settings defaults to <tt>false</tt>
232: *
233: * @return <tt>true</tt> if escape processing should be done by the driver on queries.
234: * @see java.sql.Statement#setEscapeProcessing(boolean)
235: */
236: public boolean isEscapeProcessing() {
237:
238: return profileSettings.getBoolean(JDBC_ESCAPE_PROC, false);
239: }
240:
241: /**
242: * Sets the preferred escape processing flag when configuring statements.
243: * <p>
244: * This method will fire a property change event for {@link #JDBC_ESCAPE_PROC}.
245: *
246: * @param escapeProcessing flag to enable/disable escape processing on the JDBC driver.
247: * @see java.sql.Statement#setEscapeProcessing(boolean)
248: */
249: public void setEscapeProcessing(boolean escapeProcessing) {
250:
251: boolean previous = profileSettings.getBoolean(JDBC_ESCAPE_PROC);
252: profileSettings.putBoolean(JDBC_ESCAPE_PROC, escapeProcessing);
253: propertyListeners.firePropertyChange(JDBC_ESCAPE_PROC,
254: previous, escapeProcessing);
255: }
256:
257: /**
258: * Determines if connections are automatically recovered when disconnected.
259: * <p>
260: * This settings defaults to <tt>false</tt>
261: *
262: * @return <tt>true</tt> if the connection is recovered when disconnected upon statement execution.
263: */
264: public boolean isKeepAlive() {
265:
266: return profileSettings.getBoolean(CONNECTION_KEEPALIVE, false);
267: }
268:
269: /**
270: * Sets the preferred keep-alive parameter for the connection.
271: * <p>
272: * This method will fire a property change event for {@link #CONNECTION_KEEPALIVE}.
273: *
274: * @param keepAlive flag to enable/disable automatically recovering a broken connection.
275: */
276: public void setKeepAlive(boolean keepAlive) {
277:
278: boolean previous = profileSettings
279: .getBoolean(CONNECTION_KEEPALIVE);
280: profileSettings.putBoolean(CONNECTION_KEEPALIVE, keepAlive);
281: propertyListeners.firePropertyChange(CONNECTION_KEEPALIVE,
282: previous, keepAlive);
283: }
284:
285: /**
286: * Determines if JDBC tracing through the java.sql.DriverManager is enabled.
287: * <p>
288: * This settings defaults to <tt>false</tt>
289: *
290: * @return <tt>true</tt>if tracing through the java.sql.DriverManager is enabled.
291: */
292: public boolean isTracingEnabled() {
293:
294: return profileSettings.getBoolean(JDBC_TRACE_ENABLED, false);
295: }
296:
297: /**
298: * Configures JDBC tracing through the java.sql.DriverManager.
299: * <p>
300: * This method will fire a property change event for {@link #JDBC_TRACE_ENABLED}.
301: *
302: * @param tracingEnabled flag to enable/disable JDBC tracing using the java.sql.DriverManager.
303: */
304: public void setTracingEnabled(boolean tracingEnabled) {
305:
306: boolean previous = profileSettings
307: .getBoolean(JDBC_TRACE_ENABLED);
308: profileSettings.putBoolean(JDBC_TRACE_ENABLED, tracingEnabled);
309: propertyListeners.firePropertyChange(JDBC_TRACE_ENABLED,
310: previous, tracingEnabled);
311: }
312:
313: /**
314: * Determines if stacktraces should be displayed when exceptions occur.
315: * <p>
316: * This settings defaults to <tt>false</tt>
317: *
318: * @return <tt>true</tt> stacktraces should be displayed when an exception occurs.
319: */
320: public boolean isDebuggingEnabled() {
321:
322: return profileSettings.getBoolean(PRINT_STACKTRACES, false);
323: }
324:
325: /**
326: * Sets stacktraces on exceptions from JDBC operations to be displayed.
327: * <p>
328: * This method will fire a property change event for {@link #PRINT_STACKTRACES}.
329: *
330: * @param debuggingEnabled flag to enable/disable the display of stacktraces from exceptions.
331: */
332: public void setDebuggingEnabled(boolean debuggingEnabled) {
333:
334: boolean previous = profileSettings
335: .getBoolean(PRINT_STACKTRACES);
336: profileSettings.putBoolean(PRINT_STACKTRACES, debuggingEnabled);
337: propertyListeners.firePropertyChange(PRINT_STACKTRACES,
338: previous, debuggingEnabled);
339: }
340:
341: /**
342: * Get the number of seconds allowed before timing out on creating a connection.
343: * <p>
344: * This settings defaults to <tt>0</tt>
345: *
346: * @return number of seconds to wait before giving up on creating a connection; 0 means wait forever.
347: */
348: public int getLoginTimeout() {
349:
350: return profileSettings.getInt(LOGIN_TIMEOUT, 0);
351: }
352:
353: /**
354: * Sets the number of seconds to wait for a connection to be made.
355: * <p>
356: * This method will fire a property change event for {@link #LOGIN_TIMEOUT}.
357: *
358: * @param loginTimeout number of seconds to wait for a connection to be made; use 0 to wait forever.
359: */
360: public void setLoginTimeout(int loginTimeout) {
361:
362: int previous = profileSettings.getInt(LOGIN_TIMEOUT);
363: profileSettings.putInt(LOGIN_TIMEOUT, loginTimeout);
364: propertyListeners.firePropertyChange(LOGIN_TIMEOUT,
365: new Integer(previous), new Integer(loginTimeout));
366: }
367:
368: /**
369: * Get the number of seconds allowed before timing out on execution of statements.
370: * <p>
371: * This settings defaults to <tt>0</tt>
372: *
373: * @return number of seconds to wait before giving up statement to finish; 0 means wait forever.
374: */
375: public int getQueryTimeout() {
376:
377: return profileSettings.getInt(QUERY_TIMEOUT, 0);
378: }
379:
380: /**
381: * Sets the number of seconds to wait for a connection to be made.
382: * <p>
383: * This method will fire a property change event for {@link #QUERY_TIMEOUT}.
384: *
385: * @param queryTimeout number of seconds to wait for a statement to execute; use 0 to wait forever.
386: */
387: public void setQueryTimeout(int queryTimeout) {
388:
389: int previous = profileSettings.getInt(QUERY_TIMEOUT);
390: profileSettings.putInt(QUERY_TIMEOUT, queryTimeout);
391: propertyListeners.firePropertyChange(QUERY_TIMEOUT,
392: new Integer(previous), new Integer(queryTimeout));
393: }
394:
395: /**
396: * Get the connections' preferred maximum field size for a data recieved from statements.
397: * <p>
398: * This settings defaults to <tt>0</tt>
399: *
400: * @return maximum number of bytes allowed to be recieved from a single record; 0 means no limit.
401: * @see java.sql.Statement#getMaxFieldSize()
402: */
403: public long getMaxFieldSize() {
404:
405: return profileSettings.getLong(JDBC_MAX_FIELD_SIZE, 0l);
406: }
407:
408: /**
409: * Sets the preferred max field size for data recieved from statements.
410: * <p>
411: * This method will fire a property change event for {@link #JDBC_MAX_FIELD_SIZE}.
412: *
413: * @param maxFieldSize maximum number of bytes to recieve from a single record;0 means no limit.
414: * @see java.sql.Statement#setMaxFieldSize(int)
415: */
416: public void setMaxFieldSize(long maxFieldSize) {
417:
418: long previous = profileSettings.getLong(JDBC_MAX_FIELD_SIZE);
419: profileSettings.putLong(JDBC_MAX_FIELD_SIZE, maxFieldSize);
420: propertyListeners.firePropertyChange(JDBC_MAX_FIELD_SIZE,
421: new Long(previous), new Long(maxFieldSize));
422: }
423:
424: /**
425: * Gets the maximum number of rows to receive from a statement.
426: * <p>
427: * This settings defaults to <tt>0</tt>
428: *
429: * @return maximum number of rows to receive from a statement; 0 means no limit.
430: * @see java.sql.Statement#getMaxRows()
431: */
432: public long getMaxRowCount() {
433:
434: return profileSettings.getLong(JDBC_MAX_ROWS, 0l);
435: }
436:
437: /**
438: * Sets the preferred maximum number of rows to recieve from a statement.
439: * <p>
440: * This method will fire a property change event for {@link #JDBC_MAX_ROWS}.
441: *
442: * @param maxRowCount The maxRowCount to set.
443: */
444: public void setMaxRowCount(long maxRowCount) {
445:
446: long previous = profileSettings.getLong(JDBC_MAX_ROWS);
447: profileSettings.putLong(JDBC_MAX_ROWS, maxRowCount);
448: propertyListeners.firePropertyChange(JDBC_MAX_ROWS, new Long(
449: previous), new Long(maxRowCount));
450: }
451:
452: /**
453: * Sets the parameter for accessing ResultSet keys for each statement.
454: * <p>
455: * This settings defaults to <tt>false</tt>
456: *
457: * @return <tt>true</tt> if generated keys from ResultSet objects are queried for.
458: * @see java.sql.Statement#getGeneratedKeys()
459: */
460: public boolean isResultsetKeys() {
461:
462: return profileSettings.getBoolean(JDBC_GENERATE_KEYS, false);
463: }
464:
465: /**
466: * Sets the parameter to access generated keys from executed statements.
467: * <p>
468: * This method will fire a property change event for {@link #JDBC_GENERATE_KEYS}.
469: *
470: * @param resultsetKeys flag to enable/disable generated key acquisition
471: * @see java.sql.Statement#getGeneratedKeys()
472: */
473: public void setResultsetKeys(boolean resultsetKeys) {
474:
475: boolean previous = profileSettings
476: .getBoolean(JDBC_GENERATE_KEYS);
477: profileSettings.putBoolean(JDBC_GENERATE_KEYS, resultsetKeys);
478: propertyListeners.firePropertyChange(JDBC_GENERATE_KEYS,
479: previous, resultsetKeys);
480: }
481:
482: /**
483: * Gets the flag for enabling reverse-fetchings when executing statements.
484: * <p>
485: * This settings defaults to <tt>false</tt>
486: *
487: * @return <tt>true</tt> if statements should use reverse-fetching direction for results.
488: * @see java.sql.Statement#setFetchDirection(int)
489: */
490: public boolean isReverseFetching() {
491:
492: return profileSettings.getBoolean(JDBC_REVERSE_FETCHING, false);
493: }
494:
495: /**
496: * Sets the flag to enable reverse-fetching when results are recieve from a statement.
497: * <p>
498: * This method will fire a property change event for {@link #JDBC_GENERATE_KEYS}.
499: *
500: * @param reverseFetching flag to configure fetch direction; <tt>true</tt> = reverse; <tt>false</tt> = forward.
501: * @see java.sql.Statement#setFetchDirection(int)
502: */
503: public void setReverseFetching(boolean reverseFetching) {
504:
505: boolean previous = profileSettings
506: .getBoolean(JDBC_REVERSE_FETCHING);
507: profileSettings.putBoolean(JDBC_REVERSE_FETCHING,
508: reverseFetching);
509: propertyListeners.firePropertyChange(JDBC_REVERSE_FETCHING,
510: previous, reverseFetching);
511: }
512:
513: /**
514: * Gets the preferred schema to use for schema sensitive operations.
515: * <p>
516: * This settings defaults to <tt>null</tt>
517: *
518: * @return <tt>null</tt> if no schema is preferred; schema will be determined by connection.
519: */
520: public String getPreferredSchema() {
521:
522: return profileSettings.get(PREFERRED_SCHEMA, null);
523: }
524:
525: /**
526: * Sets the preferred schema name to use when performing schema sensitve operations.
527: * <p>
528: * This method will fire a property change event for {@link #PREFERRED_SCHEMA}.
529: *
530: * @param preferredSchema preferred schema name to use; null to use default or disable.
531: */
532: public void setPreferredSchema(String preferredSchema) {
533:
534: String previous = profileSettings.get(PREFERRED_SCHEMA, null);
535: if (preferredSchema == null) {
536: profileSettings.removeProperty(PREFERRED_SCHEMA);
537: } else {
538: profileSettings.put(PREFERRED_SCHEMA, preferredSchema);
539: }
540: propertyListeners.firePropertyChange(PREFERRED_SCHEMA,
541: previous, preferredSchema);
542: }
543:
544: /**
545: * Gets the preferred schema to use for schema sensitive operations.
546: * <p>
547: * This settings defaults to <tt>null</tt>
548: *
549: * @return <tt>null</tt> if no schema is preferred; schema will be determined by connection.
550: * @see java.sql.Connection#getCatalog()
551: */
552: public String getPreferredCatalog() {
553:
554: return profileSettings.get(PREFERRED_CATALOG, null);
555: }
556:
557: /**
558: * Sets the preferred catalog name to use when performing catalog sensitve operations.
559: * <p>
560: * If this property is a non-null value, the connection will also attempt to switch to this catalog upon connection
561: * to the server.
562: * <p>
563: * This method will fire a property change event for {@link #PREFERRED_CATALOG}.
564: *
565: * @param preferredCatalog preferred catalog name to use; null to use default or disable.
566: * @see java.sql.Connection#getCatalog()
567: */
568: public void setPreferredCatalog(String preferredCatalog) {
569:
570: String previous = profileSettings.get(PREFERRED_CATALOG, null);
571: profileSettings.put(PREFERRED_CATALOG, preferredCatalog);
572: propertyListeners.firePropertyChange(PREFERRED_CATALOG,
573: previous, preferredCatalog);
574: }
575:
576: /**
577: * Sets whether the credentials for a service are persistent with the rest of the configuration.
578: * <p>
579: * For situations of paranoia or just general security practice of not storing passwords in files this is the
580: * setting to turn on/off this behavior.
581: * <p>
582: * This method will fire a property change event for {@link #STORE_CREDENTIALS}.
583: *
584: * @param credentialsPersistent flag to enable/disable storage of service passwords.
585: */
586: public void setCredentialsPersistent(boolean credentialsPersistent) {
587:
588: String previous = profileSettings.get(STORE_CREDENTIALS, null);
589: profileSettings.putBoolean(STORE_CREDENTIALS,
590: credentialsPersistent);
591: propertyListeners.firePropertyChange(STORE_CREDENTIALS,
592: previous, Boolean.valueOf(credentialsPersistent));
593:
594: }
595:
596: /**
597: * Gets the flag for determining is service passwords are persistent.
598: * <p>
599: * This settings defaults to <tt>false</tt>
600: *
601: * @return <tt>false</tt> if credentials for the service were not persisted.
602: */
603: public boolean isCredentialsPersistent() {
604:
605: return profileSettings.getBoolean(STORE_CREDENTIALS, false);
606: }
607:
608: /**
609: * Sets whether the credentials for a service are encrypted or plain-text.
610: * <p>
611: * This method will fire a property change event for {@link #USE_SECURE_CREDENTIALS}.
612: *
613: * @param credentialsSecure flag to enable/disable the encryption of service passwords.
614: */
615: public void setCredentialsSecure(boolean credentialsSecure) {
616:
617: String previous = profileSettings.get(USE_SECURE_CREDENTIALS,
618: null);
619: profileSettings.putBoolean(USE_SECURE_CREDENTIALS,
620: credentialsSecure);
621: propertyListeners.firePropertyChange(USE_SECURE_CREDENTIALS,
622: previous, Boolean.valueOf(credentialsSecure));
623:
624: }
625:
626: /**
627: * Gets the flag for determining is service passwords are encrypted.
628: * <p>
629: * This settings defaults to <tt>true</tt>
630: *
631: * @return <tt>true</tt> if credentials for the service are not in plain-text.
632: */
633: public boolean isCredentialsSecure() {
634:
635: return profileSettings.getBoolean(USE_SECURE_CREDENTIALS, true);
636: }
637:
638: /**
639: * Adds a resourceURL as part of creating a custom classloader for a connection.
640: * <p>
641: * This method will always return false if a null resourceURL is received. All duplicates are discarded and not
642: * added.
643: *
644: * @param location resource containing runtime resources required for the connection implementation.
645: * @return <tt>true</tt> if the resourceURL was sucessfully added.
646: */
647: public boolean addResource(File location) {
648:
649: if (location != null) {
650: return resources.add(location);
651: }
652: return false;
653: }
654:
655: /**
656: * Removes given resourceURL from this profile.
657: * <p>
658: *
659: * @param location to needs to be removed from this profile.
660: * @return <tt>true</tt> if the URL was actually removed.
661: */
662: public boolean removeResource(File location) {
663:
664: if (location != null) {
665: return resources.remove(location);
666: }
667: return false;
668: }
669:
670: /**
671: * Returns a enumeration of the resource URLs defined in this class.
672: * <p>
673: *
674: * @return collection of all added resource urls prior.
675: */
676: public Enumeration<File> resourceElements() {
677:
678: return Collections.<File> enumeration(resources);
679: }
680:
681: /**
682: * Exports the settings of this profile as a properties object.
683: * <p>
684: * Each property can be accessed respectively by the constants defined in this class.
685: *
686: * @return properties object containing all profile settings.
687: */
688: public synchronized Properties toProperties() {
689:
690: return (Properties) profileSettings.clone();
691: }
692:
693: /**
694: * Create a ClassLoader using the implied classloader of this class.
695: * <p>
696: * The parent class-loader will either be the classloader of the parent profile when this instance was created, if
697: * no parent profile was referenced then the parent classloader will be the classloader for this class.
698: *
699: * @return new class loader instance based on resource URLS added to this profile.
700: */
701: public synchronized ClassLoader toClassLoader() {
702:
703: URL[] resourceArray = new URL[resources.size()];
704: int i = 0;
705: for (File f : resources) {
706: try {
707: resourceArray[i++] = f.toURL();
708: } catch (MalformedURLException ignored) {
709: }
710: }
711: return new URLClassLoader(resourceArray, parentClassLoader);
712: }
713:
714: /**
715: * Add a PropertyChangeListener to the listener list.
716: * <p>
717: * The listener is registered for all profile setting changes.
718: *
719: * @param listener The PropertyChangeListener to be added
720: */
721: public synchronized void addPropertyChangeListener(
722: PropertyChangeListener listener) {
723:
724: propertyListeners.addPropertyChangeListener(listener);
725: }
726:
727: /**
728: * Add a PropertyChangeListener for a specific property.
729: * <p>
730: * The listener will be invoked only when a call on firePropertyChange names that specific profile setting.
731: *
732: * @param propertyName value should be one of the declared constants of this class.
733: * @param listener The PropertyChangeListener to be added
734: */
735: public synchronized void addPropertyChangeListener(
736: String propertyName, PropertyChangeListener listener) {
737:
738: propertyListeners.addPropertyChangeListener(propertyName,
739: listener);
740: }
741:
742: /**
743: * Remove a PropertyChangeListener from the listener list.
744: * <p>
745: * This removes a PropertyChangeListener that was registered for all profile settings.
746: *
747: * @param listener The PropertyChangeListener to be removed
748: */
749: public synchronized void removePropertyChangeListener(
750: PropertyChangeListener listener) {
751:
752: propertyListeners.removePropertyChangeListener(listener);
753: }
754:
755: /**
756: * Remove a PropertyChangeListener for a specific profile setting.
757: * <p>
758: *
759: * @param propertyName value should be one of the declared constants of this class.
760: * @param listener The PropertyChangeListener to be removed.
761: */
762: public synchronized void removePropertyChangeListener(
763: String propertyName, PropertyChangeListener listener) {
764:
765: propertyListeners.removePropertyChangeListener(propertyName,
766: listener);
767: }
768:
769: }
|