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 }