001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/csw/manager/HarvestRepository.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     ---------------------------------------------------------------------------*/
044    package org.deegree.ogcwebservices.csw.manager;
045    
046    import java.io.IOException;
047    import java.io.InputStream;
048    import java.net.URI;
049    import java.net.URISyntaxException;
050    import java.net.URL;
051    import java.sql.Connection;
052    import java.sql.PreparedStatement;
053    import java.sql.ResultSet;
054    import java.sql.SQLException;
055    import java.sql.Statement;
056    import java.sql.Timestamp;
057    import java.util.ArrayList;
058    import java.util.Date;
059    import java.util.Iterator;
060    import java.util.List;
061    import java.util.Properties;
062    
063    import org.deegree.datatypes.time.TimeDuration;
064    import org.deegree.framework.log.ILogger;
065    import org.deegree.framework.log.LoggerFactory;
066    import org.deegree.io.DBConnectionPool;
067    import org.deegree.io.DBPoolException;
068    import org.deegree.io.JDBCConnection;
069    
070    /**
071     * A harvest repository is a database that stores harvest requests and that caches basic record
072     * informations to optimizes harvesting of large sources (e.g. other catalogues). This class
073     * encapsulates access to this database.
074     * 
075     * 
076     * @version $Revision: 9345 $
077     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
078     * @author last edited by: $Author: apoth $
079     * 
080     * @version $Revision: 9345 $, $Date: 2007-12-27 17:22:25 +0100 (Do, 27 Dez 2007) $
081     */
082    class HarvestRepository {
083    
084        private static final ILogger LOG = LoggerFactory.getLogger( HarvestRepository.class );
085    
086        private static final URL url = HarvestRepository.class.getResource( "harvestrepository.properties" );
087    
088        private static HarvestRepository repository = null;
089    
090        private static DBConnectionPool pool = DBConnectionPool.getInstance();
091    
092        private JDBCConnection jdbc = null;
093    
094        private Properties prop = null;
095    
096        // possible metadata source types
097        static enum ResourceType {
098            catalogue, service, csw_profile, FGDC, dublincore, unknown
099        };
100    
101        /**
102         * returns an instance of a <code>HarvestRepository</code>
103         * 
104         * @return an instance of a <code>HarvestRepository</code>
105         * @throws IOException
106         */
107        static HarvestRepository getInstance()
108                                throws IOException {
109            if ( repository == null ) {
110                repository = new HarvestRepository();
111            }
112            return repository;
113        }
114    
115        /**
116         * 
117         * @param jdbc
118         */
119        private HarvestRepository() throws IOException {
120            prop = new Properties();
121            InputStream is = url.openStream();
122            prop.load( is );
123            is.close();
124            jdbc = new JDBCConnection( prop.getProperty( "harvester.Driver" ),
125                                       prop.getProperty( "harvester.Url" ),
126                                       prop.getProperty( "harvester.User" ),
127                                       prop.getProperty( "harvester.Password" ), null, null, null );
128        }
129    
130        /**
131         * stores a harvest request
132         * 
133         * @param request
134         * @throws DBPoolException
135         * @throws SQLException
136         */
137        synchronized void storeRequest( Harvest request )
138                                throws DBPoolException, SQLException {
139    
140            LOG.logDebug( "storing harvest request into harvest repository ..." );
141    
142            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
143                                                     jdbc.getPassword() );
144            try {
145                con.setAutoCommit( false );
146            } catch ( Exception ignore ) {
147            }
148    
149            try {
150                // insert into harvestsource table
151                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.storeRequest1" ) );
152                ps.setString( 1, request.getSource().toASCIIString() );
153                TimeDuration td = request.getHarvestInterval();
154                if ( td != null ) {
155                    ps.setLong( 2, td.getAsMilliSeconds() / 1000 );
156                } else {
157                    ps.setLong( 2, -1 );
158                }
159                ps.setTimestamp( 3, new Timestamp( request.getStartTimestamp().getTime() ) );
160                ps.setBoolean( 4, false );
161                if ( request.getResourceType() == null ) {
162                    ps.setString( 5, "unknown" );
163                } else {
164                    ps.setString( 5, request.getResourceType().toASCIIString() );
165                }
166                ps.execute();
167                ps.close();
168                Statement stmt = con.createStatement();
169                ResultSet rs = stmt.executeQuery( "select max(id) from harvestsource" );
170                rs.next();
171                int id1 = rs.getInt( 1 );
172                rs.close();
173                stmt.close();
174    
175                // insert into responsehandler table and assigns to harvestsource by
176                // performing an insert into jt_source_responsehandler
177                List<URI> list = request.getResponseHandler();
178                for ( Iterator iter = list.iterator(); iter.hasNext(); ) {
179                    URI handler = (URI) iter.next();
180                    ps = con.prepareStatement( prop.getProperty( "harvester.storeRequest2" ) );
181                    ps.setString( 1, handler.toASCIIString() );
182                    ps.setBoolean( 2, handler.toASCIIString().toLowerCase().startsWith( "mailto:" ) );
183                    ps.execute();
184                    ps.close();
185    
186                    stmt = con.createStatement();
187                    rs = stmt.executeQuery( "select max(id) from responsehandler" );
188                    rs.next();
189                    int id2 = rs.getInt( 1 );
190                    rs.close();
191                    stmt.close();
192    
193                    ps = con.prepareStatement( prop.getProperty( "harvester.storeRequest3" ) );
194                    ps.setInt( 1, id1 );
195                    ps.setInt( 2, id2 );
196                    ps.execute();
197                    ps.close();
198                }
199    
200                con.commit();
201            } catch ( SQLException e ) {
202                con.rollback();
203                e.printStackTrace();
204                throw new SQLException( getClass().getName() + " storeRequest(..) " + e.getMessage() );
205            } catch ( Exception e ) {
206                con.rollback();
207                e.printStackTrace();
208                throw new SQLException( getClass().getName() + " storeRequest(..) "
209                                        + "could not insert harvest request into repository: "
210                                        + e.getMessage() );
211            } finally {
212                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
213                                        jdbc.getPassword() );
214            }
215        }
216    
217        /**
218         * drops a request from the backend
219         * 
220         * @param source
221         * @throws DBPoolException
222         * @throws SQLException
223         */
224        synchronized void dropRequest( URI source )
225                                throws DBPoolException, SQLException {
226    
227            LOG.logDebug( "dropping harvest request from harvest repository ..." );
228    
229            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
230                                                     jdbc.getPassword() );
231            try {
232                con.setAutoCommit( false );
233            } catch ( Exception ignore ) {
234            }
235    
236            try {
237                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.dropRequest1" ) );
238                ps.setString( 1, source.toASCIIString() );
239                ResultSet rs = ps.executeQuery();
240                rs.next();
241                int id1 = rs.getInt( 1 );
242                rs.close();
243                ps = con.prepareStatement( prop.getProperty( "harvester.dropRequest2" ) );
244                ps.setInt( 1, id1 );
245                rs = ps.executeQuery();
246                List<Integer> handlers = new ArrayList<Integer>();
247                while ( rs.next() ) {
248                    handlers.add( rs.getInt( 1 ) );
249                }
250                rs.close();
251                ps.close();
252                // remove assigned entries from jointable
253                ps = con.prepareStatement( prop.getProperty( "harvester.dropRequest3" ) );
254                ps.setInt( 1, id1 );
255                ps.execute();
256                // remove assigend entries from reponse handler table
257                for ( int i = 0; i < handlers.size(); i++ ) {
258                    Integer id = handlers.get( i );
259                    ps = con.prepareStatement( prop.getProperty( "harvester.dropRequest4" ) );
260                    ps.setInt( 1, id.intValue() );
261                    ps.execute();
262                    ps.close();
263                }
264                // remove records from cache table
265                ps = con.prepareStatement( prop.getProperty( "harvester.dropRequest5" ) );
266                ps.setInt( 1, id1 );
267                ps.execute();
268                ps.close();
269    
270                // remove root from harvest source table
271                ps = con.prepareStatement( prop.getProperty( "harvester.dropRequest6" ) );
272                ps.setInt( 1, id1 );
273                ps.execute();
274                ps.close();
275    
276                con.commit();
277    
278            } catch ( SQLException e ) {
279                con.rollback();
280                throw e;
281            } catch ( Exception e ) {
282                con.rollback();
283                throw new SQLException( "could not frop request from repository: " + e.getMessage() );
284            } finally {
285                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
286                                        jdbc.getPassword() );
287            }
288    
289        }
290    
291        /**
292         * returns all sources registered to a harvest process
293         * 
294         * @return all sources registered to a harvest process
295         * @throws DBPoolException
296         * @throws SQLException
297         * @throws URISyntaxException
298         */
299        synchronized List<URI> getSources()
300                                throws DBPoolException, SQLException, URISyntaxException {
301    
302            LOG.logDebug( "reading sources from harvest repository ..." );
303    
304            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
305                                                     jdbc.getPassword() );
306            List<URI> sources = new ArrayList<URI>();
307            try {
308                Statement stmt = con.createStatement();
309                ResultSet rs = stmt.executeQuery( prop.getProperty( "harvester.getSources" ) );
310                while ( rs.next() ) {
311                    sources.add( new URI( rs.getString( 1 ) ) );
312                }
313                rs.close();
314                stmt.close();
315            } catch ( SQLException e ) {
316                throw e;
317            } catch ( URISyntaxException e ) {
318                throw e;
319            } finally {
320                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
321                                        jdbc.getPassword() );
322            }
323    
324            return sources;
325        }
326    
327        /**
328         * returns the type of the passed source
329         * 
330         * @param source
331         * @return the type of the passed source
332         * @throws DBPoolException
333         * @throws SQLException
334         */
335        synchronized ResourceType getSourceType( URI source )
336                                throws DBPoolException, SQLException {
337    
338            LOG.logDebug( "reading sources type for source: " + source );
339    
340            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
341                                                     jdbc.getPassword() );
342            String s = null;
343    
344            try {
345                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.getSourceType" ) );
346                ps.setString( 1, source.toASCIIString() );
347                ResultSet rs = ps.executeQuery();
348                rs.next();
349                s = rs.getString( 1 );
350                rs.close();
351                ps.close();
352            } catch ( SQLException e ) {
353                throw e;
354            } finally {
355                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
356                                        jdbc.getPassword() );
357            }
358    
359            ResourceType st = ResourceType.unknown;
360    
361            if ( "csw:profile".equals( s ) ) {
362                st = ResourceType.csw_profile;
363            } else if ( "dublincore".equals( s ) ) {
364                st = ResourceType.dublincore;
365            } else if ( "FGDC".equals( s ) ) {
366                st = ResourceType.FGDC;
367            } else if ( "service".equals( s ) ) {
368                st = ResourceType.service;
369            } else if ( "catalogue".equals( s ) ) {
370                st = ResourceType.catalogue;
371            }
372    
373            return st;
374        }
375    
376        /**
377         * returns true if last harvesting iteration for the passed source has been successful
378         * 
379         * @param source
380         * 
381         * @return <code>true</code> if last harvesting iteration for the passed source has been
382         *         successful
383         * @throws DBPoolException
384         * @throws SQLException
385         */
386        synchronized boolean getStatus( URI source )
387                                throws DBPoolException, SQLException {
388    
389            LOG.logDebug( "reading sources status for source: " + source );
390    
391            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
392                                                     jdbc.getPassword() );
393            boolean status = false;
394    
395            try {
396                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.getStatus" ) );
397                ps.setString( 1, source.toASCIIString() );
398                ResultSet rs = ps.executeQuery();
399                rs.next();
400                status = rs.getBoolean( 1 );
401                rs.close();
402                ps.close();
403            } catch ( SQLException e ) {
404                throw e;
405            } finally {
406                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
407                                        jdbc.getPassword() );
408            }
409    
410            return status;
411        }
412    
413        /**
414         * returns the <code>Date</code> a source has been harvested successful the last time
415         * 
416         * @param source
417         * @return the <code>Date</code> a source has been harvested successful the last time
418         * @throws DBPoolException
419         * @throws SQLException
420         */
421        synchronized Date getLastHarvestingTimestamp( URI source )
422                                throws DBPoolException, SQLException {
423    
424            LOG.logDebug( "reading sources last harvesting timestamp for source: " + source );
425    
426            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
427                                                     jdbc.getPassword() );
428            Date date = null;
429            try {
430                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.getLastHarvestingTimestamp" ) );
431                ps.setString( 1, source.toASCIIString() );
432                ResultSet rs = ps.executeQuery();
433                rs.next();
434                Timestamp ts = rs.getTimestamp( 1 );
435                rs.close();
436                ps.close();
437                if ( ts != null ) {
438                    date = new Date( ts.getTime() );
439                }
440            } catch ( SQLException e ) {
441                throw e;
442            } finally {
443                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
444                                        jdbc.getPassword() );
445            }
446            return date;
447        }
448    
449        /**
450         * sets the timestamp when a source has been harvested successfully for the last time
451         * 
452         * @param source
453         * @param date
454         * @throws DBPoolException
455         * @throws SQLException
456         */
457        synchronized void setLastHarvestingTimestamp( URI source, Date date )
458                                throws DBPoolException, SQLException {
459    
460            LOG.logDebug( "set timestamp for source: " + source + " last harvesting" );
461    
462            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
463                                                     jdbc.getPassword() );
464            try {
465                con.setAutoCommit( false );
466            } catch ( Exception ignore ) {
467            }
468            try {
469                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.setLastHarvestingTimestamp" ) );
470                ps.setTimestamp( 1, new Timestamp( date.getTime() ) );
471                ps.setString( 2, source.toASCIIString() );
472                ps.execute();
473                ps.close();
474                con.commit();
475            } catch ( SQLException e ) {
476                con.rollback();
477                throw e;
478            } finally {
479                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
480                                        jdbc.getPassword() );
481            }
482        }
483    
484        /**
485         * returns the next Date a source shall be harvested
486         * 
487         * @param source
488         * @return the next Date a source shall be harvested
489         * @throws DBPoolException
490         * @throws SQLException
491         */
492        synchronized Date getNextHarvestingTimestamp( URI source )
493                                throws DBPoolException, SQLException {
494            LOG.logDebug( "reading timestamp for source: " + source + " next harvesting" );
495    
496            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
497                                                     jdbc.getPassword() );
498            Date date = null;
499            try {
500                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.getNextHarvestingTimestamp" ) );
501                ps.setString( 1, source.toASCIIString() );
502                ResultSet rs = ps.executeQuery();
503                rs.next();
504                Timestamp ts = rs.getTimestamp( 1 );
505                rs.close();
506                ps.close();
507                date = new Date( ts.getTime() );
508            } catch ( SQLException e ) {
509                throw e;
510            } finally {
511                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
512                                        jdbc.getPassword() );
513            }
514            return date;
515        }
516    
517        /**
518         * sets the next date a source shall be harvested
519         * 
520         * @param source
521         * @param date
522         * @throws DBPoolException
523         * @throws SQLException
524         */
525        synchronized void setNextHarvestingTimestamp( URI source, Date date )
526                                throws DBPoolException, SQLException {
527    
528            LOG.logDebug( "set timestamp for source: " + source + " last harvesting" );
529    
530            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
531                                                     jdbc.getPassword() );
532            try {
533                con.setAutoCommit( false );
534            } catch ( Exception ignore ) {
535            }
536            try {
537                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.setNextHarvestingTimestamp" ) );
538                ps.setTimestamp( 1, new Timestamp( date.getTime() ) );
539                ps.setString( 2, source.toASCIIString() );
540                ps.execute();
541                ps.close();
542                con.commit();
543            } catch ( SQLException e ) {
544                con.rollback();
545            } finally {
546                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
547                                        jdbc.getPassword() );
548            }
549    
550        }
551    
552        /**
553         * returns the interval in
554         * 
555         * @param source
556         * @return
557         * @throws DBPoolException
558         * @throws SQLException
559         */
560        synchronized long getHarvestInterval( URI source )
561                                throws DBPoolException, SQLException {
562    
563            LOG.logDebug( "reading harvest interval for source: " + source );
564    
565            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
566                                                     jdbc.getPassword() );
567            long interval = 0;
568            try {
569                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.getHarvestInterval" ) );
570                ps.setString( 1, source.toASCIIString() );
571                ResultSet rs = ps.executeQuery();
572                rs.next();
573                interval = rs.getLong( 1 ) * 1000l;
574                rs.close();
575                ps.close();
576            } catch ( SQLException e ) {
577                throw e;
578            } finally {
579                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
580                                        jdbc.getPassword() );
581            }
582    
583            return interval;
584        }
585    
586        /**
587         * returns a list
588         * 
589         * @param source
590         * @return
591         * @throws DBPoolException
592         * @throws SQLException
593         * @throws URISyntaxException
594         */
595        synchronized List<ResponseHandler> getResponseHandlers( URI source )
596                                throws DBPoolException, SQLException, URISyntaxException {
597    
598            LOG.logDebug( "reading response handler for source: " + source );
599    
600            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
601                                                     jdbc.getPassword() );
602            List<ResponseHandler> list = new ArrayList<ResponseHandler>();
603            try {
604                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.getResponseHandlers1" ) );
605                ps.setString( 1, source.toASCIIString() );
606                ResultSet rs = ps.executeQuery();
607                rs.next();
608                int id1 = rs.getInt( 1 );
609                rs.close();
610                ps.close();
611    
612                ps = con.prepareStatement( prop.getProperty( "harvester.getResponseHandlers2" ) );
613                ps.setInt( 1, id1 );
614                rs = ps.executeQuery();
615                StringBuffer sb = new StringBuffer( " (" );
616                int kk = 0;
617                while ( rs.next() ) {
618                    kk++;
619                    sb.append( rs.getInt( 1 ) ).append( ',' );
620                }
621                rs.close();
622                ps.close();
623    
624                if ( kk > 0 ) {
625                    // just access response handler informations if available
626                    String s = sb.substring( 0, sb.length() - 1 ) + ')';
627                    ps = con.prepareStatement( prop.getProperty( "harvester.getResponseHandlers3" ) + s );
628                    rs = ps.executeQuery();
629    
630                    while ( rs.next() ) {
631                        String addr = rs.getString( 1 );
632                        boolean isMail = rs.getBoolean( 2 );
633                        list.add( new ResponseHandler( new URI( addr ), isMail ) );
634                    }
635                    rs.close();
636                    ps.close();
637                }
638            } catch ( SQLException e ) {
639                throw e;
640            } catch ( URISyntaxException e ) {
641                throw e;
642            } finally {
643                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
644                                        jdbc.getPassword() );
645            }
646    
647            return list;
648        }
649    
650        /**
651         * returns a <code>Record</code> from the harvesters cache. A instance of a
652         * <code>Record</code> includes its fileIdentifier,the datestamp when it has been changed for
653         * the last time and the source it belongs too.
654         * 
655         * @param source
656         * @param fileIdentifier
657         * @return a <code>Record</code> from the harvesters cache
658         * @throws DBPoolException
659         * @throws SQLException
660         */
661        synchronized Record getRecordByID( URI source, String fileIdentifier )
662                                throws DBPoolException, SQLException {
663    
664            LOG.logDebug( "reading record: " + fileIdentifier + " from harvest cache" );
665    
666            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
667                                                     jdbc.getPassword() );
668            Record record = null;
669            try {
670                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.getRecordByID1" ) );
671                ps.setString( 1, source.toASCIIString() );
672                ResultSet rs = ps.executeQuery();
673                rs.next();
674                int id = rs.getInt( 1 );
675                rs.close();
676                ps.close();
677    
678                ps = con.prepareStatement( prop.getProperty( "harvester.getRecordByID2" ) );
679                ps.setInt( 1, id );
680                ps.setString( 2, fileIdentifier );
681                rs = ps.executeQuery();
682    
683                if ( rs.next() ) {
684                    Date date = rs.getDate( 1 );
685                    record = new Record( id, date, fileIdentifier, source );
686                }
687                rs.close();
688                ps.close();
689            } catch ( SQLException e ) {
690                throw e;
691            } finally {
692                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
693                                        jdbc.getPassword() );
694            }
695    
696            return record;
697        }
698    
699        /**
700         * stores a record into the cache table used by the harvester
701         * 
702         * @param record
703         * @throws DBPoolException
704         * @throws SQLException
705         */
706        synchronized void storeRecord( Record record )
707                                throws DBPoolException, SQLException {
708    
709            LOG.logDebug( "storing record in cache; fileIdentifier: " + record.getFileIdentifier() );
710    
711            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
712                                                     jdbc.getPassword() );
713            try {
714                con.setAutoCommit( false );
715            } catch ( Exception ignore ) {
716            }
717            try {
718                String fid = record.getFileIdentifier();
719                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.storeRecord1" ) );
720                ps.setString( 1, fid );
721                ResultSet rs = ps.executeQuery();
722                rs.next();
723                int count = rs.getInt( 1 );
724                if ( count == 0 ) {
725                    ps = con.prepareStatement( prop.getProperty( "harvester.storeRecord2" ) );
726                    ps.setInt( 1, getSourceID( record.getSource() ) );
727                    ps.setString( 2, fid );
728                    ps.setTimestamp( 3, new Timestamp( record.getDatestamp().getTime() ) );
729                    ps.execute();
730                    ps.close();
731    
732                    con.commit();
733                }
734    
735            } catch ( SQLException e ) {
736                con.rollback();
737                throw e;
738            } catch ( Exception e ) {
739                con.rollback();
740                throw new SQLException( "could not insert harvest request " + "into repository: "
741                                        + e.getMessage() );
742            } finally {
743                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
744                                        jdbc.getPassword() );
745            }
746    
747        }
748    
749        /**
750         * updates a record within the cache table used by the harvester
751         * 
752         * @param record
753         * @throws DBPoolException
754         * @throws SQLException
755         */
756        synchronized void updateRecord( Record record )
757                                throws DBPoolException, SQLException {
758    
759            LOG.logDebug( "updating record in cache; fileIdentifier: " + record.getFileIdentifier() );
760    
761            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
762                                                     jdbc.getPassword() );
763            try {
764                con.setAutoCommit( false );
765            } catch ( Exception ignore ) {
766            }
767            try {
768                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.updateRecord" ) );
769                ps.setDate( 1, new java.sql.Date( record.getDatestamp().getTime() ) );
770                ps.setString( 2, record.getFileIdentifier() );
771                ps.setInt( 3, record.getSourceId() );
772                ps.execute();
773                ps.close();
774    
775                con.commit();
776    
777            } catch ( SQLException e ) {
778                con.rollback();
779                throw e;
780            } catch ( Exception e ) {
781                con.rollback();
782                throw new SQLException( "could not insert harvest request " + "into repository: "
783                                        + e.getMessage() );
784            } finally {
785                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
786                                        jdbc.getPassword() );
787            }
788    
789        }
790    
791        /**
792         * drops a record from the cache table used by the harvester
793         * 
794         * @param record
795         * @throws DBPoolException
796         * @throws SQLException
797         */
798        synchronized void dropRecord( Record record )
799                                throws DBPoolException, SQLException {
800    
801            LOG.logDebug( "deleting record from cache; fileIdentifier: " + record.getFileIdentifier() );
802    
803            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
804                                                     jdbc.getPassword() );
805            try {
806                con.setAutoCommit( false );
807            } catch ( Exception ignore ) {
808            }
809            try {
810    
811                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.dropRecord" ) );
812                ps.setString( 1, record.getFileIdentifier() );
813                ps.setInt( 2, record.getSourceId() );
814                ps.execute();
815                ps.close();
816    
817                con.commit();
818    
819            } catch ( SQLException e ) {
820                con.rollback();
821                throw e;
822            } catch ( Exception e ) {
823                con.rollback();
824                throw new SQLException( "could not insert harvest request " + "into repository: "
825                                        + e.getMessage() );
826            } finally {
827                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
828                                        jdbc.getPassword() );
829            }
830    
831        }
832    
833        /**
834         * returns fileidentifiers of all records assigend to a source from the harvest cache
835         * 
836         * @param source
837         * @return fileidentifiers of all records assigend to a source from the harvest cache
838         * @throws DBPoolException
839         * @throws SQLException
840         */
841        synchronized List<String> getAllRecords( URI source )
842                                throws DBPoolException, SQLException {
843    
844            LOG.logDebug( "getting list of all record fileidentifiers for source: " + source
845                          + " from cache" );
846    
847            List<String> fileIds = new ArrayList<String>( 10000 );
848    
849            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
850                                                     jdbc.getPassword() );
851            try {
852                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.getAllRecords1" ) );
853                ps.setString( 1, source.toASCIIString() );
854                ResultSet rs = ps.executeQuery();
855                rs.next();
856                int id = rs.getInt( 1 );
857                rs.close();
858                ps.close();
859    
860                ps = con.prepareStatement( prop.getProperty( "harvester.getAllRecords2" ) );
861                ps.setInt( 1, id );
862                rs = ps.executeQuery();
863                while ( rs.next() ) {
864                    fileIds.add( rs.getString( 1 ) );
865                }
866                rs.close();
867                ps.close();
868            } catch ( SQLException e ) {
869                throw e;
870            } finally {
871                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
872                                        jdbc.getPassword() );
873            }
874    
875            return fileIds;
876        }
877    
878        /**
879         * returns the row ID of the passed source
880         * 
881         * @param source
882         * @return the row ID of the passed source
883         * @throws DBPoolException
884         * @throws SQLException
885         */
886        synchronized int getSourceID( URI source )
887                                throws DBPoolException, SQLException {
888            LOG.logDebug( "reading row ID of source: " + source );
889    
890            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
891                                                     jdbc.getPassword() );
892            int id = -1;
893            try {
894                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.getSourceID" ) );
895                ps.setString( 1, source.toASCIIString() );
896                ResultSet rs = ps.executeQuery();
897                rs.next();
898                id = rs.getInt( 1 );
899                rs.close();
900                ps.close();
901            } catch ( SQLException e ) {
902                throw e;
903            } finally {
904                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
905                                        jdbc.getPassword() );
906            }
907    
908            return id;
909        }
910        
911        /**
912         * returs true is a harvesting shall be forced outside the regular harvesting interval  
913         * 
914         * @param source
915         * @return true if a CSW shall be harvested outside the regular harvesting interval
916         * @throws DBPoolException  
917         */
918        synchronized boolean shallForceHarvesting(URI source) throws DBPoolException {
919            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
920                                                     jdbc.getPassword() );
921            boolean force = false;
922            try {
923                PreparedStatement ps = con.prepareStatement( prop.getProperty( "harvester.forceHarvesting" ) );
924                ps.setString( 1, source.toASCIIString() );
925                ResultSet rs = ps.executeQuery();
926                rs.next();
927                force = rs.getInt( 1 ) == 1;
928                rs.close();
929                ps.close();
930            } catch ( Exception e ) {
931                // TODO 
932                // this is because downward compliance; older CSW does not know the requested field
933                // harvestsource.forceharvesting
934                //throw e;
935            } finally {
936                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(),
937                                        jdbc.getPassword() );
938            }
939            return force;
940        }
941    
942        /**
943         * inner class for encapsulating response handler informations
944         * 
945         * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
946         * @author last edited by: $Author: apoth $
947         * 
948         * @version $Revision: 9345 $, $Date: 2007-12-27 17:22:25 +0100 (Do, 27 Dez 2007) $
949         */
950        class ResponseHandler {
951    
952            private URI uri = null;
953    
954            private boolean isMailAddress = false;
955    
956            /**
957             * @param uri
958             * @param isMailAddress
959             */
960            ResponseHandler( URI uri, boolean isMailAddress ) {
961                this.uri = uri;
962                this.isMailAddress = isMailAddress;
963            }
964    
965            /**
966             * @return
967             */
968            boolean isMailAddress() {
969                return isMailAddress;
970            }
971    
972            /**
973             * @return uri
974             */
975            URI getUri() {
976                return uri;
977            }
978    
979        }
980    
981        /**
982         * 
983         * 
984         * 
985         * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
986         * @author last edited by: $Author: apoth $
987         * 
988         * @version $Revision: 9345 $, $Date: 2007-12-27 17:22:25 +0100 (Do, 27 Dez 2007) $
989         * 
990         */
991        public class Record {
992    
993            private Date datestamp = null;
994    
995            private String fileIdentifier = null;
996    
997            private URI source = null;
998    
999            private int sourceId;
1000    
1001            /**
1002             * @param sourceId
1003             * @param datestamp
1004             * @param fileIdentifier
1005             * @param source
1006             */
1007            public Record( int sourceId, Date datestamp, String fileIdentifier, URI source ) {
1008                this.datestamp = datestamp;
1009                this.fileIdentifier = fileIdentifier;
1010                this.source = source;
1011                this.sourceId = sourceId;
1012            }
1013    
1014            /**
1015             * @return datestamp
1016             */
1017            public Date getDatestamp() {
1018                return datestamp;
1019            }
1020    
1021            /**
1022             * @return fileIdentifier
1023             */
1024            public String getFileIdentifier() {
1025                return fileIdentifier;
1026            }
1027    
1028            /**
1029             * @return source
1030             */
1031            public URI getSource() {
1032                return source;
1033            }
1034    
1035            /**
1036             * @return sourceId
1037             */
1038            public int getSourceId() {
1039                return sourceId;
1040            }
1041    
1042        }
1043    
1044    }