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 }