001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/datastore/sql/generic/GenericSQLDatastore.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2008 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/deegree/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 This library is free software; you can redistribute it and/or 012 modify it under the terms of the GNU Lesser General Public 013 License as published by the Free Software Foundation; either 014 version 2.1 of the License, or (at your option) any later version. 015 016 This library is distributed in the hope that it will be useful, 017 but WITHOUT ANY WARRANTY; without even the implied warranty of 018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 019 Lesser General Public License for more details. 020 021 You should have received a copy of the GNU Lesser General Public 022 License along with this library; if not, write to the Free Software 023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 024 025 Contact: 026 027 Andreas Poth 028 lat/lon GmbH 029 Aennchenstraße 19 030 53177 Bonn 031 Germany 032 E-Mail: poth@lat-lon.de 033 034 Prof. Dr. Klaus Greve 035 Department of Geography 036 University of Bonn 037 Meckenheimer Allee 166 038 53115 Bonn 039 Germany 040 E-Mail: greve@giub.uni-bonn.de 041 042 ---------------------------------------------------------------------------*/ 043 package org.deegree.io.datastore.sql.generic; 044 045 import java.io.BufferedReader; 046 import java.io.InputStream; 047 import java.io.InputStreamReader; 048 import java.io.Reader; 049 import java.io.StringReader; 050 import java.sql.Connection; 051 import java.sql.SQLException; 052 053 import org.deegree.framework.log.ILogger; 054 import org.deegree.framework.log.LoggerFactory; 055 import org.deegree.framework.util.StringTools; 056 import org.deegree.i18n.Messages; 057 import org.deegree.io.JDBCConnection; 058 import org.deegree.io.datastore.Datastore; 059 import org.deegree.io.datastore.DatastoreException; 060 import org.deegree.io.datastore.schema.MappedFeatureType; 061 import org.deegree.io.datastore.sql.AbstractSQLDatastore; 062 import org.deegree.io.datastore.sql.QueryHandler; 063 import org.deegree.io.datastore.sql.SQLDatastoreConfiguration; 064 import org.deegree.io.datastore.sql.TableAliasGenerator; 065 import org.deegree.io.datastore.sql.VirtualContentProvider; 066 import org.deegree.io.datastore.sql.wherebuilder.WhereBuilder; 067 import org.deegree.model.crs.CRSFactory; 068 import org.deegree.model.crs.CoordinateSystem; 069 import org.deegree.model.feature.Feature; 070 import org.deegree.model.feature.FeatureCollection; 071 import org.deegree.model.filterencoding.Filter; 072 import org.deegree.model.filterencoding.FilterEvaluationException; 073 import org.deegree.model.spatialschema.GMLGeometryAdapter; 074 import org.deegree.model.spatialschema.Geometry; 075 import org.deegree.model.spatialschema.GeometryException; 076 import org.deegree.model.spatialschema.GeometryImpl; 077 import org.deegree.ogcbase.SortProperty; 078 import org.deegree.ogcwebservices.wfs.operation.Query; 079 080 /** 081 * {@link Datastore} implementation for any SQL database that can be accessed through a jdbc 082 * connection (even the odbc-jdbc bridge is supported) and that supports the storing of BLOBs. 083 * <p> 084 * The spatial information is assumed to be stored in a BLOB field as a serialized deegree geometry. 085 * It also will be assumed that a spatial index exists and that it has been created using deegree's 086 * quadtree api. 087 * 088 * @see org.deegree.io.quadtree 089 * 090 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 091 * @author last edited by: $Author: apoth $ 092 * 093 * @version $Revision: 9342 $, $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 2007) $ 094 */ 095 public class GenericSQLDatastore extends AbstractSQLDatastore { 096 097 protected static final ILogger LOG = LoggerFactory.getLogger( GenericSQLDatastore.class ); 098 099 @Override 100 public WhereBuilder getWhereBuilder( MappedFeatureType[] rootFts, String[] aliases, Filter filter, 101 SortProperty[] sortProperties, TableAliasGenerator aliasGenerator, 102 VirtualContentProvider vcProvider ) 103 throws DatastoreException { 104 JDBCConnection jdbc = ( (SQLDatastoreConfiguration) getConfiguration() ).getJDBCConnection(); 105 return new GenericSQLWhereBuilder( rootFts, aliases, filter, sortProperties, aliasGenerator, vcProvider, jdbc ); 106 } 107 108 @Override 109 protected FeatureCollection performQuery( Query query, MappedFeatureType[] rootFts, Connection conn ) 110 throws DatastoreException { 111 112 query = transformQuery( query ); 113 114 FeatureCollection result = null; 115 try { 116 QueryHandler queryHandler = new QueryHandler( this, new TableAliasGenerator(), conn, rootFts, query ); 117 result = queryHandler.performQuery(); 118 } catch ( SQLException e ) { 119 String msg = "SQL error while performing query: " + e.getMessage(); 120 LOG.logError( msg, e ); 121 throw new DatastoreException( msg, e ); 122 } catch ( Exception e ) { 123 LOG.logError( e.getMessage(), e ); 124 throw new DatastoreException( e ); 125 } 126 127 if ( query.getFilter() != null ) { 128 try { 129 LOG.logDebug( "Features (before refinement): " + result.size() ); 130 result = filterCollection( result, query.getFilter() ); 131 LOG.logDebug( "Features (after refinement): " + result.size() ); 132 } catch ( Exception e ) { 133 LOG.logError( e.getMessage(), e ); 134 throw new DatastoreException( e.getMessage(), e ); 135 } 136 } 137 138 result = transformResult( result, query.getSrsName() ); 139 return result; 140 } 141 142 /** 143 * Filters the feature collection using the given filter. 144 * <p> 145 * This is required because spatial filtering performed in the {@link GenericSQLWhereBuilder} 146 * just considers the BBOX and non-spatial filter conditions. 147 * 148 * TODO remove BBOX + non-spatial conditions from the filter to speed up evaluation 149 * 150 * @param fc 151 * @param filter 152 * @return filtered feature collection 153 */ 154 private FeatureCollection filterCollection( FeatureCollection fc, Filter filter ) 155 throws FilterEvaluationException { 156 for ( int i = fc.size() - 1; i >= 0; i-- ) { 157 Feature feat = fc.getFeature( i ); 158 if ( !filter.evaluate( feat ) ) { 159 fc.remove( i ); 160 } 161 } 162 return fc; 163 } 164 165 @Override 166 public Geometry convertDBToDeegreeGeometry( Object value, CoordinateSystem targetCS, Connection conn ) 167 throws SQLException { 168 169 Geometry geometry = null; 170 if ( value != null ) { 171 try { 172 if ( targetCS == null ) { 173 targetCS = CRSFactory.create( "EPSG:4326" ); 174 } 175 if ( value instanceof String ) { 176 geometry = GMLGeometryAdapter.wrap( (String) value, null ); 177 } else if ( value instanceof InputStream ) { 178 StringBuffer sb = new StringBuffer( 10000 ); 179 BufferedReader br = new BufferedReader( new InputStreamReader( (InputStream) value ) ); 180 String line = null; 181 while ( ( line = br.readLine() ) != null ) { 182 sb.append( line ); 183 } 184 geometry = GMLGeometryAdapter.wrap( sb.toString(), null ); 185 } else if ( value instanceof Reader ) { 186 StringBuffer sb = new StringBuffer( 10000 ); 187 BufferedReader br = new BufferedReader( (Reader) value ); 188 String line = null; 189 while ( ( line = br.readLine() ) != null ) { 190 sb.append( line ); 191 } 192 geometry = GMLGeometryAdapter.wrap( sb.toString(), null ); 193 } else { 194 geometry = GMLGeometryAdapter.wrap( new String( (byte[]) value ), null ); 195 } 196 ( (GeometryImpl) geometry ).setCoordinateSystem( targetCS ); 197 } catch ( Exception e ) { 198 String msg = Messages.getMessage( "DATASTORE_GENERICSQL_GEOM_READ_ERROR" ); 199 LOG.logError( msg, e ); 200 throw new SQLException( msg + e.getMessage() ); 201 } 202 } 203 return geometry; 204 } 205 206 @Override 207 public Object convertDeegreeToDBGeometry( Geometry geometry, int nativeSRSCode, Connection conn ) 208 throws DatastoreException { 209 String sqlObject = null; 210 try { 211 sqlObject = GMLGeometryAdapter.export( geometry ).toString(); 212 sqlObject = StringTools.replace( sqlObject, ">", " xmlns:gml=\"http://www.opengis.net/gml\">", false ); 213 } catch ( GeometryException e ) { 214 LOG.logError( e.getMessage(), e ); 215 throw new DatastoreException( e.getMessage(), e ); 216 } 217 JDBCConnection jdbc = ( (SQLDatastoreConfiguration) getConfiguration() ).getJDBCConnection(); 218 Object result = null; 219 // concrete from depends on backend. At the moment special mapping just is defined 220 // for HSQLDB, Postgres und INGRES. Default will work for MS SQLSERVER 221 if ( jdbc.getDriver().toLowerCase().indexOf( "ingres" ) > -1 222 || jdbc.getDriver().equalsIgnoreCase( "ca.edbc.jdbc.EdbcDriver" ) ) { 223 result = new StringReader( sqlObject ); 224 } else if ( jdbc.getDriver().toLowerCase().indexOf( "hsqldb" ) > -1 225 || jdbc.getDriver().toLowerCase().indexOf( "postgres" ) > -1 ) { 226 // only postgres is able to handle large strings as they are 227 result = sqlObject; 228 } else { 229 result = sqlObject.getBytes(); 230 } 231 return result; 232 } 233 234 @Override 235 protected GenericSQLTransaction createTransaction() 236 throws DatastoreException { 237 238 return new GenericSQLTransaction( this, new TableAliasGenerator(), acquireConnection() ); 239 } 240 }