001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/enterprise/servlet/OGCServletController.java $
002    // $Id: OGCServletController.java 11770 2008-05-19 08:38:25Z rbezema $
003    /*----------------    FILE HEADER  ------------------------------------------
004     
005     This file is part of deegree.
006     Copyright (C) 2001-2008 by:
007     EXSE, Department of Geography, University of Bonn
008     http://www.giub.uni-bonn.de/deegree/
009     lat/lon GmbH
010     http://www.lat-lon.de
011     
012     This library is free software; you can redistribute it and/or
013     modify it under the terms of the GNU Lesser General Public
014     License as published by the Free Software Foundation; either
015     version 2.1 of the License, or (at your option) any later version.
016     
017     This library is distributed in the hope that it will be useful,
018     but WITHOUT ANY WARRANTY; without even the implied warranty of
019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
020     Lesser General Public License for more details.
021     
022     You should have received a copy of the GNU Lesser General Public
023     License along with this library; if not, write to the Free Software
024     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
025     
026     Contact:
027     
028     Andreas Poth
029     lat/lon GmbH
030     Aennchenstr. 19
031     53115 Bonn
032     Germany
033     E-Mail: poth@lat-lon.de
034     
035     Prof. Dr. Klaus Greve
036     Department of Geography
037     University of Bonn
038     Meckenheimer Allee 166
039     53115 Bonn
040     Germany
041     E-Mail: greve@giub.uni-bonn.de
042     
043     
044     ---------------------------------------------------------------------------*/
045    
046    package org.deegree.enterprise.servlet;
047    
048    import java.io.IOException;
049    import java.io.OutputStream;
050    import java.io.PrintWriter;
051    import java.lang.reflect.InvocationTargetException;
052    import java.lang.reflect.Method;
053    import java.net.InetAddress;
054    import java.net.URL;
055    import java.nio.charset.Charset;
056    import java.text.MessageFormat;
057    import java.util.HashMap;
058    import java.util.Iterator;
059    import java.util.Map;
060    
061    import javax.servlet.ServletContext;
062    import javax.servlet.ServletException;
063    import javax.servlet.http.HttpServletRequest;
064    import javax.servlet.http.HttpServletResponse;
065    
066    import org.deegree.enterprise.AbstractOGCServlet;
067    import org.deegree.enterprise.ServiceException;
068    import org.deegree.framework.log.ILogger;
069    import org.deegree.framework.log.LoggerFactory;
070    import org.deegree.framework.util.CharsetUtils;
071    import org.deegree.framework.util.KVP2Map;
072    import org.deegree.framework.util.StringTools;
073    import org.deegree.framework.util.WebappResourceResolver;
074    import org.deegree.framework.version.Version;
075    import org.deegree.framework.xml.XMLFragment;
076    import org.deegree.ogcwebservices.ExceptionReport;
077    import org.deegree.ogcwebservices.OGCRequestFactory;
078    import org.deegree.ogcwebservices.OGCWebServiceException;
079    import org.deegree.ogcwebservices.OGCWebServiceRequest;
080    import org.deegree.ogcwebservices.wmps.configuration.WMPSConfigurationDocument;
081    import org.deegree.ogcwebservices.wms.configuration.WMSConfigurationDocument;
082    import org.deegree.ogcwebservices.wms.configuration.WMSConfigurationDocument_1_3_0;
083    import org.deegree.owscommon.XMLFactory;
084    import org.xml.sax.SAXException;
085    
086    /**
087     * An <code>OGCServletController</code> handles all incoming requests. The controller for all OGC
088     * service requests. Dispatcher to specific handler for WMS, WFS and other.
089     * 
090     * @author <a href="mailto:tfr@users.sourceforge.net">Torsten Friebe </a>
091     * 
092     * @author last edited by: $Author: rbezema $
093     * 
094     * @see <a
095     *      href="http://java.sun.com/blueprints/corej2eepatterns/Patterns/FrontController.html">Front
096     *      controller </a>
097     */
098    public class OGCServletController extends AbstractOGCServlet {
099    
100        /**
101         * address is the url of the client which requests.
102         */
103        public static String address = null;
104    
105        private static final long serialVersionUID = -4461759017823581221L;
106    
107        private static final ILogger LOG = LoggerFactory.getLogger( OGCServletController.class );
108    
109        private static final String SERVICE = "services";
110    
111        private static final String HANDLER_CLASS = ".handler";
112    
113        private static final String HANDLER_CONF = ".config";
114    
115        private static final Map<Class<?>, String> SERVICE_FACTORIES_MAPPINGS = new HashMap<Class<?>, String>();
116    
117        private static final String ERR_MSG = "Can't set configuration for {0}";
118    
119        /**
120         * 
121         * 
122         * @param request
123         * @param response
124         * @throws ServiceException
125         * @TODO refactor and optimize code for initializing handler
126         */
127        public void doService( HttpServletRequest request, HttpServletResponse response )
128                                throws ServiceException {
129            if ( response.isCommitted() ) {
130                LOG.logWarning( "The response object is already committed, cannot proceed!" );
131                return;
132            }
133    
134            long startTime = System.currentTimeMillis();
135            address = request.getRequestURL().toString();
136    
137            String service = null;
138            try {
139                OGCWebServiceRequest ogcRequest = OGCRequestFactory.create( request );
140    
141                LOG.logInfo( StringTools.concat( 500, "Handling request '", ogcRequest.getId(), "' from '",
142                                                 request.getRemoteAddr(), "' to service: '", ogcRequest.getServiceName(),
143                                                 "'" ) );
144    
145                // get service from request
146                service = ogcRequest.getServiceName().toUpperCase();
147    
148                // get handler instance
149                ServiceDispatcher handler = ServiceLookup.getInstance().getHandler( service );
150                // dispatch request to specific handler
151                handler.perform( ogcRequest, response );
152            } catch ( OGCWebServiceException e ) {
153                e.printStackTrace();
154                LOG.logError( e.getMessage(), e );
155                sendException( response, e, request, service );
156            } catch ( ServiceException e ) {
157                e.printStackTrace();
158                if ( e.getNestedException() instanceof OGCWebServiceException ) {
159                    sendException( response, (OGCWebServiceException) e.getNestedException(), request, service );
160                } else {
161                    sendException( response, new OGCWebServiceException( this.getClass().getName(), e.getMessage() ),
162                                   request, service );
163                }
164                LOG.logError( e.getMessage(), e );
165            } catch ( Exception e ) {
166                e.printStackTrace();
167                sendException( response, new OGCWebServiceException( this.getClass().getName(), e.getMessage() ), request,
168                               service );
169                LOG.logError( e.getMessage(), e );
170                throw new ServiceException( e );
171            }
172            if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
173                LOG.logDebug( "OGCServletController: request performed in "
174                              + Long.toString( System.currentTimeMillis() - startTime ) + " milliseconds." );
175            }
176        }
177    
178        /**
179         * Sends the passed <tt>OGCWebServiceException</tt> to the calling client.
180         * 
181         * @param response
182         * @param e
183         */
184        private void sendException( HttpServletResponse response, OGCWebServiceException e, HttpServletRequest request,
185                                    String service ) {
186            LOG.logInfo( "Sending OGCWebServiceException to client." );
187            ExceptionReport report = new ExceptionReport( new OGCWebServiceException[] { e } );
188    
189            // according to Sun's JavaDoc, the map always has this type
190            Map<String, String[]> map = request.getParameterMap();
191    
192            boolean isWMS130 = false, isCSW = false;
193    
194            if ( service != null ) {
195                if ( "wms".equalsIgnoreCase( service ) ) {
196                    for ( String str : map.keySet() ) {
197                        if ( str.equalsIgnoreCase( "version" ) ) {
198                            String[] version = map.get( str );
199                            if ( version != null && version.length > 0 && version[0].equals( "1.3.0" ) ) {
200                                isWMS130 = true;
201                            }
202                        }
203                    }
204                }
205    
206                isCSW = "csw".equalsIgnoreCase( service );
207            } else {
208                // could clash with other services!
209                for ( String str : map.keySet() ) {
210                    if ( str.equalsIgnoreCase( "version" ) ) {
211                        String[] version = map.get( str );
212                        if ( version != null && version.length > 0 && version[0].equals( "1.3.0" ) ) {
213                            isWMS130 = true;
214                        }
215                    }
216                }
217    
218                for ( String str : map.keySet() ) {
219                    if ( str.equalsIgnoreCase( "service" ) ) {
220                        isCSW = map.get( str )[0].equalsIgnoreCase( "csw" );
221                    }
222                }
223    
224                try {
225                    XMLFragment doc = new XMLFragment( request.getReader(), "http://www.systemid.org" );
226                    service = OGCRequestFactory.getTargetService( "", "", doc.getRootElement().getOwnerDocument() );
227                    isCSW = isCSW || "csw".equalsIgnoreCase( service );
228                } catch ( SAXException e1 ) {
229                    // ignore
230                } catch ( IOException e1 ) {
231                    // ignore
232                }
233            }
234    
235            try {
236                XMLFragment doc;
237                if ( isWMS130 || "wcs".equalsIgnoreCase( e.getLocator() ) ) {
238                    response.setContentType( "text/xml" );
239                    doc = XMLFactory.exportNS( report );
240                } else if ( isCSW ) {
241                    response.setContentType( "text/xml" );
242                    doc = XMLFactory.exportExceptionReport( report );
243                } else {
244                    response.setContentType( "application/vnd.ogc.se_xml" );
245                    doc = XMLFactory.export( report );
246                }
247                OutputStream os = response.getOutputStream();
248                doc.write( os );
249                os.close();
250            } catch ( Exception ex ) {
251                LOG.logError( "ERROR: " + ex.getMessage(), ex );
252            }
253        }
254    
255        /**
256         * 
257         * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
258         *      javax.servlet.http.HttpServletResponse)
259         */
260        @Override
261        protected void doGet( HttpServletRequest request, HttpServletResponse response )
262                                throws ServletException, IOException {
263    
264            LOG.logDebug( "query string ", request.getQueryString() );
265            try {
266                if ( request.getParameter( "RELOADDEEGREE" ) != null ) {
267                    reloadServices( request, response );
268                } else {
269                    this.doService( request, response );
270                }
271            } catch ( ServiceException e ) {
272                LOG.logError( e.getMessage(), e );
273                sendException( response, new OGCWebServiceException( e.getMessage() ), request, null );
274            }
275        }
276    
277        /**
278         * 
279         * @param request
280         * @param response
281         * @throws ServletException
282         * @throws IOException
283         */
284        private void reloadServices( HttpServletRequest request, HttpServletResponse response )
285                                throws ServletException, IOException {
286            Map<?, ?> map = KVP2Map.toMap( request );
287            String user = (String) map.get( "USER" );
288            String password = (String) map.get( "PASSWORD" );
289            String message = null;
290            if ( getInitParameter( "USER" ) != null && getInitParameter( "PASSWORD" ) != null
291                 && getInitParameter( "USER" ).equals( user ) && getInitParameter( "PASSWORD" ).equals( password ) ) {
292                initServices( getServletContext() );
293                ctDestroyed();
294                message = Messages.getString( "OGCServletController.reloadsuccess" );
295            } else {
296                message = Messages.getString( "OGCServletController.reloadfailed" );
297            }
298            PrintWriter pw = response.getWriter();
299            pw.print( message );
300            pw.flush();
301            pw.close();
302        }
303    
304        /*
305         * (non-Javadoc)
306         * 
307         * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
308         *      javax.servlet.http.HttpServletResponse)
309         */
310        @Override
311        protected void doPost( HttpServletRequest request, HttpServletResponse response )
312                                throws ServletException, IOException {
313            try {
314                this.doService( request, response );
315            } catch ( ServiceException e ) {
316                LOG.logError( e.getMessage(), e );
317                sendException( response, new OGCWebServiceException( e.getMessage() ), request, null );
318            }
319        }
320    
321        /**
322         * @see javax.servlet.GenericServlet#init()
323         */
324        @Override
325        public void init()
326                                throws ServletException {
327            super.init();
328    
329            LOG.logDebug( "Logger for " + this.getClass().getName() + " initialized." );
330    
331            SERVICE_FACTORIES_MAPPINGS.put( CSWHandler.class, "org.deegree.ogcwebservices.csw.CSWFactory" );
332            SERVICE_FACTORIES_MAPPINGS.put( WFSHandler.class, "org.deegree.ogcwebservices.wfs.WFServiceFactory" );
333            SERVICE_FACTORIES_MAPPINGS.put( WCSHandler.class, "org.deegree.ogcwebservices.wcs.WCServiceFactory" );
334            SERVICE_FACTORIES_MAPPINGS.put( WMSHandler.class, "org.deegree.ogcwebservices.wms.WMServiceFactory" );
335            SERVICE_FACTORIES_MAPPINGS.put( SOSHandler.class, "org.deegree.ogcwebservices.sos.SOServiceFactory" );
336            SERVICE_FACTORIES_MAPPINGS.put( WPVSHandler.class, "org.deegree.ogcwebservices.wpvs.WPVServiceFactory" );
337            SERVICE_FACTORIES_MAPPINGS.put( WMPSHandler.class, "org.deegree.ogcwebservices.wmps.WMPServiceFactory" );
338            SERVICE_FACTORIES_MAPPINGS.put( WPSHandler.class, "org.deegree.ogcwebservices.wps.WPServiceFactory" );
339            SERVICE_FACTORIES_MAPPINGS.put( WASSHandler.class, "org.deegree.ogcwebservices.wass.common.WASServiceFactory" );
340            //SERVICE_FACTORIES_MAPPINGS.put( WCTSHandler.class, "org.deegree.ogcwebservices.wcts.WCTServiceFactory" );
341    
342            LOG.logInfo( "-------------------------------------------------------------------------------" );
343            LOG.logInfo( "Starting deegree version " + Version.getVersion() );
344            LOG.logInfo( "- context        : " + this.getServletContext().getServletContextName() );
345            LOG.logInfo( "- real path      : " + this.getServletContext().getRealPath( "/" ) );
346            LOG.logInfo( "- java version   : " + System.getProperty( "java.version" ) + "" );
347            LOG.logInfo( "- system charset : " + CharsetUtils.getSystemCharset() );
348            LOG.logInfo( "- default charset: " + Charset.defaultCharset() );
349            LOG.logInfo( "- server info    : " + this.getServletContext().getServerInfo() );
350            try {
351                LOG.logInfo( "- ip            : " + InetAddress.getLocalHost().getHostAddress() );
352                LOG.logInfo( "- host name     : " + InetAddress.getLocalHost().getHostName() );
353                LOG.logInfo( "- domain name   : " + InetAddress.getLocalHost().getCanonicalHostName() );
354            } catch ( Exception e ) {
355                LOG.logError( e.getMessage(), e );
356            }
357            LOG.logInfo( "-------------------------------------------------------------------------------" );
358            this.initServices( getServletContext() );
359            checkServerCompatibility();
360            LOG.logInfo( "-------------------------------------------------------------------------------" );
361            String tmpServiceList = this.getServiceList();
362            if ( tmpServiceList != null && !( "".equals( tmpServiceList.trim() ) ) ) {
363                LOG.logInfo( "Initialized successfully (context '" + this.getServletContext().getServletContextName()
364                             + "'):" );
365                String[] tmpServices = tmpServiceList.split( "," );
366                for ( String service : tmpServices ) {
367                    LOG.logInfo( "- " + service );
368                }
369            } else {
370                LOG.logError( "An Error occured while initializing context '"
371                              + this.getServletContext().getServletContextName() + "', no services are available." );
372            }
373    
374            LOG.logInfo( "-------------------------------------------------------------------------------" );
375            // Sets the attributes for tomcat -> application.getAttribute(); in jsp sites
376            this.getServletContext().setAttribute( "deegree_ogc_services", this.getServiceList() );
377        }
378    
379        private void checkServerCompatibility() {
380            String serverInfo = getServletContext().getServerInfo();
381            if ( "Apache Tomcat/5.5.26".equals( serverInfo ) || "Apache Tomcat/6.0.16".equals( serverInfo ) ) {
382                LOG.logWarning( "*******************************************************************************" );
383                LOG.logWarning( "YOU ARE RUNNING DEEGREE ON A TOMCAT RELEASE (" + serverInfo
384                                + ") THAT IS KNOWN TO HAVE A SERIOUS ISSUE WITH LARGE POST REQUESTS." );
385                LOG.logWarning( "PLEASE CONSIDER THE CORRESPONDING DEEGREE WIKI PAGE AT  https://wiki.deegree.org/deegreeWiki/ApacheTomcat "
386                                + "FOR DETAILS AND SWITCH TO A DIFFERENT TOMCAT VERSION." );
387                LOG.logWarning( "*******************************************************************************" );
388            }
389        }
390        private void initServices( ServletContext context )
391                                throws ServletException {
392    
393            // get list of OGC services
394            String serviceList = this.getRequiredInitParameter( SERVICE );
395    
396            String[] serviceNames = StringTools.toArray( serviceList, ",", false );
397    
398            ServiceLookup lookup = ServiceLookup.getInstance();
399            for ( int i = 0; i < serviceNames.length; i++ ) {
400                LOG.logInfo( StringTools.concat( 100, "---- Initializing ", serviceNames[i].toUpperCase(), " ----" ) );
401                try {
402                    String className = this.getRequiredInitParameter( serviceNames[i] + HANDLER_CLASS );
403                    Class<?> handlerClzz = Class.forName( className );
404    
405                    // initialize each service factory
406                    String s = this.getRequiredInitParameter( serviceNames[i] + HANDLER_CONF );
407                    URL serviceConfigurationURL = WebappResourceResolver.resolveFileLocation( s, context, LOG );
408    
409                    // set configuration
410                    LOG.logInfo( StringTools.concat( 300, "Reading configuration for ", serviceNames[i].toUpperCase(),
411                                                     " from URL: '", serviceConfigurationURL, "'." ) );
412    
413                    String factoryClassName = SERVICE_FACTORIES_MAPPINGS.get( handlerClzz );
414    
415                    Class<?> factory = Class.forName( factoryClassName );
416                    Method method = factory.getMethod( "setConfiguration", new Class[] { URL.class } );
417                    method.invoke( factory, new Object[] { serviceConfigurationURL } );
418    
419                    // put handler to available service list
420                    lookup.addService( serviceNames[i].toUpperCase(), handlerClzz );
421    
422                    LOG.logInfo( StringTools.concat( 300, serviceNames[i].toUpperCase(), " successfully initialized." ) );
423                } catch ( ServletException e ) {
424                    LOG.logError( e.getMessage(), e );
425                } catch ( InvocationTargetException e ) {
426                    e.getTargetException().printStackTrace();
427                    LOG.logError( this.produceMessage( ERR_MSG, new Object[] { serviceNames[i] } ), e );
428                } catch ( Exception e ) {
429                    LOG.logError( "Can't initialize OGC service:" + serviceNames[i], e );
430                }
431            }
432        }
433    
434        private String getRequiredInitParameter( String name )
435                                throws ServletException {
436            String paramValue = getInitParameter( name );
437            if ( paramValue == null ) {
438    
439                String msg = "Required init parameter '" + name + "' missing in web.xml";
440                LOG.logError( msg );
441                throw new ServletException( msg );
442            }
443            return paramValue;
444        }
445    
446        /**
447         * @return the services, separated by ","
448         */
449        private String getServiceList() {
450    
451            StringBuffer buf = new StringBuffer();
452            ServiceLookup lookup = ServiceLookup.getInstance();
453            for ( Iterator<?> iter = lookup.getIterator(); iter.hasNext(); ) {
454                String serviceName = (String) iter.next();
455                buf.append( serviceName );
456                if ( iter.hasNext() ) {
457                    buf.append( ',' );
458                }
459            }
460            return buf.toString();
461        }
462    
463        /**
464         * Formats the provided string and the args array into a String using MessageFormat.
465         * 
466         * @param pattern
467         * @param args
468         * @return the message to present the client.
469         */
470        private String produceMessage( String pattern, Object[] args ) {
471            return new MessageFormat( pattern ).format( args );
472        }
473    
474        /**
475         * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
476         */
477        public void ctDestroyed() {
478            LOG.logInfo( "Stopping context: " );
479    
480            WMSConfigurationDocument.resetCapabilitiesCache();
481            WMSConfigurationDocument_1_3_0.resetCapabilitiesCache();
482            WMPSConfigurationDocument.resetCapabilitiesCache();
483    
484            ServiceLookup lookup = ServiceLookup.getInstance();
485            for ( Iterator<?> iter = lookup.getIterator(); iter.hasNext(); ) {
486                String serviceName = (String) iter.next();
487                LOG.logInfo( "Stopping service " + serviceName );
488    
489                try {
490                    String s = SERVICE_FACTORIES_MAPPINGS.get( lookup.getService( serviceName ) );
491                    Class<?> clzz = Class.forName( s );
492                    // TODO stop and reset all service instances
493                    Method[] methods = clzz.getMethods();
494                    for ( int j = 0; j < methods.length; j++ ) {
495                        if ( methods[j].getName().equals( "reset" ) ) {
496                            Object[] args = new Object[0];
497                            methods[j].invoke( clzz.newInstance(), args );
498                        }
499                    }
500                } catch ( Exception e ) {
501                    LOG.logError( e.getMessage(), e );
502                }
503            }
504        }
505    
506        @Override
507        public void destroy() {
508            super.destroy();
509        }
510    }