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 }