001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/quadtree/DBQuadtreeManager.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 Aennchenstr. 19 030 53115 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.quadtree; 044 045 import java.io.IOException; 046 import java.io.StringReader; 047 import java.security.InvalidParameterException; 048 import java.sql.Connection; 049 import java.sql.Date; 050 import java.sql.PreparedStatement; 051 import java.sql.ResultSet; 052 import java.sql.ResultSetMetaData; 053 import java.sql.SQLException; 054 import java.sql.Statement; 055 import java.util.HashMap; 056 import java.util.UUID; 057 058 import org.deegree.datatypes.Types; 059 import org.deegree.framework.log.ILogger; 060 import org.deegree.framework.log.LoggerFactory; 061 import org.deegree.framework.util.StringTools; 062 import org.deegree.io.DBConnectionPool; 063 import org.deegree.io.DBPoolException; 064 import org.deegree.io.JDBCConnection; 065 import org.deegree.io.dbaseapi.DBaseException; 066 import org.deegree.io.shpapi.HasNoDBaseFileException; 067 import org.deegree.io.shpapi.ShapeFile; 068 import org.deegree.model.feature.Feature; 069 import org.deegree.model.feature.schema.FeatureType; 070 import org.deegree.model.feature.schema.PropertyType; 071 import org.deegree.model.spatialschema.Envelope; 072 import org.deegree.model.spatialschema.GMLGeometryAdapter; 073 import org.deegree.model.spatialschema.Geometry; 074 import org.deegree.model.spatialschema.GeometryException; 075 import org.deegree.model.spatialschema.GeometryFactory; 076 import org.deegree.model.spatialschema.Point; 077 078 /** 079 * Access control to a quadtree for managing spatial indizes stored in a usual database. 080 * 081 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 082 * @author last edited by: $Author: apoth $ 083 * 084 * @version $Revision: 9342 $, $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 2007) $ 085 * @param <T> 086 * the type of the quadtree. If unsure use the determineQuattreType() method to determine the type. Be 087 * carefull though, if you use a wrong generic here (e.g. not Integer or String) while supplying another 088 * types.Type to the constructor there is no way to check find the correct instance. 089 */ 090 public class DBQuadtreeManager<T> { 091 092 private static final ILogger LOG = LoggerFactory.getLogger( DBQuadtreeManager.class ); 093 094 protected JDBCConnection jdbc = null; 095 096 protected String table = null; 097 098 protected String column = null; 099 100 protected String owner = null; 101 102 protected String indexName = null; 103 104 protected int maxDepth = 6; 105 106 private DBQuadtree<T> qt = null; 107 108 protected Envelope envelope = null; 109 110 protected String backend = null; 111 112 private int TYPE; 113 114 private static HashMap<String, String> quadTreeVersionInfo = new HashMap<String, String>(); 115 116 /** 117 * @param jdbc 118 * database connection info 119 * @param owner 120 * owner of the table (optional, database user will be used if set to null ) 121 * @param indexName 122 * this name will be used to create the table that stores the nodes of a specific quadtree 123 * @param table 124 * name of table the index shall be created for 125 * @param column 126 * name of column the index shall be created for 127 * @param maxDepth 128 * max depth of the generated quadtree (default = 6 if a value < 2 will be passed) 129 * @param type 130 * the type of the id of the quadtree, valid are Types.INTEGER and Types.VARCHAR. if unsure use 131 * Intger.MIN_VALUE and call {@link #determineQuattreeType()}, to get an instance of the Type. 132 */ 133 public DBQuadtreeManager( JDBCConnection jdbc, String owner, String indexName, String table, String column, 134 int maxDepth, int type ) { 135 TYPE = type; 136 if ( TYPE != Types.INTEGER && TYPE != Types.VARCHAR ) { 137 TYPE = Integer.MIN_VALUE; 138 } 139 if ( jdbc == null ) { 140 throw new InvalidParameterException( "The JDBCConnection reference parameter 'jdbc' may not be null." ); 141 } 142 this.jdbc = jdbc; 143 if ( table == null || "".equals( table.trim() ) ) { 144 throw new InvalidParameterException( "The 'table' parameter may not be null or emtpy." ); 145 } 146 this.table = table.trim(); 147 148 if ( indexName == null || "".equals( indexName.trim() ) || "idx_".equalsIgnoreCase( indexName.trim() ) ) { 149 throw new InvalidParameterException( "The 'indexName' parameter may not be null or emtpy or solumnly exist of idx_." ); 150 } 151 this.indexName = indexName.trim(); 152 153 if ( column == null || "".equals( column.trim() ) ) { 154 throw new InvalidParameterException( "The 'column' parameter may not be null or emtpy." ); 155 } 156 this.column = column.trim(); 157 158 this.owner = owner; 159 if ( owner == null ) { 160 String user = jdbc.getUser(); 161 if ( user == null || "".equals( user.trim() ) ) { 162 this.owner = ""; 163 } else { 164 this.owner = user; 165 } 166 } 167 if ( maxDepth > 1 ) { 168 this.maxDepth = maxDepth; 169 } else { 170 this.maxDepth = 6; 171 } 172 173 String driver = jdbc.getDriver(); 174 if ( driver == null || "".equals( driver.trim() ) ) { 175 throw new InvalidParameterException( "The JDBCConnection.driver may not be null or emtpy." ); 176 } 177 // find out which database is used 178 if ( driver.toUpperCase().contains( "POSTGRES" ) ) { 179 backend = "POSTGRES"; 180 } else if ( driver.toUpperCase().contains( "SQLSERVER" ) ) { 181 backend = "SQLSERVER"; 182 } else if ( driver.toUpperCase().contains( "INGRES" ) || driver.equals( "ca.edbc.jdbc.EdbcDriver" ) ) { 183 backend = "INGRES"; 184 } else if ( driver.toUpperCase().contains( "HSQLDB" ) ) { 185 backend = "HSQLDB"; 186 } else { 187 backend = "GENERICSQL"; 188 } 189 190 try { 191 if ( !hasIndexTable() ) { 192 LOG.logDebug( "It seems no indextable with name: '"+indexName +"' exists in the database backend, creating one." ); 193 createIndexTable( indexName, "VARCHAR(50)" ); 194 } 195 } catch ( IndexException e ) { 196 LOG.logError( "Following error occurred while trying to create the indexTable: " + e.getMessage(), e ); 197 } 198 } 199 200 201 /** 202 * 203 * @param driver 204 * database connection driver 205 * @param logon 206 * database connection logon 207 * @param user 208 * database user 209 * @param password 210 * database user's password 211 * @param encoding 212 * character encoding to be used (if possible) 213 * @param indexName 214 * this name will be used to create the table that stores the nodes of a specific quadtree 215 * @param table 216 * name of table the index shall be created for 217 * @param column 218 * name of column the index shall be created for 219 * @param owner 220 * owner of the table (optional, database user will be used if set to null ) 221 * @param maxDepth 222 * max depth of the generated quadtree (default = 6 if a value < 2 will be passed) 223 * @param type 224 * the type of the id of the quadtree, valid are Types.INTEGER and Types.VARCHAR. if unsure use 225 * Intger.MIN_VALUE and call {@link #determineQuattreeType()}, to get an instance of the Type. 226 */ 227 public DBQuadtreeManager( String driver, String logon, String user, String password, String encoding, 228 String indexName, String table, String column, String owner, int maxDepth, int type ) { 229 this( new JDBCConnection( driver, logon, user, password, null, encoding, null ), 230 owner, 231 indexName, 232 table, 233 column, 234 maxDepth, 235 type ); 236 } 237 238 /** 239 * initializes a QuadtreeManager to access an alread existing Quadtree 240 * 241 * @param jdbc 242 * database connection info 243 * @param table 244 * name of table the index shall be created for 245 * @param column 246 * name of column the index shall be created for 247 * @param owner 248 * owner of the table (optional, database user will be used if set to null ) 249 * @param type 250 * the type of the id of the quadtree, valid are Types.INTEGER and Types.VARCHAR. if unsure use 251 * Intger.MIN_VALUE and call {@link #determineQuattreeType()}, to get an instance of the Type. 252 */ 253 public DBQuadtreeManager( JDBCConnection jdbc, String table, String column, String owner, int type ) { 254 this( jdbc, owner, "idx_" + table, table, column, 6, type ); 255 } 256 257 /** 258 * initializes a QuadtreeManager to access an alread existing Quadtree 259 * 260 * @param driver 261 * database connection driver 262 * @param logon 263 * database connection logon 264 * @param user 265 * database user 266 * @param password 267 * database user's password 268 * @param encoding 269 * character encoding to be used (if possible) 270 * @param table 271 * name of table the index shall be created for 272 * @param column 273 * name of column the index shall be created for 274 * @param owner 275 * owner of the table (optional, database user will be used if set to null ) 276 * @param type 277 * the type of the id of the quadtree, valid are Types.INTEGER and Types.VARCHAR. if unsure use 278 * Intger.MIN_VALUE and call {@link #determineQuattreeType()}, to get an instance of the Type. 279 */ 280 public DBQuadtreeManager( String driver, String logon, String user, String password, String encoding, String table, 281 String column, String owner, int type ) { 282 this( new JDBCConnection( driver, logon, user, password, null, encoding, null ), 283 owner, 284 "idx_" + table, 285 table, 286 column, 287 6, 288 type ); 289 } 290 291 /** 292 * loads the metadata of a Index from the TAB_DEEGREE_IDX table 293 * 294 * @return FK to the index 295 * @throws IndexException 296 */ 297 protected int loadIndexMetadata() 298 throws IndexException { 299 int fk_indexTree = -1; 300 Connection con = null; 301 DBConnectionPool pool = null; 302 try { 303 pool = DBConnectionPool.getInstance(); 304 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 305 306 StringBuilder sb = new StringBuilder( 200 ); 307 sb.append( "Select INDEX_NAME, FK_INDEXTREE from TAB_DEEGREE_IDX where " ); 308 sb.append( "column_name = '" ).append( column ).append( "' AND " ); 309 sb.append( "table_name = '" ).append( table ).append( "' AND " ); 310 sb.append( "owner = '" ).append( owner ).append( "'" ); 311 312 Statement stmt = con.createStatement(); 313 ResultSet rs = stmt.executeQuery( sb.toString() ); 314 315 if ( rs.next() ) { 316 indexName = rs.getString( "INDEX_NAME" ); 317 fk_indexTree = rs.getInt( "FK_INDEXTREE" ); 318 } else { 319 throw new IndexException( "Could not read the structure of the quadtree tables from database (did you run the base/scripts/index/quadtree.hsql script, which create the meta-info tables?)." ); 320 } 321 rs.close(); 322 stmt.close(); 323 } catch ( SQLException e ) { 324 throw new IndexException( "Could not load quadtree definition from database (did you run the base/scripts/index/quadtree.hsql script, which create the meta-info tables?). The error message was: " + e.getMessage() ); 325 } catch ( DBPoolException e ) { 326 throw new IndexException( "Could not acquire a database connection. The error message was: " + e.getMessage() ); 327 } finally { 328 try { 329 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 330 } catch ( Exception e1 ) { 331 LOG.logError( "Could not release the jdbc connection because: " + e1.getMessage() ); 332 } 333 } 334 LOG.logDebug( "It seems an indextable with name: '"+indexName +"' allready exists in the database backend." ); 335 return fk_indexTree; 336 } 337 338 /** 339 * returns the current Quadtree 340 * 341 * @return the current Quadtree 342 * @throws IndexException 343 */ 344 public DBQuadtree<T> getQuadtree() 345 throws IndexException { 346 if ( qt == null ) { 347 qt = loadQuadtree(); 348 } 349 return qt; 350 } 351 352 /** 353 * loads an already existing quadtree 354 * 355 * @return the Quadtree structure read from the database 356 * @throws IndexException 357 */ 358 private DBQuadtree<T> loadQuadtree() 359 throws IndexException { 360 int fk_index = loadIndexMetadata(); 361 362 String version = getQTVersion( table ); 363 return new DBQuadtree<T>( fk_index, indexName, jdbc, version ); 364 } 365 366 /** 367 * @return an instance of the type of the feature id's stored in the db. Possible instances are 368 * <code>String<code>, <code>Integer</code> or <code>null</code> if the type could not be determined. 369 * @throws IndexException if the type information could not be retrieved either because no connection was acquired or an error occurred while executing the select statement. 370 */ 371 public Object determineQuattreeType() 372 throws IndexException { 373 374 if ( TYPE == Integer.MIN_VALUE ) { 375 StringBuilder sb = new StringBuilder( 1000 ); 376 sb.append( "SELECT FK_ITEM from " ).append( indexName ).append( "_ITEM " ); 377 Connection con = null; 378 DBConnectionPool pool = null; 379 380 try { 381 pool = DBConnectionPool.getInstance(); 382 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 383 384 PreparedStatement stmt = con.prepareStatement( sb.toString() ); 385 ResultSet rs = stmt.executeQuery(); 386 ResultSetMetaData metaData = rs.getMetaData(); 387 388 if ( metaData != null ) { 389 TYPE = metaData.getColumnType( 1 ); 390 LOG.logDebug( "Found type: "+ TYPE ); 391 } 392 rs.close(); 393 stmt.close(); 394 395 } catch ( SQLException e ) { 396 throw new IndexException( "Could not get Type information because: " + e.getMessage(), e ); 397 } catch ( DBPoolException e ) { 398 throw new IndexException( "Could not acquire a connection to the database to retrieve column information because: " + e.getMessage(), 399 e ); 400 } finally { 401 try { 402 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 403 } catch ( DBPoolException e ) { 404 LOG.logError( "Could not release JDBC connection because: " + e.getMessage() ); 405 } 406 } 407 } 408 Object result = null; 409 switch ( TYPE ) { 410 case Types.VARCHAR: 411 result = ""; 412 break; 413 case Types.INTEGER: 414 result = new Integer( 1 ); 415 break; 416 default: 417 TYPE = Integer.MAX_VALUE; 418 } 419 return result; 420 } 421 422 /** 423 * @param table 424 * to open a quadtree for. 425 * @return the version of the quadtree used. 426 */ 427 private String getQTVersion( String table ) { 428 String version = "1.0.0"; 429 if ( quadTreeVersionInfo.containsKey( table ) && quadTreeVersionInfo.get( table ) != null ) { 430 LOG.logDebug( "Retrieved the quatdree version info for table: " + table + " from cache." ); 431 version = quadTreeVersionInfo.get( table ); 432 } else { 433 Connection con = null; 434 DBConnectionPool pool = null; 435 Statement stmt = null; 436 ResultSet rs = null; 437 pool = DBConnectionPool.getInstance(); 438 StringBuilder sb = new StringBuilder( 400 ); 439 sb.append( "SELECT fk_indextree FROM tab_deegree_idx WHERE " ); 440 sb.append( "column_name = 'geometry' AND " ); 441 sb.append( "table_name = '" ).append( table.toLowerCase() ).append( "'" ); 442 443 LOG.logDebug( "Get Index Metadata sql statement:\n", sb ); 444 try { 445 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 446 stmt = con.createStatement(); 447 rs = stmt.executeQuery( sb.toString() ); 448 String tableID = null; 449 if ( rs.next() ) { 450 tableID = rs.getString( 1 ); 451 } 452 if ( tableID != null ) { 453 sb = new StringBuilder( 400 ); 454 sb.append( "SELECT * FROM tab_quadtree WHERE " ); 455 sb.append( "fk_root = '" ).append( tableID.trim() ).append( "'" ); 456 if ( rs != null ) { 457 rs.close(); 458 } 459 if ( stmt != null ) { 460 stmt.close(); 461 } 462 stmt = con.createStatement(); 463 rs = stmt.executeQuery( sb.toString() ); 464 if ( rs.next() ) { 465 boolean hasVersion = false; 466 ResultSetMetaData md = rs.getMetaData(); 467 int numberOfColumns = md.getColumnCount(); 468 System.out.println( "Columnecount: " + numberOfColumns ); 469 for ( int i = 1; i <= numberOfColumns && !hasVersion; i++ ) { 470 String tmp = md.getColumnName( i ); 471 LOG.logDebug( "Found columnname: " + tmp ); 472 if ( tmp != null ) { 473 if ( "version".equalsIgnoreCase( tmp.trim() ) ) { 474 hasVersion = true; 475 version = rs.getString( i ); 476 LOG.logDebug( "Found a version column, setting version to: " + rs.getString( i ) ); 477 } 478 } 479 } 480 if ( !hasVersion ) { 481 try { 482 LOG.logInfo( "Found no Version Column in the TAB_QUADTREE table, assuming version 1.0.0, and adding the version column." ); 483 if ( rs != null ) { 484 rs.close(); 485 } 486 if ( stmt != null ) { 487 stmt.close(); 488 } 489 stmt = con.createStatement(); 490 rs = stmt.executeQuery( "ALTER TABLE TAB_QUADTREE ADD version VARCHAR(15)" ); 491 rs.close(); 492 stmt.close(); 493 } catch ( SQLException e ) { 494 if ( rs != null ) { 495 rs.close(); 496 } 497 if ( stmt != null ) { 498 stmt.close(); 499 } 500 LOG.logError( "An error occurred while trying to insert a new 'version' column in the database: " + e.getMessage(), 501 e ); 502 } 503 } 504 } 505 } else { 506 LOG.logError( "Could not find the foreign key (fk_root) of the table: '" + table 507 + "' is your database is set up correct?" ); 508 } 509 } catch ( SQLException e ) { 510 LOG.logError( "An error occurred while determening version of quadtree, therefore setting version to '1.0.0'. Errormessage: " + e.getMessage(), 511 e ); 512 } catch ( DBPoolException e ) { 513 LOG.logError( "An error occurred while acquiring connection to the database to determine version of quadtree, therefore setting version to '1.0.0'. Errormessage: " + e.getMessage(), 514 e ); 515 } finally { 516 quadTreeVersionInfo.put( table, version ); 517 try { 518 if ( rs != null ) { 519 rs.close(); 520 } 521 if ( stmt != null ) { 522 stmt.close(); 523 } 524 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 525 } catch ( SQLException e ) { 526 LOG.logError( "Could not close ResultSet or Statement because: " + e.getMessage() ); 527 } catch ( DBPoolException e ) { 528 LOG.logError( "Could not reslease connection because: " + e.getMessage() ); 529 } 530 } 531 } 532 return version; 533 } 534 535 /** 536 * stores one feature into the defined table 537 * 538 * @param feature 539 * the feature to insert into the 'table' 540 * @param id 541 * of the feature to store in the database, currently String and Integer are supported. If it is neither, 542 * the Object is saved as an object, which may result in inconsitencies. 543 * @param jdbc 544 * the connection to the database. 545 * @throws IndexException 546 * if the feature can not be inserted or a connection error occurrs. 547 */ 548 protected void storeFeature( Feature feature, T id, JDBCConnection jdbc ) 549 throws IndexException { 550 551 Connection con = null; 552 DBConnectionPool pool = null; 553 554 FeatureType ft = feature.getFeatureType(); 555 PropertyType[] ftp = ft.getProperties(); 556 try { 557 pool = DBConnectionPool.getInstance(); 558 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 559 560 StringBuilder sb = new StringBuilder( 100 ); 561 sb.append( "INSERT INTO " ).append( table ).append( '(' ); 562 sb.append( "FEATURE_ID," ); 563 for ( int i = 0; i < ftp.length; i++ ) { 564 if ( ftp[i].getType() == Types.GEOMETRY ) { 565 sb.append( column ).append( ' ' ); 566 } else { 567 sb.append( ftp[i].getName().getLocalName() ); 568 } 569 if ( i < ftp.length - 1 ) { 570 sb.append( ", " ); 571 } 572 } 573 sb.append( ") VALUES (?," ); 574 for ( int i = 0; i < ftp.length; i++ ) { 575 sb.append( '?' ); 576 if ( i < ftp.length - 1 ) { 577 sb.append( ", " ); 578 } 579 } 580 sb.append( ')' ); 581 582 PreparedStatement stmt = con.prepareStatement( sb.toString() ); 583 if ( id instanceof String ) { 584 LOG.logDebug( "Setting to id '" + id + "'an instance of String" ); 585 stmt.setString( 1, (String) id ); 586 } else if ( id instanceof Integer ) { 587 LOG.logDebug( "Setting to id '" + id + "'an instance of integer" ); 588 stmt.setInt( 1, ( (Integer) id ).intValue() ); 589 } else { 590 LOG.logWarning( "The type of id is uncertain (neiter String nor Integer), adding it as an 'object' to the database." ); 591 stmt.setObject( 1, id ); 592 } 593 594 for ( int i = 0; i < ftp.length; i++ ) { 595 Object o = null; 596 if ( feature.getProperties( ftp[i].getName() ) != null ) { 597 if ( feature.getProperties( ftp[i].getName() ).length > 0 ) { 598 o = feature.getProperties( ftp[i].getName() )[0].getValue(); 599 } 600 } 601 if ( o == null ) { 602 stmt.setNull( i + 2, ftp[i].getType() ); 603 } else { 604 switch ( ftp[i].getType() ) { 605 case Types.CHAR: 606 case Types.VARCHAR: 607 stmt.setString( i + 2, o.toString() ); 608 break; 609 case Types.SMALLINT: 610 case Types.TINYINT: 611 case Types.INTEGER: 612 case Types.BIGINT: 613 stmt.setInt( i + 2, (int) Double.parseDouble( o.toString() ) ); 614 break; 615 case Types.DOUBLE: 616 case Types.FLOAT: 617 case Types.DECIMAL: 618 case Types.NUMERIC: 619 stmt.setFloat( i + 2, Float.parseFloat( o.toString() ) ); 620 break; 621 case Types.DATE: 622 case Types.TIME: 623 case Types.TIMESTAMP: 624 stmt.setDate( i + 2, (Date) o ); 625 break; 626 case Types.GEOMETRY: { 627 StringBuffer gs = GMLGeometryAdapter.export( (Geometry) o ); 628 String s = StringTools.replace( gs.toString(), 629 ">", 630 " xmlns:gml=\"http://www.opengis.net/gml\">", 631 false ); 632 if ( backend.equals( "POSTGRES" ) || backend.equals( "HSQLDB" ) ) { 633 LOG.logDebug( "Adding geometry: " + s ); 634 stmt.setString( i + 2, s ); 635 } else if ( backend.equals( "INGRES" ) ) { 636 stmt.setObject( i + 2, new StringReader( s ) ); 637 } else { 638 stmt.setObject( i + 2, s.getBytes() ); 639 } 640 break; 641 } 642 default: { 643 LOG.logWarning( "unsupported type: " + ftp[i].getType() ); 644 } 645 } 646 } 647 } 648 LOG.logDebug( "SQL statement for insert feature: " + sb ); 649 if ( !stmt.execute() ) { 650 LOG.logError( "The insertion of the feature resulted in " + stmt.getUpdateCount() + " updates." ); 651 } 652 653 stmt.close(); 654 } catch ( SQLException e ) { 655 String msg = "Could not insert feature with id='" + id + "' into the database because: " + e.getMessage(); 656 LOG.logError( msg, e ); 657 throw new IndexException( msg, e ); 658 } catch ( DBPoolException e ) { 659 String msg = "Could not acquire a connection to the database to insert the feature with id: " + id; 660 LOG.logError( msg, e ); 661 throw new IndexException( msg, e ); 662 } catch ( GeometryException e ) { 663 String msg = "Could not insert feature with id='" + id + "' into the database because: " + e.getMessage(); 664 LOG.logError( msg, e ); 665 throw new IndexException( msg, e ); 666 } finally { 667 try { 668 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 669 } catch ( DBPoolException e ) { 670 LOG.logError( "Could not release JDBC connection because: " + e.getMessage() ); 671 } 672 } 673 } 674 675 /** 676 * initializes the root node of the quadtree 677 * 678 * @param fileName 679 * @throws IndexException 680 * @throws IOException 681 * 682 */ 683 protected void initRootNode( String fileName ) 684 throws IndexException, IOException { 685 LOG.logDebug( "Trying to read shapefile from file: " + fileName ); 686 ShapeFile sf = new ShapeFile( fileName ); 687 if ( envelope == null ) { 688 envelope = sf.getFileMBR(); 689 } 690 envelope = envelope.getBuffer( envelope.getWidth() / 20 ); 691 LOG.logInfo( "Bounding box of the root feature: " + envelope ); 692 sf.close(); 693 // DBQuadtree<T> qtTmp = loadQuadtree(); 694 Connection con = null; 695 DBConnectionPool pool = null; 696 try { 697 pool = DBConnectionPool.getInstance(); 698 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 699 700 StringBuilder sb = new StringBuilder( 100 ); 701 sb.append( "INSERT INTO " ).append( indexName ); 702 sb.append( " ( ID, MINX, MINY, MAXX , MAXY ) " ); 703 sb.append( "VALUES ( ?, ?, ?, ?, ? ) " ); 704 PreparedStatement stmt = con.prepareStatement( sb.toString() ); 705 stmt.setString( 1, "1" ); 706 stmt.setFloat( 2, (float) envelope.getMin().getX() ); 707 stmt.setFloat( 3, (float) envelope.getMin().getY() ); 708 stmt.setFloat( 4, (float) envelope.getMax().getX() ); 709 stmt.setFloat( 5, (float) envelope.getMax().getY() ); 710 stmt.execute(); 711 stmt.close(); 712 } catch ( Exception e ) { 713 LOG.logError( e.getMessage(), e ); 714 throw new IndexException( "could not create root node definition at database", e ); 715 } finally { 716 try { 717 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 718 } catch ( Exception e1 ) { 719 e1.printStackTrace(); 720 } 721 } 722 } 723 724 /** 725 * before importing a shape a user may set an envelope for the quadtree to bee created that is different from the 726 * one of the shape by calling this method. Notice: calling this method does not have any effect when calling 727 * 728 * @see #appendShape(String) method. 729 * @param envelope 730 */ 731 public void setRootEnvelope( Envelope envelope ) { 732 this.envelope = envelope; 733 } 734 735 /** 736 * initializes a new Quadtree by adding a row into table TAB_QUADTREE and into TAB_QTNODE (-> root node) 737 * 738 * @param fileName 739 * 740 * @return the id of the inserted node 741 * @throws IndexException 742 * @throws IOException 743 * if the shape file could not be read. 744 */ 745 protected int initQuadtree( String fileName ) 746 throws IndexException, IOException { 747 748 initRootNode( fileName ); 749 Connection con = null; 750 DBConnectionPool pool = null; 751 int id = -1; 752 try { 753 pool = DBConnectionPool.getInstance(); 754 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 755 756 // first check if the version column exists; 757 StringBuilder versionCheck = new StringBuilder( "Select * from TAB_QUADTREE;" ); 758 Statement stmt = con.createStatement(); 759 ResultSet rs = stmt.executeQuery( versionCheck.toString() ); 760 boolean hasVersion = false; 761 try { 762 ResultSetMetaData md = rs.getMetaData(); 763 int numberOfColumns = md.getColumnCount(); 764 765 for ( int i = 1; i <= numberOfColumns && !hasVersion; i++ ) { 766 String tmp = md.getColumnName( i ); 767 if ( tmp != null ) { 768 if ( "version".equalsIgnoreCase( tmp.trim() ) ) { 769 hasVersion = true; 770 } 771 } 772 } 773 if ( !hasVersion ) { 774 LOG.logInfo( "Found no Version Column in the TAB_QUADTREE table, assuming version 2.0.0, and adding the version column." ); 775 rs.close(); 776 stmt.close(); 777 stmt = con.createStatement(); 778 rs = stmt.executeQuery( "ALTER TABLE TAB_QUADTREE ADD version VARCHAR(15)" ); 779 rs.close(); 780 stmt.close(); 781 } 782 } catch ( SQLException e ) { 783 LOG.logError( "An error occurred while trying to determine if the database supports versioning: " + e.getMessage() ); 784 } 785 786 StringBuilder sb = new StringBuilder( 100 ); 787 sb.append( "INSERT INTO TAB_QUADTREE (" ); 788 if ( backend.equals( "INGRES" ) || backend.equals( "HSQLDB" ) ) { 789 sb.append( "ID, " ); 790 } 791 sb.append( "FK_ROOT, DEPTH, VERSION ) VALUES ( " ); 792 if ( backend.equals( "INGRES" ) || backend.equals( "HSQLDB" ) ) { 793 stmt = con.createStatement(); 794 rs = stmt.executeQuery( "SELECT MAX(ID) FROM TAB_QUADTREE" ); 795 rs.next(); 796 int myid = rs.getInt( 1 ) + 1; 797 sb.append( myid + ", " ); 798 } 799 sb.append( " '1', ?, '2.0.0' ) " ); 800 801 PreparedStatement pstmt = con.prepareStatement( sb.toString() ); 802 pstmt.setInt( 1, maxDepth ); 803 pstmt.execute(); 804 pstmt.close(); 805 stmt = con.createStatement(); 806 rs = stmt.executeQuery( "select max(ID) from TAB_QUADTREE" ); 807 rs.next(); 808 id = rs.getInt( 1 ); 809 if ( id < 0 ) { 810 throw new IndexException( "could not read ID of quadtree from database." ); 811 } 812 } catch ( SQLException e ) { 813 throw new IndexException( "Could not load quadtree definition from database (did you run the base/scripts/index/quadtree.hsql script, which create the meta-info tables?). The error message was: " + e.getMessage() ); 814 } catch ( DBPoolException e ) { 815 throw new IndexException( "Could not acquire a connection to the database to initiate the quattree index structure because: " + e.getMessage() ); 816 } finally { 817 try { 818 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 819 } catch ( DBPoolException e ) { 820 LOG.logError( "Could not release JDBC connection because: " + e.getMessage() ); 821 } 822 } 823 return id; 824 } 825 826 /** 827 * Inserts a row into the quadtree meta data structure 'TAB_DEEGREE_IDX', containing information on the table, 828 * geometry, indexname, owner and the foreign_key to the index table. 829 * 830 * @param fk_indexTree 831 * @throws IndexException 832 */ 833 public void insertIndexMetadata( int fk_indexTree ) 834 throws IndexException { 835 836 Connection con = null; 837 DBConnectionPool pool = null; 838 try { 839 pool = DBConnectionPool.getInstance(); 840 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 841 842 StringBuilder sb = new StringBuilder( 100 ); 843 sb.append( "INSERT INTO TAB_DEEGREE_IDX ( " ); 844 if ( backend.equals( "INGRES" ) || backend.equals( "HSQLDB" ) ) { 845 sb.append( "ID, " ); 846 } 847 sb.append( "column_name, table_name, " ); 848 sb.append( "owner, INDEX_NAME, FK_indexTree ) " ); 849 sb.append( "VALUES ( " ); 850 if ( backend.equals( "INGRES" ) || backend.equals( "HSQLDB" ) ) { 851 Statement stm = con.createStatement(); 852 ResultSet rs = stm.executeQuery( "SELECT MAX(ID) FROM TAB_QUADTREE" ); 853 rs.next(); 854 int myid = rs.getInt( 1 ) + 1; 855 sb.append( myid + ", " ); 856 } 857 sb.append( "?, ?, ?, ?, ? ) " ); 858 PreparedStatement stmt = con.prepareStatement( sb.toString() ); 859 stmt.setString( 1, column ); 860 stmt.setString( 2, table ); 861 stmt.setString( 3, owner ); 862 stmt.setString( 4, indexName ); 863 stmt.setInt( 5, fk_indexTree ); 864 865 stmt.execute(); 866 stmt.close(); 867 } catch ( SQLException e ) { 868 throw new IndexException( "Could not insert a new row into the quadtree index metadata table (did you run the base/scripts/index/quadtree.hsql script, which creates the meta-info tables?). The error message was: " + e.getMessage() ); 869 } catch ( DBPoolException e ) { 870 throw new IndexException( "Could not acquire a connection to the database to store the quattree index metadata structure because: " + e.getMessage() ); 871 } finally { 872 try { 873 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 874 } catch ( DBPoolException e ) { 875 LOG.logError( "Could not release JDBC connection because: " + e.getMessage() ); 876 } 877 } 878 } 879 880 /** 881 * creates table the shape data shall be stored 882 * 883 * @param fileName 884 * @param idType 885 * the type of the feature_id column, for example VARCHAR(50) or NUMBER. 886 * @throws IndexException 887 * @throws IOException 888 */ 889 protected void createDataTable( String fileName, String idType ) 890 throws IndexException, IOException { 891 ShapeFile sf = new ShapeFile( fileName ); 892 FeatureType ft = null; 893 try { 894 ft = sf.getFeatureByRecNo( 1 ).getFeatureType(); 895 } catch ( HasNoDBaseFileException e ) { 896 throw new IndexException( e ); 897 } catch ( DBaseException e ) { 898 throw new IndexException( e ); 899 } 900 sf.close(); 901 StringBuilder sb = new StringBuilder( 1000 ); 902 sb.append( "CREATE TABLE " ).append( table ).append( '(' ); 903 904 sb.append( "FEATURE_ID " ).append( idType ).append( "," ); 905 PropertyType[] ftp = ft.getProperties(); 906 for ( int i = 0; i < ftp.length; i++ ) { 907 if ( ftp[i].getType() == Types.GEOMETRY ) { 908 sb.append( column ).append( ' ' ); 909 } else { 910 sb.append( ftp[i].getName().getLocalName() ).append( ' ' ); 911 } 912 sb.append( getDatabaseType( ftp[i].getType() ) ); 913 if ( i < ftp.length - 1 ) { 914 sb.append( ", " ); 915 } 916 } 917 sb.append( ')' ); 918 919 Connection con = null; 920 DBConnectionPool pool = null; 921 try { 922 pool = DBConnectionPool.getInstance(); 923 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 924 925 Statement stmt = con.createStatement(); 926 LOG.logDebug( sb.toString() ); 927 stmt.execute( sb.toString() ); 928 stmt.close(); 929 } catch ( SQLException e ) { 930 throw new IndexException( "Could not create a DataTable: '" + table 931 + "' (which will hold the features from the shapefile: '" 932 + fileName 933 + "'). The error message was: " 934 + e.getMessage(), e ); 935 } catch ( DBPoolException e ) { 936 throw new IndexException( "Could not acquire a connection to the database to create a DataTable because: " + e.getMessage(), 937 e ); 938 } finally { 939 try { 940 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 941 } catch ( DBPoolException e ) { 942 LOG.logError( "Could not release JDBC connection because: " + e.getMessage() ); 943 } 944 } 945 } 946 947 /** 948 * returns the type name for a generic type code as used by SQLServer 949 * 950 * @param dataTypeCode 951 * @return the type name for a generic type code as used by SQLServer 952 * @throws IndexException 953 */ 954 String getDatabaseType( int dataTypeCode ) { 955 String type = null; 956 957 switch ( dataTypeCode ) { 958 case Types.CHAR: 959 case Types.VARCHAR: 960 type = DBQuadtreeDataTypes.getString( backend + ".string" ); 961 break; 962 case Types.SMALLINT: 963 case Types.TINYINT: 964 case Types.INTEGER: 965 case Types.BIGINT: 966 type = DBQuadtreeDataTypes.getString( backend + ".integer" ); 967 break; 968 case Types.DOUBLE: 969 case Types.FLOAT: 970 case Types.DECIMAL: 971 case Types.NUMERIC: 972 type = DBQuadtreeDataTypes.getString( backend + ".float" ); 973 break; 974 case Types.DATE: 975 case Types.TIME: 976 case Types.TIMESTAMP: 977 type = DBQuadtreeDataTypes.getString( backend + ".datetime" ); 978 break; 979 case Types.GEOMETRY: 980 type = DBQuadtreeDataTypes.getString( backend + ".geometry" ); 981 break; 982 default: 983 throw new InvalidParameterException( "Unknown data type code: " + dataTypeCode ); 984 } 985 986 return type; 987 } 988 989 /** 990 * imports a shape into the database and builds a quadtree on it 991 * 992 * @param fileName 993 * of the shapefile. 994 * @throws IOException 995 * if the shapefile could not be opened. 996 * @throws IndexException 997 * if an error occurred while talking to the jdbc database. 998 * @throws DBaseException 999 * if the connection to the shapefile could not be opened. 1000 * @throws HasNoDBaseFileException 1001 * if the feature could not be read from shape file's database file. 1002 */ 1003 public void importShape( String fileName ) 1004 throws IOException, IndexException, HasNoDBaseFileException, 1005 DBaseException { 1006 if ( TYPE == Integer.MIN_VALUE ) { 1007 LOG.logInfo( "You supplied an unknown type to the DBQuadtreeManager, therefore assuming you meant the Types.VARCHAR type" ); 1008 TYPE = Types.VARCHAR; 1009 } 1010 StringBuilder typeName = new StringBuilder( 64 ); 1011 1012 typeName.append( getDatabaseType( TYPE ) ); 1013 1014 createDataTable( fileName, typeName.toString() ); 1015 1016 int qtid = initQuadtree( fileName ); 1017 1018 insertIndexMetadata( qtid ); 1019 1020 qt = new DBQuadtree<T>( qtid, indexName, jdbc ); 1021 1022 ShapeFile sf = new ShapeFile( fileName ); 1023 1024 double step = 100.0 / sf.getRecordNum(); 1025 double counter = 0; 1026 Envelope sfEnv = sf.getFileMBR(); 1027 1028 LOG.logDebug( "The shape file read " + sf.getRecordNum() + " number of records" ); 1029 for ( int i = 0; i < sf.getRecordNum(); i++ ) { 1030 Feature feat = sf.getFeatureByRecNo( i + 1 ); 1031 if ( counter < step * i ) { 1032 if ( step < 1 ) { 1033 counter += 10; 1034 } else { 1035 counter += step; 1036 } 1037 System.out.println( counter + "%" ); 1038 } 1039 if ( i % 200 == 0 ) { 1040 System.gc(); 1041 } 1042 Envelope env = feat.getDefaultGeometryPropertyValue().getEnvelope(); 1043 LOG.logDebug( i + " --- " + env ); 1044 if ( env == null ) { 1045 // must be a point geometry 1046 Point point = (Point) feat.getDefaultGeometryPropertyValue(); 1047 double w = sfEnv.getWidth() / 1000; 1048 double h = sfEnv.getHeight() / 1000; 1049 env = GeometryFactory.createEnvelope( point.getX() - w / 2d, 1050 point.getY() - h / 2d, 1051 point.getX() + w / 2d, 1052 point.getY() + h / 2d, 1053 null ); 1054 } 1055 // map to the requested featuretype id's type 1056 T id = getMappedID( i ); 1057 LOG.logDebug( "Inserting item : " + i ); 1058 qt.insert( id, env ); 1059 storeFeature( feat, id, jdbc ); 1060 } 1061 1062 if ( "HSQLDB".equals( backend ) ) { 1063 LOG.logInfo( "Because you are using an hsql database, the current thread will wait '10' seconds, this gives the inmemory database time to flush it's tables" ); 1064 try { 1065 Thread.sleep( 10000 ); 1066 } catch ( InterruptedException e ) { 1067 LOG.logError( "Exception occurred while waitig for the db-manager to flush it's memory tables. Message: " + e.getMessage(), 1068 e ); 1069 } 1070 } 1071 sf.close(); 1072 LOG.logInfo( "finished!" ); 1073 } 1074 1075 @SuppressWarnings("unchecked") 1076 private T getMappedID( int i ) { 1077 if ( TYPE == Types.VARCHAR ) { 1078 return (T) UUID.randomUUID().toString(); 1079 } else if ( TYPE == Types.INTEGER ) { 1080 return (T) new Integer( i ); 1081 } 1082 return null; 1083 1084 } 1085 1086 /** 1087 * appends the features of a shape to an existing datatable and inserts references into the assigned quadtree table. 1088 * <p> 1089 * you have to consider that the quadtree is just valid for a defined area. if the features to append exceeds this 1090 * area the quadtree has to be rebuilded. 1091 * </p> 1092 * 1093 * @param fileName 1094 * @throws IOException 1095 * if the shape file cannot be read. 1096 * @throws IndexException 1097 * if the quatree could not be read. 1098 */ 1099 public void appendShape( String fileName ) 1100 throws IOException, IndexException { 1101 1102 ShapeFile sf = new ShapeFile( fileName ); 1103 1104 int b = sf.getRecordNum() / 100; 1105 if ( b == 0 ) 1106 b = 1; 1107 int k = 0; 1108 qt = getQuadtree(); 1109 Envelope sfEnv = sf.getFileMBR(); 1110 int cnt = getMaxIdValue(); 1111 1112 for ( int i = 0; i < sf.getRecordNum(); i++ ) { 1113 Feature feat = null; 1114 try { 1115 feat = sf.getFeatureByRecNo( i + 1 ); 1116 } catch ( HasNoDBaseFileException e ) { 1117 throw new IndexException( e ); 1118 } catch ( DBaseException e ) { 1119 throw new IndexException( e ); 1120 } 1121 if ( i % b == 0 ) { 1122 System.out.println( k + "%" ); 1123 k++; 1124 } 1125 if ( i % 200 == 0 ) { 1126 System.gc(); 1127 } 1128 Envelope env = feat.getDefaultGeometryPropertyValue().getEnvelope(); 1129 if ( env == null ) { 1130 // must be a point geometry 1131 Point point = (Point) feat.getDefaultGeometryPropertyValue(); 1132 double w = sfEnv.getWidth() / 1000; 1133 double h = sfEnv.getHeight() / 1000; 1134 env = GeometryFactory.createEnvelope( point.getX() - w / 2d, 1135 point.getY() - h / 2d, 1136 point.getX() + w / 2d, 1137 point.getY() + h / 2d, 1138 null ); 1139 } 1140 // map to the requested featuretype id's type 1141 T id = getMappedID( cnt + i + 1 ); 1142 qt.insert( id, env ); 1143 storeFeature( feat, id, jdbc ); 1144 } 1145 LOG.logInfo( " finished!" ); 1146 sf.close(); 1147 } 1148 1149 /** 1150 * returns the maximum ID of the data table 1151 * 1152 * @return the maximum ID of the data table 1153 * @throws IndexException 1154 */ 1155 private int getMaxIdValue() 1156 throws IndexException { 1157 if ( TYPE != Types.INTEGER ) { 1158 return 0; 1159 } 1160 String sql = "SELECT MAX( FEATURE_ID ) FROM " + table; 1161 1162 Connection con = null; 1163 DBConnectionPool pool = null; 1164 Statement stmt = null; 1165 int maxId = 0; 1166 try { 1167 pool = DBConnectionPool.getInstance(); 1168 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 1169 1170 stmt = con.createStatement(); 1171 LOG.logDebug( sql ); 1172 ResultSet rs = stmt.executeQuery( sql ); 1173 if ( rs.next() ) { 1174 maxId = rs.getInt( 1 ); 1175 } 1176 } catch ( SQLException e ) { 1177 throw new IndexException( "Error while executing the sql statement while finding the max( Faeture_Id ) from table: " + table, 1178 e ); 1179 } catch ( DBPoolException e ) { 1180 throw new IndexException( "Could not acquire a jdbc connection to read the max( Faeture_Id ) from table: " + table, 1181 e ); 1182 } finally { 1183 try { 1184 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 1185 } catch ( DBPoolException e ) { 1186 LOG.logError( "Could not release JDBC connection because: " + e.getMessage() ); 1187 } 1188 } 1189 1190 return maxId; 1191 } 1192 1193 /** 1194 * Creates actually two tables, an indextable, which will hold the actual quadtree and an index_item table which is 1195 * a join-table between the dbNodes and the feature_ids. 1196 * 1197 * @param indexTable 1198 * name of the index table. 1199 * @param idType 1200 * the type of the feature_id column, for example VARCHAR(50) or NUMBER. 1201 * @throws IndexException 1202 * if the table could not be created. 1203 */ 1204 protected void createIndexTable( String indexTable, String idType ) 1205 throws IndexException { 1206 StringBuilder sb = new StringBuilder( 2000 ); 1207 String qtDataType = getDatabaseType( Types.VARCHAR ); 1208 sb.append( "CREATE TABLE " ).append( indexTable ).append( " ( " ); 1209 sb.append( "ID " ).append( qtDataType ).append( " NOT NULL," ); 1210 sb.append( "minx float NOT NULL," ); 1211 sb.append( "miny float NOT NULL," ); 1212 sb.append( "maxx float NOT NULL," ); 1213 sb.append( "maxy float NOT NULL," ); 1214 sb.append( "FK_SUBNODE1 " ).append( qtDataType ); 1215 sb.append( ", FK_SUBNODE2 " ).append( qtDataType ); 1216 sb.append( ", FK_SUBNODE3 " ).append( qtDataType ); 1217 sb.append( ", FK_SUBNODE4 " ).append( qtDataType ).append( ")" ); 1218 1219 StringBuilder sb2 = new StringBuilder( 1000 ); 1220 sb2.append( "CREATE TABLE " ).append( indexName ).append( "_ITEM ( " ); 1221 sb2.append( "FK_QTNODE " ).append( qtDataType ).append( " NOT NULL," ); 1222 sb2.append( "FK_ITEM " ).append( idType ).append( " NOT NULL )" ); 1223 1224 Connection con = null; 1225 DBConnectionPool pool = null; 1226 try { 1227 pool = DBConnectionPool.getInstance(); 1228 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 1229 1230 Statement stmt = con.createStatement(); 1231 stmt.execute( sb.toString() ); 1232 stmt.close(); 1233 1234 stmt = con.createStatement(); 1235 stmt.execute( sb2.toString() ); 1236 stmt.close(); 1237 } catch ( SQLException e ) { 1238 throw new IndexException( "Could not create the indextable: '" + indexTable 1239 + "' and/or the index_item table: '" 1240 + indexTable 1241 + "_ITEM'. The error message was: " 1242 + e.getMessage(), e ); 1243 } catch ( DBPoolException e ) { 1244 throw new IndexException( "Could not acquire a connection to the database to store create the necessary tables: " + e.getMessage(), 1245 e ); 1246 } finally { 1247 try { 1248 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 1249 } catch ( DBPoolException e ) { 1250 LOG.logError( "Could not release JDBC connection because: " + e.getMessage() ); 1251 } 1252 } 1253 } 1254 1255 /** 1256 * Executes a simple select from indextable, and returns true if no SQL exception occurred. 1257 * @return true if a select * from indextable resulted in no exceptions, false otherwise. 1258 */ 1259 private boolean hasIndexTable() { 1260 DBConnectionPool pool = DBConnectionPool.getInstance(); 1261 Connection con = null; 1262 try { 1263 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 1264 } catch ( DBPoolException e ) { 1265 LOG.logError( "Could not aqcuire connection to the database backend because: " + e.getMessage(), e ); 1266 return false; 1267 } finally { 1268 try { 1269 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 1270 } catch ( Exception e1 ) { 1271 LOG.logError( "Could not release the jdbc connection because: " + e1.getMessage() ); 1272 } 1273 } 1274 1275 try { 1276 Statement stmt = con.createStatement(); 1277 stmt.execute( "SELECT * from " + indexName ); 1278 } catch ( SQLException e ) { 1279 return false; 1280 } 1281 1282 return true; 1283 } 1284 1285 1286 }