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 &lt; 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 &lt; 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    }