001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/enterprise/servlet/OGCServletController.java $
002    // $Id: OGCServletController.java 7611 2007-06-21 17:06:26Z rbezema $
003    /*----------------    FILE HEADER  ------------------------------------------
004     
005     This file is part of deegree.
006     Copyright (C) 2001-2006 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.owscommon.XMLFactory;
081    
082    /**
083     * An <code>OGCServletController</code> handles all incoming requests. The controller for all OGC
084     * service requests. Dispatcher to specific handler for WMS, WFS and other.
085     * 
086     * @author <a href="mailto:tfr@users.sourceforge.net">Torsten Friebe </a>
087     * 
088     * @author last edited by: $Author: rbezema $
089     * 
090     * @see <a
091     *      href="http://java.sun.com/blueprints/corej2eepatterns/Patterns/FrontController.html">Front
092     *      controller </a>
093     */
094    public class OGCServletController extends AbstractOGCServlet {
095    
096        /**
097         * address is the url of the client which requests.
098         */
099        public static String address = null;
100    
101        private static final long serialVersionUID = -4461759017823581221L;
102    
103        private static final ILogger LOG = LoggerFactory.getLogger( OGCServletController.class );
104    
105        private static final String SERVICE = "services";
106    
107        private static final String HANDLER_CLASS = ".handler";
108    
109        private static final String HANDLER_CONF = ".config";
110    
111        private static final Map<Class, String> SERVICE_FACTORIES_MAPPINGS = new HashMap<Class, String>();
112    
113        private static final String ERR_MSG = "Can't set configuration for {0}";
114    
115        /**
116         * 
117         * 
118         * @param request
119         * @param response
120         * @throws ServiceException
121         * @TODO refactor and optimize code for initializing handler
122         */
123        public void doService( HttpServletRequest request, HttpServletResponse response )
124                                throws ServiceException {
125            if ( response.isCommitted() ) {
126                LOG.logWarning( "The response object is already committed, cannot proceed!" );
127                return;
128            }
129    
130            long startTime = System.currentTimeMillis();
131            address = request.getRequestURL().toString();
132    
133            try {
134                OGCWebServiceRequest ogcRequest = OGCRequestFactory.create( request );
135    
136                LOG.logInfo( StringTools.concat( 500, "Handling request '", ogcRequest.getId(),
137                                                 "' from '", request.getRemoteAddr(),
138                                                 "' to service: '", ogcRequest.getServiceName(), "'" ) );
139    
140                // get service from request
141                String service = ogcRequest.getServiceName().toUpperCase();
142    
143                // get handler instance
144                ServiceDispatcher handler = ServiceLookup.getInstance().getHandler( service );
145                // dispatch request to specific handler
146                handler.perform( ogcRequest, response );
147            } catch ( OGCWebServiceException e ) {
148                e.printStackTrace();
149                LOG.logError( e.getMessage(), e );
150                sendException( response, e, request );
151            } catch ( ServiceException e ) {
152                e.printStackTrace();
153                if ( e.getNestedException() instanceof OGCWebServiceException ) {
154                    sendException( response, (OGCWebServiceException) e.getNestedException(), request );
155                } else {
156                    sendException( response, new OGCWebServiceException( this.getClass().getName(),
157                                                                         e.getMessage() ), request );
158                }
159                LOG.logError( e.getMessage(), e );
160            } catch ( Exception e ) {
161                e.printStackTrace();
162                sendException( response, new OGCWebServiceException( this.getClass().getName(),
163                                                                     e.getMessage() ), request );
164                LOG.logError( e.getMessage(), e );
165                throw new ServiceException( e );
166            }
167            if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
168                LOG.logDebug( "OGCServletController: request performed in "
169                              + Long.toString( System.currentTimeMillis() - startTime ) + " milliseconds." );
170            }
171        }
172    
173        /**
174         * Sends the passed <tt>OGCWebServiceException</tt> to the calling client.
175         * 
176         * @param response
177         * @param e
178         */
179        @SuppressWarnings("unchecked")
180        private void sendException( HttpServletResponse response, OGCWebServiceException e,
181                                   HttpServletRequest request ) {
182            LOG.logInfo( "Sending OGCWebServiceException to client." );
183            ExceptionReport report = new ExceptionReport( new OGCWebServiceException[] { e } );
184    
185            // according to Sun's JavaDoc, the map always has this type
186            Map<String, String[]> map = request.getParameterMap();
187    
188            // could clash with other services!
189            boolean isWMS130 = false;
190            for ( String str : map.keySet() ) {
191                if ( str.equalsIgnoreCase( "version" ) ) {
192                    String[] version = map.get( str );
193                    if ( version != null && version.length > 0 && version[0].equals( "1.3.0" ) ) {
194                        isWMS130 = true;
195                    }
196                }
197            }
198    
199            try {
200                XMLFragment doc;
201                if ( isWMS130 || "wcs".equalsIgnoreCase( e.getLocator() ) ) {
202                    response.setContentType( "text/xml" );
203                    doc = XMLFactory.exportNS( report );
204                } else {
205                    response.setContentType( "application/vnd.ogc.se_xml" );
206                    doc = XMLFactory.export( report );
207                }
208                OutputStream os = response.getOutputStream();
209                doc.write( os );
210                os.close();
211            } catch ( Exception ex ) {
212                LOG.logError( "ERROR: " + ex.getMessage(), ex );
213            }
214        }
215    
216        /**
217         * 
218         * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
219         *      javax.servlet.http.HttpServletResponse)
220         */
221        @Override
222        protected void doGet( HttpServletRequest request, HttpServletResponse response )
223                                throws ServletException, IOException {
224    
225            LOG.logDebug( "query string ", request.getQueryString() );
226            try {
227                if ( request.getParameter( "RELOADDEEGREE" ) != null ) {
228                    reloadServices( request, response );
229                } else {
230                    this.doService( request, response );
231                }
232            } catch ( ServiceException e ) {
233                LOG.logError( e.getMessage(), e );
234                sendException( response, new OGCWebServiceException( e.getMessage() ), request );
235            }
236        }
237    
238        /**
239         * 
240         * @param request
241         * @param response
242         * @throws ServletException
243         * @throws IOException
244         */
245        private void reloadServices( HttpServletRequest request, HttpServletResponse response )
246                                throws ServletException, IOException {
247            Map map = KVP2Map.toMap( request );
248            String user = (String) map.get( "USER" );
249            String password = (String) map.get( "PASSWORD" );
250            String message = null;
251            if ( getInitParameter( "USER" ) != null && getInitParameter( "PASSWORD" ) != null
252                 && getInitParameter( "USER" ).equals( user )
253                 && getInitParameter( "PASSWORD" ).equals( password ) ) {
254                initServices( getServletContext() );
255                ctDestroyed();
256                message = Messages.getString( "OGCServletController.reloadsuccess" );
257            } else {
258                message = Messages.getString( "OGCServletController.reloadfailed" );
259            }
260            PrintWriter pw = response.getWriter();
261            pw.print( message );
262            pw.flush();
263            pw.close();
264        }
265    
266        /*
267         * (non-Javadoc)
268         * 
269         * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
270         *      javax.servlet.http.HttpServletResponse)
271         */
272        @Override
273        protected void doPost( HttpServletRequest request, HttpServletResponse response )
274                                throws ServletException, IOException {
275            try {
276                this.doService( request, response );
277            } catch ( ServiceException e ) {
278                LOG.logError( e.getMessage(), e );
279                sendException( response, new OGCWebServiceException( e.getMessage() ), request );
280            }
281        }
282    
283        /**
284         * @see javax.servlet.GenericServlet#init()
285         */
286        @Override
287        public void init()
288                                throws ServletException {
289            super.init();
290    
291            LOG.logDebug( "Logger for " + this.getClass().getName() + " initialized." );
292    
293            SERVICE_FACTORIES_MAPPINGS.put( CSWHandler.class,
294                                            "org.deegree.ogcwebservices.csw.CSWFactory" );
295            SERVICE_FACTORIES_MAPPINGS.put( WFSHandler.class,
296                                            "org.deegree.ogcwebservices.wfs.WFServiceFactory" );
297            SERVICE_FACTORIES_MAPPINGS.put( WCSHandler.class,
298                                            "org.deegree.ogcwebservices.wcs.WCServiceFactory" );
299            SERVICE_FACTORIES_MAPPINGS.put( WMSHandler.class,
300                                            "org.deegree.ogcwebservices.wms.WMServiceFactory" );
301            SERVICE_FACTORIES_MAPPINGS.put( SOSHandler.class,
302                                            "org.deegree.ogcwebservices.sos.SOServiceFactory" );
303            SERVICE_FACTORIES_MAPPINGS.put( WPVSHandler.class,
304                                            "org.deegree.ogcwebservices.wpvs.WPVServiceFactory" );
305            SERVICE_FACTORIES_MAPPINGS.put( WMPSHandler.class,
306                                            "org.deegree.ogcwebservices.wmps.WMPServiceFactory" );
307            SERVICE_FACTORIES_MAPPINGS.put( WPSHandler.class,
308                                            "org.deegree.ogcwebservices.wps.WPServiceFactory" );
309            SERVICE_FACTORIES_MAPPINGS.put( WASSHandler.class,
310                                            "org.deegree.ogcwebservices.wass.common.WASServiceFactory" );
311    
312            LOG.logInfo( "-------------------------------------------------------------------------------" );
313            LOG.logInfo( "Starting deegree version " + Version.getVersion() );
314            LOG.logInfo( "- context        : " + this.getServletContext().getServletContextName() );
315            LOG.logInfo( "- real path      : " + this.getServletContext().getRealPath( "/" ) );
316            LOG.logInfo( "- java version   : " + System.getProperty( "java.version" ) + "" );
317            LOG.logInfo( "- system charset : " + CharsetUtils.getSystemCharset() );
318            LOG.logInfo( "- default charset: " + Charset.defaultCharset() );
319            LOG.logInfo( "- server info    : " + this.getServletContext().getServerInfo() );
320            try {
321                LOG.logInfo( "- ip            : " + InetAddress.getLocalHost().getHostAddress() );
322                LOG.logInfo( "- host name     : " + InetAddress.getLocalHost().getHostName() );
323                LOG.logInfo( "- domain name   : " + InetAddress.getLocalHost().getCanonicalHostName() );
324            } catch ( Exception e ) {
325                LOG.logError( e.getMessage(), e );
326            }
327            LOG.logInfo( "-------------------------------------------------------------------------------" );
328            this.initServices( getServletContext() );
329            LOG.logInfo( "-------------------------------------------------------------------------------" );
330            String tmpServiceList = this.getServiceList();
331            if ( tmpServiceList != null && !( "".equals( tmpServiceList.trim() ) ) ) {
332                LOG.logInfo( "Initialized successfully (context '"
333                             + this.getServletContext().getServletContextName() + "'):" );
334                String[] tmpServices = tmpServiceList.split( "," );
335                for ( String service : tmpServices ) {
336                    LOG.logInfo( "- " + service );
337                }
338            } else {
339                LOG.logError( "An Error occured while initializing context '"
340                              + this.getServletContext().getServletContextName()
341                              + "', no services are available." );
342            }
343    
344            LOG.logInfo( "-------------------------------------------------------------------------------" );
345            // Sets the attributes for tomcat -> application.getAttribute(); in jsp sites
346            this.getServletContext().setAttribute( "deegree_ogc_services", this.getServiceList() );
347        }
348    
349        private void initServices( ServletContext context )
350                                throws ServletException {
351    
352            // get list of OGC services
353            String serviceList = this.getRequiredInitParameter( SERVICE );
354    
355            String[] serviceNames = StringTools.toArray( serviceList, ",", false );
356    
357            ServiceLookup lookup = ServiceLookup.getInstance();
358            for ( int i = 0; i < serviceNames.length; i++ ) {
359                LOG.logInfo( StringTools.concat( 100, "---- Initializing ",
360                                                 serviceNames[i].toUpperCase(), " ----" ) );
361                try {
362                    String className = this.getRequiredInitParameter( serviceNames[i] + HANDLER_CLASS );
363                    Class handlerClzz = Class.forName( className );
364    
365                    // initialize each service factory
366                    String s = this.getRequiredInitParameter( serviceNames[i] + HANDLER_CONF );
367                    URL serviceConfigurationURL = WebappResourceResolver.resolveFileLocation( s,
368                                                                                              context,
369                                                                                              LOG );
370    
371                    // set configuration
372                    LOG.logInfo( StringTools.concat( 300, "Reading configuration for ",
373                                                     serviceNames[i].toUpperCase(), " from URL: '",
374                                                     serviceConfigurationURL, "'." ) );
375    
376                    String factoryClassName = SERVICE_FACTORIES_MAPPINGS.get( handlerClzz );
377    
378                    Class factory = Class.forName( factoryClassName );
379                    Method method = factory.getMethod( "setConfiguration", new Class[] { URL.class } );
380                    method.invoke( factory, new Object[] { serviceConfigurationURL } );
381    
382                    // put handler to available service list
383                    lookup.addService( serviceNames[i].toUpperCase(), handlerClzz );
384    
385                    LOG.logInfo( StringTools.concat( 300, serviceNames[i].toUpperCase(),
386                                                     " successfully initialized." ) );
387                } catch ( ServletException e ) {
388                    LOG.logError( e.getMessage(), e );
389                } catch ( InvocationTargetException e ) {
390                    e.getTargetException().printStackTrace();
391                    LOG.logError( this.produceMessage( ERR_MSG, new Object[] { serviceNames[i] } ), e );
392                } catch ( Exception e ) {
393                    LOG.logError( "Can't initialize OGC service:" + serviceNames[i], e );
394                }
395            }
396        }
397    
398        private String getRequiredInitParameter( String name )
399                                throws ServletException {
400            String paramValue = getInitParameter( name );
401            if ( paramValue == null ) {
402    
403                String msg = "Required init parameter '" + name + "' missing in web.xml";
404                LOG.logError( msg );
405                throw new ServletException( msg );
406            }
407            return paramValue;
408        }
409    
410        /**
411         * @return the services, separated by ","
412         */
413        private String getServiceList() {
414    
415            StringBuffer buf = new StringBuffer();
416            ServiceLookup lookup = ServiceLookup.getInstance();
417            for ( Iterator iter = lookup.getIterator(); iter.hasNext(); ) {
418                String serviceName = (String) iter.next();
419                buf.append( serviceName );
420                if ( iter.hasNext() ) {
421                    buf.append( ',' );
422                }
423            }
424            return buf.toString();
425        }
426    
427        /**
428         * Formats the provided string and the args array into a String using MessageFormat.
429         * 
430         * @param pattern
431         * @param args
432         * @return the message to present the client.
433         */
434        private String produceMessage( String pattern, Object[] args ) {
435            return new MessageFormat( pattern ).format( args );
436        }
437    
438        /**
439         * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
440         */
441        public void ctDestroyed() {
442            LOG.logInfo( "Stopping context: " );
443    
444            ServiceLookup lookup = ServiceLookup.getInstance();
445            for ( Iterator iter = lookup.getIterator(); iter.hasNext(); ) {
446                String serviceName = (String) iter.next();
447                LOG.logInfo( "Stopping service " + serviceName );
448    
449                try {
450                    String s = SERVICE_FACTORIES_MAPPINGS.get( lookup.getService( serviceName ) );
451                    Class clzz = Class.forName( s );
452                    // TODO stop and reset all service instances
453                    Method[] methods = clzz.getMethods();
454                    for ( int j = 0; j < methods.length; j++ ) {
455                        if ( methods[j].getName().equals( "reset" ) ) {
456                            Object[] args = new Object[0];
457                            methods[j].invoke( clzz.newInstance(), args );
458                        }
459                    }
460                } catch ( Exception e ) {
461                    LOG.logError( e.getMessage(), e );
462                }
463            }
464        }
465    
466        @Override
467        public void destroy() {
468            super.destroy();
469        }
470    }