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 }