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 }