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;
017:
018: import java.io.IOException;
019: import java.sql.Connection;
020: import java.sql.DatabaseMetaData;
021: import java.sql.ResultSet;
022: import java.sql.SQLException;
023: import java.util.ArrayList;
024: import java.util.HashMap;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.Set;
028:
029: import org.geotools.data.DataSourceException;
030: import org.geotools.data.Transaction;
031: import org.geotools.data.jdbc.fidmapper.FIDMapper;
032: import org.geotools.data.jdbc.fidmapper.FIDMapperFactory;
033: import org.geotools.feature.AttributeType;
034: import org.geotools.feature.FeatureType;
035: import org.geotools.feature.GeometryAttributeType;
036:
037: /**
038: * This class acts as a manager for FIDMappers and FeatureTypeInfo on behalf of a JDBCDataStore.<br>
039: * At the same time, it acts as a FeatureTypeInfo cache, with a user selectable timeout. Set the
040: * timeout to 0 if you want each request to be in-line with the actual database state, to
041: * Long.MAX_VALUE in order to make it work basically in-memory, or to an intermediate value to
042: * make requests cached allowing at the same time to keep it in-line with a changing database.
043: *
044: * @author wolf
045: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/jdbc/src/main/java/org/geotools/data/jdbc/FeatureTypeHandler.java $
046: */
047: public class FeatureTypeHandler {
048: private FIDMapperFactory fmFactory;
049: protected Map featureTypeMap = new HashMap();
050: protected Map featureTypeTimeoutMap = new HashMap();
051: JDBC1DataStore dataStore;
052: Map typeMappers = new HashMap();
053: long cacheTimeOut;
054: long lastTypeNameRequestTime;
055:
056: /**
057: * Creates a new feature type handler
058: *
059: * @param store the parent data store
060: * @param fmFactory the FIDMapper factory
061: * @param cacheTimeOut timeout used to purge possibly stale data from the caches
062: */
063: public FeatureTypeHandler(JDBC1DataStore store,
064: FIDMapperFactory fmFactory, long cacheTimeOut) {
065: this .dataStore = store;
066: this .cacheTimeOut = cacheTimeOut;
067: this .fmFactory = fmFactory;
068: }
069:
070: /**
071: * Returns a list of FeatureType names contained in the parent JDBCDataStore
072: *
073: *
074: * @throws IOException
075: */
076: public String[] getTypeNames() throws IOException {
077: long lastTime = lastTypeNameRequestTime;
078: long now = System.currentTimeMillis();
079: lastTypeNameRequestTime = now;
080:
081: if ((lastTime < (now - cacheTimeOut))
082: || (featureTypeMap.size() == 0)) {
083: // type names cache timeout, better sync with the actual
084: // list of type names while we're at it
085: String[] newTypeNames = loadTypeNamesFromDatabase();
086:
087: HashMap newFeatureTypeMap = new HashMap();
088:
089: // get the already parsed feature type info from the old map and put them
090: // into the new one
091: for (int i = 0; i < newTypeNames.length; i++) {
092: String typeName = newTypeNames[i];
093: FeatureTypeInfo info = (FeatureTypeInfo) featureTypeMap
094: .get(typeName);
095: newFeatureTypeMap.put(typeName, info);
096: }
097:
098: featureTypeMap = newFeatureTypeMap;
099:
100: return newTypeNames;
101: } else {
102: // cache has not expired, thus we can freely return it
103: Set keys = featureTypeMap.keySet();
104:
105: return (String[]) keys.toArray(new String[keys.size()]);
106: }
107: }
108:
109: /**
110: * Clears the map between FeatureType name and FIDMappers
111: */
112: public void resetFIDMappers() {
113: typeMappers.clear();
114: }
115:
116: /**
117: * Really loads the list of FeatureType names from the database
118: *
119: *
120: * @throws IOException
121: * @throws DataSourceException DOCUMENT ME!
122: */
123: private String[] loadTypeNamesFromDatabase() throws IOException {
124: final int TABLE_NAME_COL = 3;
125: Connection conn = null;
126: List list = new ArrayList();
127:
128: try {
129: conn = dataStore.getConnection(Transaction.AUTO_COMMIT);
130:
131: DatabaseMetaData meta = conn.getMetaData();
132: String[] tableType = { "TABLE", "VIEW" };
133: ResultSet tables = meta.getTables(null, dataStore.config
134: .getDatabaseSchemaName(), "%", tableType);
135:
136: while (tables.next()) {
137: String tableName = tables.getString(TABLE_NAME_COL);
138:
139: if (dataStore.allowTable(tableName)) {
140: list.add(tableName);
141: }
142: }
143:
144: return (String[]) list.toArray(new String[list.size()]);
145: } catch (SQLException sqlException) {
146: JDBCUtils
147: .close(conn, Transaction.AUTO_COMMIT, sqlException);
148: conn = null;
149:
150: String message = "Error querying database for list of tables:"
151: + sqlException.getMessage();
152: throw new DataSourceException(message, sqlException);
153: } finally {
154: JDBCUtils.close(conn, Transaction.AUTO_COMMIT, null);
155: }
156: }
157:
158: /**
159: * Will reverse engineer and return the schema from the database.<br>
160: * Performance warning: this request will always hit the database for unknown types
161: *
162: * @param typeName
163: *
164: *
165: * @throws IOException
166: */
167: public FeatureType getSchema(String typeName) throws IOException {
168: FeatureTypeInfo info = getFeatureTypeInfo(typeName);
169:
170: return info.getSchema();
171: }
172:
173: /**
174: * Retreives the FeatureTypeInfo object for a FeatureType.
175: *
176: * <p>
177: * This allows subclasses to get access to the information about a feature type, this includes
178: * the schema and the fidColumnName.
179: * </p>
180: *
181: * @param featureTypeName The name of the feature type to get the info for.
182: *
183: * @return The FeatureTypeInfo object for the named feature type or null if the feature type
184: * does not exist.
185: *
186: * @throws IOException If an error occurs creating the FeatureTypeInfo.
187: */
188: public FeatureTypeInfo getFeatureTypeInfo(String featureTypeName)
189: throws IOException {
190: long now = System.currentTimeMillis();
191: Long ftInfoTime = (Long) featureTypeTimeoutMap
192: .get(featureTypeName);
193:
194: FeatureTypeInfo info = (FeatureTypeInfo) featureTypeMap
195: .get(featureTypeName);
196:
197: // If the feature type info is not there or is outdated, load it from the
198: // datastore and put it in the cache
199: if ((ftInfoTime == null)
200: || ((now - ftInfoTime.longValue()) > cacheTimeOut)) {
201: // make sure table exists
202: FIDMapper mapper = getFIDMapper(featureTypeName);
203: FeatureType schema = dataStore.buildSchema(featureTypeName,
204: mapper);
205: info = new FeatureTypeInfo(featureTypeName, schema, mapper);
206:
207: AttributeType[] types = schema.getAttributeTypes();
208:
209: // get srdid for each geometry
210: for (int i = 0; i < types.length; i++) {
211: if (types[i] instanceof GeometryAttributeType) {
212: int srid = dataStore.determineSRID(featureTypeName,
213: types[i].getName());
214: info.putSRID(types[i].getName(), srid);
215: }
216: }
217:
218: // put the infos in the feature type info cache, but only if the caching
219: // is allowed, no point in continously overwriting it otherwise
220: if (cacheTimeOut > 0) {
221: featureTypeMap.put(featureTypeName, info);
222: featureTypeTimeoutMap.put(featureTypeName,
223: new Long(now));
224: }
225: }
226:
227: return info;
228: }
229:
230: /**
231: * @see org.geotools.data.jdbc.FeatureTypeHandler#getFIDMapper(java.lang.String)
232: */
233: public FIDMapper getFIDMapper(String typeName) throws IOException {
234: FIDMapper mapper = (FIDMapper) typeMappers.get(typeName);
235:
236: if (mapper != null)
237: return mapper;
238:
239: mapper = dataStore.buildFIDMapper(typeName, fmFactory);
240: if (mapper == null)
241: throw new RuntimeException(
242: "Could not build a FIDMapper for type " + typeName);
243: typeMappers.put(typeName, mapper);
244: return mapper;
245: }
246:
247: /**
248: * @see org.geotools.data.jdbc.FeatureTypeHandler#setFIDMapper(java.lang.String)
249: */
250: public void setFIDMapper(String typeName, FIDMapper mapper) {
251: typeMappers.put(typeName, mapper);
252: }
253:
254: /**
255: * Forces the type handler to throw away all cached information and parse again the database on
256: * type requests
257: */
258: public void forceRefresh() {
259: lastTypeNameRequestTime = Long.MIN_VALUE;
260: featureTypeMap.clear();
261: featureTypeTimeoutMap.clear();
262: }
263:
264: /**
265: * Returns the FIDMapperFactory used by this FeatureTypeHandler
266: *
267: */
268: public FIDMapperFactory getFIDMapperFactory() {
269: return fmFactory;
270: }
271:
272: /**
273: * Sets the FIDMapperFactory used by this FeatureTypeHandler. It can't be null.
274: *
275: * @param factory
276: */
277: public void setFIDMapperFactory(FIDMapperFactory factory) {
278: if (factory == null)
279: throw new IllegalArgumentException(
280: "FIDMapper factory cannot be null");
281:
282: fmFactory = factory;
283: }
284: }
|