001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/ogcwebservices/csw/manager/CSWSychronizationTrigger.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005       Department of Geography, University of Bonn
006     and
007       lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035    ----------------------------------------------------------------------------*/
036    package org.deegree.ogcwebservices.csw.manager;
037    
038    import java.io.IOException;
039    import java.io.InputStream;
040    import java.net.URL;
041    import java.sql.Connection;
042    import java.sql.PreparedStatement;
043    import java.sql.ResultSet;
044    import java.sql.SQLException;
045    import java.sql.Statement;
046    import java.util.ArrayList;
047    import java.util.List;
048    
049    import org.apache.commons.httpclient.HttpClient;
050    import org.apache.commons.httpclient.HttpException;
051    import org.apache.commons.httpclient.methods.PostMethod;
052    import org.apache.commons.httpclient.methods.StringRequestEntity;
053    import org.deegree.enterprise.WebUtils;
054    import org.deegree.framework.log.ILogger;
055    import org.deegree.framework.log.LoggerFactory;
056    import org.deegree.framework.mail.EMailMessage;
057    import org.deegree.framework.mail.MailHelper;
058    import org.deegree.framework.mail.MailMessage;
059    import org.deegree.framework.mail.SendMailException;
060    import org.deegree.framework.mail.UnknownMimeTypeException;
061    import org.deegree.framework.trigger.Trigger;
062    import org.deegree.framework.trigger.TriggerException;
063    import org.deegree.framework.util.CharsetUtils;
064    import org.deegree.framework.xml.XMLFragment;
065    import org.deegree.i18n.Messages;
066    import org.deegree.io.DBConnectionPool;
067    import org.xml.sax.SAXException;
068    
069    /**
070     * Trigger implementation for synchronizing several CSW instances for incomming Transaction requests
071     *
072     *
073     * @version $Revision: 18195 $
074     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
075     * @author last edited by: $Author: mschneider $
076     *
077     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
078     */
079    public class CSWSychronizationTrigger implements Trigger {
080    
081        private static final ILogger LOG = LoggerFactory.getLogger( CSWSychronizationTrigger.class );
082    
083        private String name;
084    
085        private URL[] cswAddr;
086    
087        private String driver;
088    
089        private String url;
090    
091        private String user;
092    
093        private String password;
094    
095        private String smtpServer;
096    
097        private String sender;
098    
099        private String receiver;
100    
101        private int maxRepeat = 0;
102    
103        /**
104         *
105         * @param driver
106         * @param url
107         * @param user
108         * @param password
109         * @param smtpServer
110         * @param sender
111         * @param receiver
112         * @param maxRepeat
113         * @param address
114         *            addresses of all CSW instances to be synchronized
115         */
116        public CSWSychronizationTrigger( String driver, String url, String user, String password, String smtpServer,
117                                         String sender, String receiver, Integer maxRepeat, URL address ) {
118            this.cswAddr = new URL[] { address };
119            this.driver = driver;
120            this.url = url;
121            this.user = user;
122            this.password = password;
123            this.smtpServer = smtpServer;
124            this.sender = sender;
125            this.receiver = receiver;
126            this.maxRepeat = maxRepeat;
127        }
128    
129        /**
130         * @param caller
131         * @param values
132         * @return the objects
133         */
134        public Object[] doTrigger( Object caller, Object... values ) {
135    
136            // try to execute failed request stored in the db
137            performFormerRequests();
138    
139            if ( !( values[0] instanceof TransactionResult ) ) {
140                return values;
141            }
142    
143            TransactionResult result = (TransactionResult) values[0];
144            Transaction transaction = (Transaction) result.getRequest();
145    
146            TransactionDocument tDoc = null;
147            try {
148                tDoc = XMLFactory.export( transaction );
149            } catch ( Exception e ) {
150                // should not happen because request has been parsed and
151                // performed before caling this method
152                LOG.logError( e.getMessage(), e );
153                throw new TriggerException( e );
154            }
155    
156            List<URL> errorAddr = new ArrayList<URL>();
157            String req = tDoc.getAsString();
158            for ( int i = 0; i < cswAddr.length; i++ ) {
159                try {
160                    String excep = performRequest( req, cswAddr[i] );
161                    if ( "Exception".equals( excep ) ) {
162                        errorAddr.add( cswAddr[i] );
163                    }
164                } catch ( Exception e ) {
165                    LOG.logError( e.getMessage(), e );
166                    errorAddr.add( cswAddr[i] );
167                }
168            }
169    
170            try {
171                if ( errorAddr.size() > 0 ) {
172                    handleErrors( errorAddr, tDoc.getAsString() );
173                }
174            } catch ( Exception e ) {
175                // exception will not be forwarded because it does not affect
176                // performance of request by the triggering CSW
177                LOG.logError( e.getMessage(), e );
178            }
179    
180            return values;
181        }
182    
183        /**
184         * sends a request to the passed url
185         *
186         * @param req
187         * @param url
188         * @return the local name of the response XML
189         * @throws IOException
190         * @throws HttpException
191         * @throws SAXException
192         */
193        private String performRequest( String req, URL url )
194                                throws IOException, HttpException, SAXException {
195            StringRequestEntity re = new StringRequestEntity( req, "text/xml", CharsetUtils.getSystemCharset() );
196            PostMethod post = new PostMethod( url.toExternalForm() );
197            post.setRequestEntity( re );
198            HttpClient client = new HttpClient();
199            client = WebUtils.enableProxyUsage( client, url );
200            client.executeMethod( post );
201            InputStream is = post.getResponseBodyAsStream();
202            XMLFragment xml = new XMLFragment();
203            xml.load( is, url.toExternalForm() );
204            String excep = xml.getRootElement().getLocalName();
205            return excep;
206        }
207    
208        /**
209         *
210         * @param errorAddr
211         * @param request
212         */
213        private void handleErrors( List<URL> errorAddr, String request ) {
214            storeCurrentRequest( errorAddr, request );
215            informAdmin( Messages.getMessage( "CSW_ERROR_SYNCHRONIZE_CSW", errorAddr, request ) );
216        }
217    
218        private void performFormerRequests() {
219            try {
220                DBConnectionPool pool = DBConnectionPool.getInstance();
221                Connection con = pool.acquireConnection( driver, url, user, password );
222                Statement stmt = con.createStatement();
223                List<Fail> failed = new ArrayList<Fail>( 100 );
224                ResultSet rs = stmt.executeQuery( "SELECT * FROM FAILEDREQUESTS" );
225                // first read all request that failed before from the database
226                // to avoid performing transactions on the same table at the
227                // same time
228                while ( rs.next() ) {
229                    int id = rs.getInt( "ID" );
230                    String req = rs.getString( "REQUEST" );
231                    String cswAddress = rs.getString( "CSWADDRESS" );
232                    int repeat = rs.getInt( "REPEAT" );
233                    failed.add( new Fail( id, req, new URL( cswAddress ), repeat ) );
234                }
235                rs.close();
236    
237                for ( int i = 0; i < failed.size(); i++ ) {
238                    try {
239                        String excep = performRequest( failed.get( i ).request, failed.get( i ).cswAddress );
240                        if ( !"Exception".equals( excep ) ) {
241                            // if request has been performed successfully delete entry
242                            // from the database
243                            stmt.execute( "DELETE FROM FAILEDREQUESTS WHERE ID = " + failed.get( i ).id );
244                        } else {
245                            // otherwise increase counter to indicate how often performing
246                            // this request has failed
247                            updateFailedrequests( stmt, failed.get( i ) );
248                        }
249                    } catch ( Exception e ) {
250                        // just to ensure that if a sql exception occurs other requests
251                        // has the chance to be removed from the DB
252                        LOG.logError( e.getMessage(), e );
253                        informAdmin( Messages.getMessage( "CSW_ERROR_UPDATING_FAILEDREQUESTS", failed.get( i ).id ) );
254                        updateFailedrequests( stmt, failed.get( i ) );
255                    }
256                }
257                stmt.close();
258                pool.releaseConnection( con, driver, url, user, password );
259            } catch ( Exception e ) {
260                LOG.logError( e.getMessage(), e );
261                throw new TriggerException( e );
262            }
263        }
264    
265        private void updateFailedrequests( Statement stmt, Fail failed )
266                                throws SQLException {
267            // increase counter to indicate how often performing
268            // this request has failed
269            failed.repeat++;
270            if ( failed.repeat > maxRepeat ) {
271                informAdmin( Messages.getMessage( "CSW_ERROR_EXCEEDING_MAX_REPEAT", failed.cswAddress, failed.request,
272                                                  maxRepeat ) );
273                Boolean result = stmt.execute( "DELETE FROM FAILEDREQUESTS WHERE ID = " + failed.id );
274                LOG.logDebug( "Result of deleting from failed requests when maxRepeat is reached: " + result );
275            } else {
276                Boolean result = stmt.execute( "UPDATE FAILEDREQUESTS SET REPEAT = " + failed.repeat + " WHERE ID = "
277                                               + failed.id );
278                LOG.logDebug( "Result of updating repeat of failed requests: " + result );
279            }
280        }
281    
282        private void storeCurrentRequest( List<URL> errorAddr, String request ) {
283    
284            try {
285                DBConnectionPool pool = DBConnectionPool.getInstance();
286                Connection con = pool.acquireConnection( driver, url, user, password );
287                for ( int i = 0; i < errorAddr.size(); i++ ) {
288                    PreparedStatement stmt = con.prepareStatement( "INSERT INTO FAILEDREQUESTS (REQUEST,CSWADDRESS,REPEAT) VALUES (?,?,?)" );
289                    try {
290                        stmt.setString( 1, request );
291                        stmt.setString( 2, errorAddr.get( i ).toExternalForm() );
292                        stmt.setInt( 3, 1 );
293                        Boolean result = stmt.execute();
294                        LOG.logDebug( "Result of inserting failed requests: " + result );
295                    } catch ( Exception e ) {
296                        // just to ensure that if a sql exception occurs other requests
297                        // has the chance to be inserted into the DB
298                        LOG.logError( e.getMessage(), e );
299                        informAdmin( Messages.getMessage( "CSW_ERROR_INSERTING_INTO_FAILEDREQUESTS", errorAddr.get( i ),
300                                                          request ) );
301                    }
302                    stmt.close();
303                }
304                pool.releaseConnection( con, driver, url, user, password );
305            } catch ( Exception e ) {
306                LOG.logError( e.getMessage(), e );
307                throw new TriggerException( e );
308            }
309    
310        }
311    
312        private void informAdmin( String message ) {
313    
314            String subject = Messages.getMessage( "CSW_SYNCHRONIZE_MAIL_SUBJECT" );
315    
316            MailMessage email;
317            try {
318                email = new EMailMessage( sender, receiver, subject, message, "text/html" );
319            } catch ( UnknownMimeTypeException e ) {
320                LOG.logError( e.getMessage(), e );
321                throw new TriggerException( "Unknown mime type set." + e );
322            }
323    
324            try {
325                MailHelper.createAndSendMail( email, smtpServer );
326            } catch ( SendMailException e ) {
327                LOG.logError( e.getMessage(), e );
328            }
329    
330        }
331    
332        /**
333         * @see org.deegree.framework.trigger.Trigger#getName()
334         */
335        public String getName() {
336            return name;
337        }
338    
339        /**
340         * @see org.deegree.framework.trigger.Trigger#setName(java.lang.String)
341         */
342        public void setName( String name ) {
343            this.name = name;
344        }
345    
346        private class Fail {
347            /**
348             *
349             */
350            public int id = 0;
351    
352            /**
353             *
354             */
355            public String request;
356    
357            /**
358             *
359             */
360            public URL cswAddress;
361    
362            /**
363             *
364             */
365            public int repeat;
366    
367            /**
368             * @param id
369             * @param request
370             * @param cswAddress
371             * @param repeat
372             */
373            public Fail( int id, String request, URL cswAddress, int repeat ) {
374                this.id = id;
375                this.request = request;
376                this.cswAddress = cswAddress;
377                this.repeat = repeat;
378            }
379        }
380    
381    }