001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.data.jdbc.datasource;
017:
018: import java.io.IOException;
019: import java.sql.Connection;
020: import java.sql.Statement;
021: import java.util.Arrays;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.Map;
025: import java.util.Set;
026: import java.util.logging.Level;
027: import java.util.logging.Logger;
028:
029: import javax.sql.DataSource;
030:
031: import org.geotools.factory.FactoryCreator;
032: import org.geotools.factory.FactoryRegistry;
033:
034: /**
035: * Enable programs to find all available {@link DataSourceFactorySpi} implementations.
036: *
037: * <p>
038: * In addition to implementing this interface data souces should have a services file:<br/><code>META-INF/services/org.geotools.data.jdbc.DataSourceFactorySpi</code>
039: * </p>
040: *
041: * <p>
042: * The file should contain a single line which gives the full name of the implementing class.
043: * </p>
044: *
045: * <p>
046: * Example:<br/><code>org.geotools.data.jdbc.DBCPDataSourceFactory</code>
047: * </p>
048: *
049: * @source $URL:
050: * http://svn.geotools.org/geotools/trunk/gt/modules/unsupported/h2/src/main/java/org/geotools/data/jdbc/ds/DataSourceFinder.java $
051: */
052: public final class DataSourceFinder {
053: /** The logger for the filter module. */
054: protected static final Logger LOGGER = org.geotools.util.logging.Logging
055: .getLogger("org.geotools.data.jdbc");
056:
057: /**
058: * The service registry for this manager. Will be initialized only when first needed.
059: */
060: private static FactoryRegistry registry;
061:
062: // Singleton pattern
063: private DataSourceFinder() {
064: }
065:
066: /**
067: * Checks each available datasource implementation in turn and returns the first one which
068: * claims to support the resource identified by the params object.
069: *
070: * @param params
071: * A Map object which contains a defenition of the resource to connect to. for file
072: * based resources the property 'url' should be set within this Map.
073: *
074: * @return The first datasource which claims to process the required resource, returns null if
075: * none can be found.
076: *
077: * @throws IOException
078: * If a suitable loader can be found, but it can not be attached to the specified
079: * resource without errors.
080: */
081: public static synchronized DataSource getDataSource(Map params)
082: throws IOException {
083: Iterator ps = getAvailableDataSources();
084: DataSourceFactorySpi fac;
085: while (ps.hasNext()) {
086: fac = (DataSourceFactorySpi) ps.next();
087:
088: try {
089: if (fac.canProcess(params)) {
090: return fac.createDataSource(params);
091: }
092: } catch (Throwable t) {
093: /** The logger for the filter module. */
094: LOGGER.log(Level.WARNING, "Could not acquire "
095: + fac.getDescription() + ":" + t, t);
096: // Protect against DataStores that don't carefully
097: // code canProcess
098:
099: }
100: }
101:
102: return null;
103: }
104:
105: /**
106: * Checks each available datasource implementation in turn and returns the first one which
107: * claims to support the resource identified by the params object.
108: *
109: * @param params
110: * A Map object which contains a defenition of the resource to connect to. for file
111: * based resources the property 'url' should be set within this Map.
112: *
113: * @return The first datasource which claims to process the required resource, returns null if
114: * none can be found.
115: *
116: * @throws IOException
117: * If a suitable loader can be found, but it can not be attached to the specified
118: * resource without errors.
119: */
120: public static synchronized UnWrapper getUnWrapper(Connection conn)
121: throws IOException {
122: Iterator ps = getUnWrappers();
123: UnWrapper uw;
124: while (ps.hasNext()) {
125: uw = (UnWrapper) ps.next();
126:
127: try {
128: if (uw.canUnwrap(conn)) {
129: return uw;
130: }
131: } catch (Throwable t) {
132: /** The logger for the filter module. */
133: LOGGER
134: .log(Level.WARNING, "Could not test " + uw
135: + " for unwrapping abilities agaist "
136: + conn, t);
137: // Protect against DataStores that don't carefully
138: // code canProcess
139:
140: }
141: }
142:
143: return null;
144: }
145:
146: /**
147: * Checks each available {@link UnWrapper} implementation in turn and returns the first one which
148: * claims to support the resource identified by the params object.
149: *
150: * @param params
151: * A Map object which contains a defenition of the resource to connect to. for file
152: * based resources the property 'url' should be set within this Map.
153: *
154: * @return The first datasource which claims to process the required resource, returns null if
155: * none can be found.
156: *
157: * @throws IOException
158: * If a suitable loader can be found, but it can not be attached to the specified
159: * resource without errors.
160: */
161: public static synchronized UnWrapper getUnWrapper(Statement st)
162: throws IOException {
163: Iterator ps = getUnWrappers();
164: UnWrapper uw;
165: while (ps.hasNext()) {
166: uw = (UnWrapper) ps.next();
167:
168: try {
169: if (uw.canUnwrap(st)) {
170: return uw;
171: }
172: } catch (Throwable t) {
173: /** The logger for the filter module. */
174: LOGGER.log(Level.WARNING, "Could not test " + uw
175: + " for unwrapping abilities agaist " + st, t);
176: // Protect against DataStores that don't carefully
177: // code canProcess
178:
179: }
180: }
181:
182: return null;
183: }
184:
185: /**
186: * Finds all implemtaions of DataStoreFactory which have registered using the services
187: * mechanism, and that have the appropriate libraries on the classpath.
188: *
189: * @return An iterator over all discovered datastores which have registered factories, and whose
190: * available method returns true.
191: */
192: public static synchronized Iterator getAvailableDataSources() {
193: Set availableDS = new HashSet();
194: Iterator it = getServiceRegistry().getServiceProviders(
195: DataSourceFactorySpi.class);
196: DataSourceFactorySpi dsFactory;
197: while (it.hasNext()) {
198: dsFactory = (DataSourceFactorySpi) it.next();
199:
200: if (dsFactory.isAvailable()) {
201: availableDS.add(dsFactory);
202: }
203: }
204:
205: return availableDS.iterator();
206: }
207:
208: /**
209: * Finds all implemtaions of DataStoreFactory which have registered using the services
210: * mechanism, and that have the appropriate libraries on the classpath.
211: *
212: * @return An iterator over all discovered datastores which have registered factories, and whose
213: * available method returns true.
214: */
215: public static synchronized Iterator getUnWrappers() {
216: Set availableDS = new HashSet();
217: return getServiceRegistry()
218: .getServiceProviders(UnWrapper.class);
219: }
220:
221: /**
222: * Returns the service registry. The registry will be created the first time this method is
223: * invoked.
224: */
225: private static FactoryRegistry getServiceRegistry() {
226: assert Thread.holdsLock(DataSourceFinder.class);
227: if (registry == null) {
228: registry = new FactoryCreator(Arrays.asList(new Class[] {
229: DataSourceFactorySpi.class, UnWrapper.class }));
230: }
231: return registry;
232: }
233:
234: /**
235: * Scans for factory plug-ins on the application class path. This method is needed because the
236: * application class path can theoretically change, or additional plug-ins may become available.
237: * Rather than re-scanning the classpath on every invocation of the API, the class path is
238: * scanned automatically only on the first invocation. Clients can call this method to prompt a
239: * re-scan. Thus this method need only be invoked by sophisticated applications which
240: * dynamically make new plug-ins available at runtime.
241: */
242: public static synchronized void scanForPlugins() {
243:
244: getServiceRegistry().scanForPlugins();
245:
246: }
247: }
|