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