/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2003-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.data.jdbc;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.geotools.data.DataSourceException;
import org.geotools.data.Transaction;
import org.geotools.data.jdbc.fidmapper.FIDMapper;
import org.geotools.data.jdbc.fidmapper.FIDMapperFactory;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryDescriptor;
/**
* This class acts as a manager for FIDMappers and FeatureTypeInfo on behalf of a JDBCDataStore.<br>
* At the same time, it acts as a FeatureTypeInfo cache, with a user selectable timeout. Set the
* timeout to 0 if you want each request to be in-line with the actual database state, to
* Long.MAX_VALUE in order to make it work basically in-memory, or to an intermediate value to
* make requests cached allowing at the same time to keep it in-line with a changing database.
*
* @author wolf
* @source $URL$
*
* @deprecated scheduled for removal in 2.7, use classes in org.geotools.jdbc
*/
public class FeatureTypeHandler {
private FIDMapperFactory fmFactory;
protected Map featureTypeMap = new HashMap();
protected Map featureTypeTimeoutMap = new HashMap();
JDBC1DataStore dataStore;
Map typeMappers = new HashMap();
long cacheTimeOut;
long lastTypeNameRequestTime;
/**
* Creates a new feature type handler
*
* @param store the parent data store
* @param fmFactory the FIDMapper factory
* @param cacheTimeOut timeout used to purge possibly stale data from the caches
*/
public FeatureTypeHandler(JDBC1DataStore store, FIDMapperFactory fmFactory, long cacheTimeOut) {
this.dataStore = store;
this.cacheTimeOut = cacheTimeOut;
this.fmFactory = fmFactory;
}
/**
* Returns a list of FeatureType names contained in the parent JDBCDataStore
*
*
* @throws IOException
*/
public String[] getTypeNames() throws IOException {
long lastTime = lastTypeNameRequestTime;
long now = System.currentTimeMillis();
lastTypeNameRequestTime = now;
if ((lastTime < (now - cacheTimeOut)) || (featureTypeMap.size() == 0)) {
// type names cache timeout, better sync with the actual
// list of type names while we're at it
String[] newTypeNames = loadTypeNamesFromDatabase();
HashMap newFeatureTypeMap = new HashMap();
// get the already parsed feature type info from the old map and put them
// into the new one
for (int i = 0; i < newTypeNames.length; i++) {
String typeName = newTypeNames[i];
FeatureTypeInfo info = (FeatureTypeInfo) featureTypeMap.get(typeName);
newFeatureTypeMap.put(typeName, info);
}
featureTypeMap = newFeatureTypeMap;
return newTypeNames;
} else {
// cache has not expired, thus we can freely return it
Set keys = featureTypeMap.keySet();
return (String[]) keys.toArray(new String[keys.size()]);
}
}
/**
* Clears the map between FeatureType name and FIDMappers
*/
public void resetFIDMappers() {
typeMappers.clear();
}
/**
* Really loads the list of FeatureType names from the database
*
*
* @throws IOException
* @throws DataSourceException DOCUMENT ME!
*/
private String[] loadTypeNamesFromDatabase() throws IOException {
final int TABLE_NAME_COL = 3;
Connection conn = null;
List list = new ArrayList();
try {
conn = dataStore.getConnection(Transaction.AUTO_COMMIT);
DatabaseMetaData meta = conn.getMetaData();
String[] tableType = { "TABLE" , "VIEW"};
ResultSet tables = meta.getTables(null, dataStore.config.getDatabaseSchemaName(), "%",
tableType);
while (tables.next()) {
String tableName = tables.getString(TABLE_NAME_COL);
if (dataStore.allowTable(tableName)) {
list.add(tableName);
}
}
return (String[]) list.toArray(new String[list.size()]);
} catch (SQLException sqlException) {
JDBCUtils.close(conn, Transaction.AUTO_COMMIT, sqlException);
conn = null;
String message = "Error querying database for list of tables:"
+ sqlException.getMessage();
throw new DataSourceException(message, sqlException);
} finally {
JDBCUtils.close(conn, Transaction.AUTO_COMMIT, null);
}
}
/**
* Will reverse engineer and return the schema from the database.<br>
* Performance warning: this request will always hit the database for unknown types
*
* @param typeName
*
*
* @throws IOException
*/
public SimpleFeatureType getSchema(String typeName) throws IOException {
FeatureTypeInfo info = getFeatureTypeInfo(typeName);
return info.getSchema();
}
/**
* Retreives the FeatureTypeInfo object for a SimpleFeatureType.
*
* <p>
* This allows subclasses to get access to the information about a feature type, this includes
* the schema and the fidColumnName.
* </p>
*
* @param featureTypeName The name of the feature type to get the info for.
*
* @return The FeatureTypeInfo object for the named feature type or null if the feature type
* does not exist.
*
* @throws IOException If an error occurs creating the FeatureTypeInfo.
*/
public FeatureTypeInfo getFeatureTypeInfo(String featureTypeName)
throws IOException {
long now = System.currentTimeMillis();
Long ftInfoTime = (Long) featureTypeTimeoutMap.get(featureTypeName);
FeatureTypeInfo info = (FeatureTypeInfo) featureTypeMap.get(featureTypeName);
// If the feature type info is not there or is outdated, load it from the
// datastore and put it in the cache
if ((ftInfoTime == null) || ((now - ftInfoTime.longValue()) > cacheTimeOut)) {
// make sure table exists
FIDMapper mapper = getFIDMapper(featureTypeName);
SimpleFeatureType schema = dataStore.buildSchema(featureTypeName, mapper);
info = new FeatureTypeInfo(featureTypeName, schema, mapper);
// get srdid for each geometry
for (AttributeDescriptor ad : schema.getAttributeDescriptors()) {
if (ad instanceof GeometryDescriptor ) {
int srid = dataStore.determineSRID(featureTypeName, ad.getLocalName());
info.putSRID(ad.getLocalName(), srid);
}
}
// put the infos in the feature type info cache, but only if the caching
// is allowed, no point in continously overwriting it otherwise
if (cacheTimeOut > 0) {
featureTypeMap.put(featureTypeName, info);
featureTypeTimeoutMap.put(featureTypeName, new Long(now));
}
}
return info;
}
/**
* @see org.geotools.data.jdbc.FeatureTypeHandler#getFIDMapper(java.lang.String)
*/
public FIDMapper getFIDMapper(String typeName) throws IOException {
FIDMapper mapper = (FIDMapper) typeMappers.get(typeName);
if (mapper != null)
return mapper;
mapper = dataStore.buildFIDMapper(typeName, fmFactory);
if(mapper == null)
throw new RuntimeException("Could not build a FIDMapper for type " + typeName);
typeMappers.put(typeName, mapper);
return mapper;
}
/**
* @see org.geotools.data.jdbc.FeatureTypeHandler#setFIDMapper(java.lang.String)
*/
public void setFIDMapper(String typeName, FIDMapper mapper) {
typeMappers.put(typeName, mapper);
}
/**
* Forces the type handler to throw away all cached information and parse again the database on
* type requests
*/
public void forceRefresh() {
lastTypeNameRequestTime = Long.MIN_VALUE;
featureTypeMap.clear();
featureTypeTimeoutMap.clear();
}
/**
* Returns the FIDMapperFactory used by this FeatureTypeHandler
*
*/
public FIDMapperFactory getFIDMapperFactory() {
return fmFactory;
}
/**
* Sets the FIDMapperFactory used by this FeatureTypeHandler. It can't be null.
*
* @param factory
*/
public void setFIDMapperFactory(FIDMapperFactory factory) {
if(factory == null)
throw new IllegalArgumentException("FIDMapper factory cannot be null");
fmFactory = factory;
}
}