001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/enterprise/servlet/SOAPFacadeServletFilter.java $ 002 /*---------------------------------------------------------------------------- 003 This file is part of deegree, http://deegree.org/ 004 Copyright (C) 2001-2009 by: 005 Department of Geography, University of Bonn 006 and 007 lat/lon GmbH 008 009 This library is free software; you can redistribute it and/or modify it under 010 the terms of the GNU Lesser General Public License as published by the Free 011 Software Foundation; either version 2.1 of the License, or (at your option) 012 any later version. 013 This library is distributed in the hope that it will be useful, but WITHOUT 014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 016 details. 017 You should have received a copy of the GNU Lesser General Public License 018 along with this library; if not, write to the Free Software Foundation, Inc., 019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 020 021 Contact information: 022 023 lat/lon GmbH 024 Aennchenstr. 19, 53177 Bonn 025 Germany 026 http://lat-lon.de/ 027 028 Department of Geography, University of Bonn 029 Prof. Dr. Klaus Greve 030 Postfach 1147, 53001 Bonn 031 Germany 032 http://www.geographie.uni-bonn.de/deegree/ 033 034 e-mail: info@deegree.org 035 ----------------------------------------------------------------------------*/ 036 package org.deegree.enterprise.servlet; 037 038 import static org.deegree.framework.xml.XMLTools.appendElement; 039 import static org.deegree.framework.xml.XMLTools.getElement; 040 import static org.deegree.framework.xml.XMLTools.getElements; 041 import static org.deegree.ogcbase.CommonNamespaces.OWSNS; 042 import static org.deegree.ogcbase.CommonNamespaces.W3SOAP_1_1_PREFIX; 043 import static org.deegree.ogcbase.CommonNamespaces.W3SOAP_ENVELOPE_1_1; 044 import static org.deegree.ogcbase.CommonNamespaces.getNamespaceContext; 045 046 import java.io.BufferedReader; 047 import java.io.ByteArrayOutputStream; 048 import java.io.IOException; 049 import java.io.InputStreamReader; 050 import java.io.OutputStream; 051 import java.io.OutputStreamWriter; 052 import java.io.PrintWriter; 053 import java.io.StringReader; 054 import java.lang.reflect.Constructor; 055 import java.lang.reflect.InvocationTargetException; 056 import java.net.MalformedURLException; 057 import java.net.URI; 058 import java.net.URISyntaxException; 059 import java.net.URL; 060 import java.util.ArrayList; 061 import java.util.Arrays; 062 import java.util.List; 063 import java.util.Map; 064 065 import javax.servlet.Filter; 066 import javax.servlet.FilterChain; 067 import javax.servlet.FilterConfig; 068 import javax.servlet.ServletException; 069 import javax.servlet.ServletRequest; 070 import javax.servlet.ServletResponse; 071 import javax.servlet.http.HttpServletRequest; 072 import javax.servlet.http.HttpServletResponse; 073 import javax.xml.transform.TransformerException; 074 075 import org.deegree.framework.log.ILogger; 076 import org.deegree.framework.log.LoggerFactory; 077 import org.deegree.framework.util.CharsetUtils; 078 import org.deegree.framework.util.WebappResourceResolver; 079 import org.deegree.framework.xml.NamespaceContext; 080 import org.deegree.framework.xml.XMLException; 081 import org.deegree.framework.xml.XMLFragment; 082 import org.deegree.framework.xml.XMLParsingException; 083 import org.deegree.framework.xml.XMLTools; 084 import org.deegree.ogcbase.CommonNamespaces; 085 import org.deegree.ogcbase.ExceptionCode; 086 import org.deegree.ogcwebservices.OGCWebServiceException; 087 import org.w3c.dom.Document; 088 import org.w3c.dom.Element; 089 import org.w3c.dom.Node; 090 import org.xml.sax.SAXException; 091 092 /** 093 * The <code>SOAP_1_1_FacadeServletFilter</code> class is able to handle an incoming SOAP requests. 094 * <p> 095 * It is also able to handle multipart messages, by using the {@link RequestMultiPartHandler}. 096 * </p> 097 * <p> 098 * Following filter-parameters are supported: 099 * <ol> 100 * <li>multipart.handler -- should denote a sub class of RequestMultipartHandler, which can be used to handle 101 * multiparts</li> 102 * <li>error.namespace -- the default namespace of error messages, default to: http://www.opengis.net/ows </li> 103 * <li>wsdl.location -- the location of a wsdl file which will be sent to a requesting client (GET->wsdl)</li> 104 * <li>soap.mustUnderstand -- A comma separated list of namespace bound strings a soap service must understand e.g 105 * {http://some.namespace.org/}:CoolElement,{http://other.namespace.org/}:HotElement </li> 106 * <li>only.except.soap -- if 'true' the service will reject all incoming request which are not soap encoded except for 107 * the get -wsdl request</li> 108 * </ol> 109 * 110 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> 111 * 112 * @author last edited by: $Author: poth $ 113 * 114 * @version $Revision: 1.13 $, $Date: 2007-11-27 12:50:25 $ 115 * 116 */ 117 118 public class SOAP_1_1_FacadeServletFilter implements Filter { 119 120 private static ILogger LOG = LoggerFactory.getLogger( SOAP_1_1_FacadeServletFilter.class ); 121 122 private static NamespaceContext nsContext = getNamespaceContext(); 123 124 private URI defaultErrorNamespace = OWSNS; 125 126 private RequestMultiPartHandler multiPartHandler = null; 127 128 private XMLFragment wsdlDescription = null; 129 130 private List<String> soapUnderstanding = new ArrayList<String>(); 131 132 private boolean onlyExceptSoap; 133 134 @SuppressWarnings("unchecked") 135 // for instantiation of the multipart handler 136 public void init( FilterConfig config ) 137 throws ServletException { 138 String multiPartString = config.getInitParameter( "multipart.handler" ); 139 if ( multiPartString != null && !"".equals( multiPartString.trim() ) ) { 140 // try to instantiate the multipart handler. 141 try { 142 Class<?> c = Class.forName( multiPartString ); 143 Constructor<RequestMultiPartHandler> con = (Constructor<RequestMultiPartHandler>) c.getConstructor(); 144 // call constructor and instantiate a new MultipartHandler 145 multiPartHandler = con.newInstance(); 146 LOG.logDebug( "Successfully Instantiated class: " + multiPartString ); 147 } catch ( ClassNotFoundException e ) { 148 LOG.logError( e.getMessage(), e ); 149 } catch ( SecurityException e ) { 150 LOG.logError( e.getMessage(), e ); 151 } catch ( NoSuchMethodException e ) { 152 LOG.logError( "An empty constructor must be specified for the class: " + multiPartString 153 + ". The error message was: " + e.getMessage(), e ); 154 } catch ( IllegalArgumentException e ) { 155 LOG.logError( "An empty constructor must be specified for the class: " + multiPartString 156 + ". The error message was: " + e.getMessage(), e ); 157 158 } catch ( InstantiationException e ) { 159 LOG.logError( "Could not instantiate the configured multipart handler (" + multiPartString 160 + ") because: " + e.getMessage(), e ); 161 } catch ( IllegalAccessException e ) { 162 LOG.logError( "Could not acquire access to the configured multipart handler (" + multiPartString 163 + ") because: " + e.getMessage(), e ); 164 } catch ( InvocationTargetException e ) { 165 LOG.logError( "Could not invoce the configured multipart handler (" + multiPartString + ") because: " 166 + e.getMessage(), e ); 167 } 168 } 169 String errorNamespace = config.getInitParameter( "error.namespace" ); 170 if ( errorNamespace != null && !"".equals( errorNamespace.trim() ) ) { 171 try { 172 defaultErrorNamespace = new URI( errorNamespace ); 173 } catch ( URISyntaxException e ) { 174 LOG.logError( "Configured 'error.namespace' parameter is not a valid URI, setting to " 175 + OWSNS.toASCIIString() + ". Error message was: " + e.getMessage(), e ); 176 defaultErrorNamespace = OWSNS; 177 } 178 } 179 String wsdlLocation = config.getInitParameter( "wsdl.location" ); 180 if ( wsdlLocation != null && !"".equals( wsdlLocation.trim() ) ) { 181 try { 182 URL wsdlFile = WebappResourceResolver.resolveFileLocation( wsdlLocation, config.getServletContext(), 183 LOG ); 184 wsdlDescription = new XMLFragment( wsdlFile ); 185 } catch ( MalformedURLException e ) { 186 LOG.logError( "Could not load wsdl description document ('wsdl.location' parameter) because: " 187 + e.getMessage(), e ); 188 } catch ( IOException e ) { 189 LOG.logError( "Could not load wsdl description document ('wsdl.location' parameter) because: " 190 + e.getMessage(), e ); 191 } catch ( SAXException e ) { 192 LOG.logError( "Could not load wsdl description document ('wsdl.location' parameter) because: " 193 + e.getMessage(), e ); 194 } 195 } 196 String tmp = config.getInitParameter( "soap.mustUnderstand" ); 197 if ( tmp != null && !"".equals( tmp ) ) { 198 LOG.logDebug( "The mustunderstand list contains following values: " + tmp ); 199 String[] names = tmp.trim().split( "," ); 200 if ( names != null ) { 201 soapUnderstanding = Arrays.asList( names ); 202 } 203 } 204 205 tmp = config.getInitParameter( "only.except.soap" ); 206 if ( tmp != null && !"".equals( tmp ) ) { 207 tmp = tmp.trim().toLowerCase(); 208 onlyExceptSoap = "true".equals( tmp ) || "1".equals( tmp ) || "yes".equals( tmp ) || "on".equals( tmp ); 209 } 210 211 LOG.logInfo( "SOAP 1.1 Servlet Filter successfully initialized " 212 + ( ( multiPartHandler == null ) ? "without" : "with" ) + " multipart support" ); 213 } 214 215 @SuppressWarnings("unchecked") 216 public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) 217 throws IOException, ServletException { 218 ServletRequestWrapper requestWrapper = null; 219 if ( request instanceof ServletRequestWrapper ) { 220 LOG.logDebug( "the incoming request is actually an org.deegree.enterprise.servlet.RequestWrapper, so not creating new instance." ); 221 requestWrapper = (ServletRequestWrapper) request; 222 } else { 223 requestWrapper = new ServletRequestWrapper( (HttpServletRequest) request ); 224 } 225 if ( requestWrapper.getMethod().equalsIgnoreCase( "GET" ) ) { 226 // check for the wsdl parameter, if it is given, return the wsdl (Web Services 227 // Description Language 228 // (WSDL) 1.1) for this service. 229 Map<String, String[]> params = requestWrapper.getParameterMap(); 230 if ( params != null ) { 231 if ( params.containsKey( "wsdl" ) ) { 232 if ( params.keySet().size() > 1 ) { 233 sendException( 234 response, 235 new OGCWebServiceException( 236 "If the wsdl keyword is supplied, no other parameters are allowed.", 237 ExceptionCode.INVALIDPARAMETERVALUE ), false ); 238 } else { 239 try { 240 sendWSDL( response ); 241 } catch ( OGCWebServiceException e ) { 242 LOG.logError( e.getMessage(), e ); 243 sendException( response, e, false ); 244 } 245 } 246 return; 247 } 248 } 249 if ( !onlyExceptSoap ) { 250 chain.doFilter( requestWrapper, response ); 251 } else { 252 sendException( 253 response, 254 new OGCWebServiceException( 255 "This service only excepts soap version 1.1 encoded requests.", 256 ExceptionCode.INVALID_FORMAT ), false ); 257 } 258 } else { 259 response.setCharacterEncoding( CharsetUtils.getSystemCharset() ); 260 261 BufferedReader reader = new BufferedReader( new InputStreamReader( requestWrapper.getInputStream() ) ); 262 String firstLine = reader.readLine(); 263 LOG.logDebug( "first line of request: " + firstLine ); 264 if ( firstLine == null ) { 265 LOG.logInfo( "No request characters found, not handling request" ); 266 // chain.doFilter( requestWrapper, response ); 267 sendException( response, 268 new OGCWebServiceException( "No request characters found, not handling request", 269 ExceptionCode.INVALIDPARAMETERVALUE ), false ); 270 return; 271 } 272 if ( LOG.isDebug() ) { 273 LOG.logDebug( "OUTPUTING as Strings" ); 274 LOG.logDebug( firstLine ); 275 while ( reader.ready() ) { 276 LOG.logDebug( reader.readLine() ); 277 } 278 } 279 280 LOG.logDebug( "Contentype of the request: " + requestWrapper.getContentType() ); 281 282 // These values will be set according to the request properties 283 boolean usingMultiparts = ( requestWrapper.getContentType() != null ) 284 && ( requestWrapper.getContentType().contains( "multipart/form-data" ) ) 285 && multiPartHandler != null; 286 XMLFragment resultingRequest = new XMLFragment(); 287 XMLFragment[] mimeParts = null; 288 if ( usingMultiparts ) { 289 // because we have some multiparts, we will insert them into the request body 290 try { 291 mimeParts = multiPartHandler.handleMultiparts( requestWrapper ); 292 if ( mimeParts.length > 0 ) { 293 resultingRequest = mimeParts[0]; 294 } 295 } catch ( OGCWebServiceException e ) { 296 LOG.logError( e.getMessage(), e ); 297 sendException( response, e, false ); 298 return; 299 } 300 if ( resultingRequest == null ) { 301 LOG.logDebug( "could not generate an xml-dom representation out of the multiparts, returning an error message" ); 302 sendException( 303 response, 304 new OGCWebServiceException( 305 "Could not generate an XML-DOM-representation out of the multiparts", 306 ExceptionCode.INVALID_FORMAT ), false ); 307 return; 308 } 309 } else {// not a mime-multipart 310 try { 311 resultingRequest.load( requestWrapper.getInputStream(), XMLFragment.DEFAULT_URL ); 312 } catch ( XMLException e ) { 313 LOG.logError( e.getMessage(), e ); 314 sendException( response, 315 new OGCWebServiceException( "An error occurred while parsing request: " 316 + e.getMessage(), ExceptionCode.INVALID_FORMAT ), false ); 317 return; 318 } catch ( SAXException e ) { 319 LOG.logError( e.getMessage(), e ); 320 sendException( response, 321 new OGCWebServiceException( "An error occurred while parsing request: " 322 + e.getMessage(), ExceptionCode.INVALID_FORMAT ), false ); 323 return; 324 } 325 } 326 if ( resultingRequest.getRootElement() == null ) { 327 sendException( 328 response, 329 new OGCWebServiceException( 330 "Could not validate your request, please check your parameters.", 331 ExceptionCode.INVALID_FORMAT ), false ); 332 return; 333 334 } 335 336 String s = resultingRequest.getRootElement().getNamespaceURI(); 337 LOG.logDebug( "Namespace of root element: " + s ); 338 339 // checking if the root elements node name equals the root name of a SOAP message 340 // document. If so the SOAP 341 // body must be accessed to be forwarded to the the filter/servlet 342 boolean usingSoap = s.equals( W3SOAP_ENVELOPE_1_1.toASCIIString() ); 343 if ( usingSoap ) { 344 try { 345 resultingRequest = handleSOAPRequest( resultingRequest ); 346 } catch ( XMLParsingException e ) { 347 LOG.logError( e.getMessage(), e ); 348 sendException( response, 349 new OGCWebServiceException( e.getMessage(), ExceptionCode.INVALID_FORMAT ), 350 usingSoap ); 351 return; 352 } catch ( OGCWebServiceException e ) { 353 LOG.logError( e.getMessage(), e ); 354 sendException( response, e, usingSoap ); 355 return; 356 } 357 } else if ( onlyExceptSoap ) { 358 sendException( 359 response, 360 new OGCWebServiceException( 361 "This service only excepts soap version 1.1 encoded requests.", 362 ExceptionCode.INVALID_FORMAT ), false ); 363 return; 364 } 365 366 if ( usingMultiparts ) { 367 // append the multiparts to the root request which is stripped of any soap envelope. 368 Document doc = resultingRequest.getRootElement().getOwnerDocument(); 369 for ( XMLFragment multipart : mimeParts ) { 370 if ( multipart != null ) { 371 Element rootElement = multipart.getRootElement(); 372 if ( rootElement != null ) { 373 String nameID = rootElement.getAttribute( "originalNameID" ); 374 if ( nameID != null && !"".equals( nameID ) ) { 375 Element parent = multiPartHandler.getElementForId( resultingRequest, nameID ); 376 if ( parent == null ) { 377 LOG.logError( "No element was given to append the multipart node with id: " 378 + nameID ); 379 sendException( response, 380 new OGCWebServiceException( 381 "An error occurred while processing multipart with id: " 382 + nameID, 383 ExceptionCode.INTERNAL_SERVER_ERROR ), 384 usingSoap ); 385 return; 386 } 387 Element imported = (Element) doc.importNode( multipart.getRootElement(), true ); 388 parent.appendChild( imported ); 389 } else { 390 LOG.logError( "No nameID found in the originalNameID attribute, this is strange!!" ); 391 } 392 } else { 393 LOG.logError( "One of the mime multiparts does not contain a root element, this is strange!!" ); 394 } 395 } else { 396 LOG.logError( "One of the mime multiparts is null, this is strange!!" ); 397 } 398 } 399 } 400 401 // the original request has been changed, set the request accordingly. Deegree will be 402 // able to handle it. 403 if ( usingSoap || usingMultiparts ) { 404 ByteArrayOutputStream bos = new ByteArrayOutputStream( 50000 ); 405 String encoding = requestWrapper.getCharacterEncoding(); 406 if ( encoding == null ) { 407 encoding = CharsetUtils.getSystemCharset(); 408 } 409 OutputStreamWriter osw = new OutputStreamWriter( bos, encoding ); 410 resultingRequest.write( osw ); 411 requestWrapper.setInputStreamAsByteArray( bos.toByteArray() ); 412 } 413 ServletResponseWrapper responseWrapper = new ServletResponseWrapper( (HttpServletResponse) response ); 414 chain.doFilter( requestWrapper, responseWrapper ); 415 if ( usingMultiparts ) { 416 // send response using multiparts. 417 LOG.logInfo( "Sending multiparted response is not supported yet" ); 418 } 419 if ( usingSoap ) { 420 try { 421 createSoapResponse( responseWrapper ); 422 } catch ( OGCWebServiceException e ) { 423 LOG.logError( e.getMessage(), e ); 424 sendException( responseWrapper, e, usingSoap ); 425 return; 426 } 427 } 428 OutputStream os = responseWrapper.getOutputStream(); 429 String encoding = requestWrapper.getCharacterEncoding(); 430 LOG.logDebug( "The request uses following character encoding: " + encoding ); 431 if ( !CharsetUtils.getSystemCharset().equals( encoding ) ) { 432 LOG.logDebug( "The request uses following character encoding: " + encoding 433 + " setting to CharsetUtils.getSystemCharsset: " + CharsetUtils.getSystemCharset() ); 434 encoding = CharsetUtils.getSystemCharset(); 435 } 436 String responseString = ( (ServletResponseWrapper.ProxyServletOutputStream) os ).toString( encoding ); 437 os.close(); 438 if ( LOG.isDebug() ) { 439 LOG.logDebug( "Responding with: " + responseString ); 440 } 441 PrintWriter writer = response.getWriter(); 442 writer.write( responseString ); 443 writer.flush(); 444 writer.close(); 445 } 446 447 } 448 449 /** 450 * @param responseWrapper 451 * @throws OGCWebServiceException 452 * representing the exception which was found inside the response 453 */ 454 private void createSoapResponse( ServletResponseWrapper responseWrapper ) 455 throws OGCWebServiceException { 456 String contentType = responseWrapper.getContentType(); 457 LOG.logDebug( "Creating soap response with content type: " + contentType ); 458 if ( contentType != null ) { 459 if ( contentType.contains( "xml" ) || contentType.contains( "gml" ) ) { 460 try { 461 OutputStream os = responseWrapper.getOutputStream(); 462 String responseString = ( (ServletResponseWrapper.ProxyServletOutputStream) os ).toString( CharsetUtils.getSystemCharset() ); 463 XMLFragment responseTree = new XMLFragment( new StringReader( responseString ), 464 XMLFragment.DEFAULT_URL ); 465 LOG.logDebug( "The original response was (which will be wrapped): " 466 + responseTree.getAsPrettyString() ); 467 Element root = responseTree.getRootElement(); 468 if ( root.getLocalName().toLowerCase().contains( "exception" ) ) { 469 responseWrapper.reset(); 470 throw new OGCWebServiceException( responseTree.getAsPrettyString(), ExceptionCode.SOAP_SERVER ); 471 } 472 responseWrapper.setContentType( "text/xml" ); 473 Document doc = XMLTools.create(); 474 Element responseRoot = doc.createElementNS( W3SOAP_ENVELOPE_1_1.toASCIIString(), W3SOAP_1_1_PREFIX 475 + ":Envelope" ); 476 Element body = appendElement( responseRoot, W3SOAP_ENVELOPE_1_1, W3SOAP_1_1_PREFIX + ":Body" ); 477 Node result = doc.importNode( root, true ); 478 body.appendChild( result ); 479 responseTree = new XMLFragment( responseRoot ); 480 if ( LOG.isDebug() ) { 481 LOG.logDebug( "The soap-response will be: " + responseTree.getAsPrettyString() ); 482 } 483 responseWrapper.reset(); 484 os = responseWrapper.getOutputStream(); 485 responseTree.prettyPrint( os ); 486 } catch ( IOException e ) { 487 LOG.logError( e.getMessage(), e ); 488 throw new OGCWebServiceException( 489 "Following error occurred while creating a soap envelope of the service response: " 490 + e.getMessage(), 491 ExceptionCode.SOAP_SERVER ); 492 } catch ( SAXException e ) { 493 LOG.logError( e.getMessage(), e ); 494 throw new OGCWebServiceException( 495 "Following error occurred while creating a soap envelope of the service response: " 496 + e.getMessage(), 497 ExceptionCode.SOAP_SERVER ); 498 } catch ( TransformerException e ) { 499 LOG.logError( e.getMessage(), e ); 500 throw new OGCWebServiceException( 501 "Following error occurred while creating a soap envelope of the service response: " 502 + e.getMessage(), 503 ExceptionCode.SOAP_SERVER ); 504 } 505 } else { 506 LOG.logInfo( "Response did not contain a known xml contentype, therefore it cannot be embedded in a soap envelope" ); 507 } 508 } else { 509 LOG.logInfo( "Response did not contain a known xml contentype, therefore it cannot be embedded in a soap envelope" ); 510 } 511 // responseWrapper.reset(); 512 } 513 514 /** 515 * @param response 516 * to write to. 517 * @throws IOException 518 * if a given exception could not be written to the stream 519 * @throws OGCWebServiceException 520 * if no wsdl file was given. 521 */ 522 private void sendWSDL( ServletResponse response ) 523 throws IOException, OGCWebServiceException { 524 if ( wsdlDescription == null ) { 525 throw new OGCWebServiceException( "No wsdl description document available.", 526 ExceptionCode.INTERNAL_SERVER_ERROR ); 527 } 528 response.setCharacterEncoding( CharsetUtils.getSystemCharset() ); 529 response.setContentType( "application/xml" ); 530 PrintWriter writer = response.getWriter(); 531 writer.write( wsdlDescription.getAsPrettyString() ); 532 writer.flush(); 533 writer.close(); 534 535 } 536 537 /** 538 * Handles a SOAP 1.1. envelope request. The given xml-dom tree will be traversed and the content of the body will 539 * be returned. 540 * 541 * @param xmlReq 542 * the xml-dom representation of the original request, it should be a soap-envelope bound to the 543 * namespace: http://schemas.xmlsoap.org/soap/envelope/ 544 * @return the contents of the soap-body never <code>null</code> 545 * @throws XMLParsingException 546 * if the body could not be parsed 547 * @throws OGCWebServiceException 548 * if one of the header elements was not configured to be understood. 549 * @throws IllegalArgumentException 550 * if the xmlReq is <code>null</code> 551 */ 552 protected XMLFragment handleSOAPRequest( XMLFragment xmlReq ) 553 throws XMLParsingException, OGCWebServiceException, IllegalArgumentException { 554 if ( xmlReq == null ) { 555 throw new IllegalArgumentException( "The xmlReq element may not be null" ); 556 } 557 LOG.logDebug( "Handling SOAP request" ); 558 // check header elements for mustUnderstand attributes. 559 if ( LOG.isDebug() ) { 560 LOG.logDebug( xmlReq.getAsPrettyString() ); 561 } 562 Element rootElement = xmlReq.getRootElement(); 563 if ( rootElement == null ) { 564 throw new OGCWebServiceException( 565 "The request does not contain a root node, hence the request cannot be handled." ); 566 } 567 checkMustUnderstandAttributes( getElement( xmlReq.getRootElement(), W3SOAP_1_1_PREFIX + ":Header", nsContext ) ); 568 Element elem = XMLTools.getRequiredElement( xmlReq.getRootElement(), W3SOAP_1_1_PREFIX + ":Body", nsContext ); 569 // use first child element 570 elem = XMLTools.getElement( elem, "*[1]", nsContext ); 571 // extract SOAPBody 572 Document doc = XMLTools.create(); 573 Element root = (Element) doc.importNode( elem, true ); 574 XMLFragment result = new XMLFragment( root ); 575 576 if ( LOG.isDebug() ) { 577 LOG.logDebug( "Extracted request", result.getAsPrettyString() ); 578 } 579 580 return result; 581 } 582 583 /** 584 * Check all direct children of the given headerElement for the mustUndertand attribute. If one is set to '1' the 585 * {namespace}:localName will be checked against the configured names. If such an Element is not configured as 586 * understandable an SOAP-Fault will be sent, as described in the soap 1.1 specification. 587 * 588 * @param headerElement 589 * which top-level child-nodes will be checked for mustUnderstand attributes. If <code>null</code> 590 * nothing will be done. 591 * @throws OGCWebServiceException 592 * if one of the found children was not configured to be understood. 593 * @throws XMLParsingException 594 * if an error occurs while retrieving the child elements of the headerelement 595 */ 596 protected void checkMustUnderstandAttributes( Element headerElement ) 597 throws OGCWebServiceException, XMLParsingException { 598 if ( headerElement != null ) { 599 List<Element> children = getElements( headerElement, "*", nsContext ); 600 for ( Element child : children ) { 601 if ( child != null ) { 602 String mustUnderstand = child.getAttributeNS( W3SOAP_ENVELOPE_1_1.toASCIIString(), "mustUnderstand" ); 603 if ( mustUnderstand != null && !"".equals( mustUnderstand.trim() ) ) { 604 if ( "1".equals( mustUnderstand ) ) { 605 StringBuilder sb = new StringBuilder( 200 ); 606 String namespace = child.getNamespaceURI(); 607 if ( namespace != null ) { 608 sb.append( "{" ).append( namespace ).append( "}:" ); 609 } 610 sb.append( child.getLocalName() ); 611 if ( !soapUnderstanding.contains( sb.toString().trim() ) ) { 612 throw new OGCWebServiceException( "The element: " + sb.toString() 613 + " is not understood by this SOAP-Server.", 614 ExceptionCode.SOAP_MUST_UNDERSTAND ); 615 } 616 617 } 618 } 619 620 } 621 } 622 } 623 } 624 625 public void destroy() { 626 // implements nottin. 627 } 628 629 /** 630 * Sends the passed <tt>OGCWebServiceException</tt> to the calling client and flushes/closes the writer. 631 * 632 * @param response 633 * to write the exception message to. 634 * @param e 635 * the exception to 'send' e.g. write to the stream. 636 * @param usingSoap 637 * true if the exception should be wrapped inside a soap body. 638 * @throws IOException 639 * if an error occurred while getting the writer of the response. 640 */ 641 protected void sendException( ServletResponse response, OGCWebServiceException e, boolean usingSoap ) 642 throws IOException { 643 if ( LOG.isDebug() ) { 644 Thread.dumpStack(); 645 } 646 if ( response instanceof ServletResponseWrapper ) { 647 ( (ServletResponseWrapper) response ).reset(); 648 } 649 Document doc = XMLTools.create(); 650 XMLFragment errorResponse = null; 651 ExceptionCode code = e.getCode(); 652 String exceptionCode = ExceptionCode.NOAPPLICABLECODE.value; 653 if ( code != null && code.value != null ) { 654 exceptionCode = code.value; 655 } 656 657 if ( usingSoap ) { 658 // the specification says following content-type 659 response.setContentType( "text/xml" ); 660 Element root = doc.createElementNS( W3SOAP_ENVELOPE_1_1.toASCIIString(), W3SOAP_1_1_PREFIX + ":Envelope" ); 661 Element body = appendElement( root, W3SOAP_ENVELOPE_1_1, W3SOAP_1_1_PREFIX + ":Body" ); 662 Element fault = appendElement( body, W3SOAP_ENVELOPE_1_1, W3SOAP_1_1_PREFIX + ":Fault" ); 663 if ( !( "VersionMismatch".equalsIgnoreCase( exceptionCode ) 664 || "MustUnderStand".equalsIgnoreCase( exceptionCode ) || "Client".equalsIgnoreCase( exceptionCode ) || "Server".equalsIgnoreCase( exceptionCode ) ) ) { 665 exceptionCode = "Server." + exceptionCode; 666 } else { 667 // if a soap error occurred set the internal server error 500. 668 ( (HttpServletResponse) response ).setStatus( HttpServletResponse.SC_INTERNAL_SERVER_ERROR ); 669 } 670 appendElement( fault, W3SOAP_ENVELOPE_1_1, W3SOAP_1_1_PREFIX + ":faultcode", exceptionCode ); 671 String message = e.getMessage(); 672 if ( message != null && !"".equals( message.trim() ) ) { 673 // this is definitely an xml file, please put it in the detailed section. 674 if ( message.startsWith( "<?xml" ) ) { 675 Element detail = appendElement( fault, W3SOAP_ENVELOPE_1_1, W3SOAP_1_1_PREFIX + ":detail" ); 676 try { 677 XMLFragment errorMessage = new XMLFragment( new StringReader( message ), 678 XMLFragment.DEFAULT_URL ); 679 Node imported = doc.importNode( errorMessage.getRootElement(), true ); 680 detail.appendChild( imported ); 681 } catch ( SAXException e1 ) { 682 LOG.logError( e1.getMessage(), e1 ); 683 sendException( 684 response, 685 new OGCWebServiceException( 686 "The server responded with an error message, but unable to create a valid soap response from it.", 687 ExceptionCode.SOAP_SERVER ), false ); 688 return; 689 } 690 } else { 691 appendElement( fault, W3SOAP_ENVELOPE_1_1, W3SOAP_1_1_PREFIX + ":faultstring", message ); 692 } 693 } 694 errorResponse = new XMLFragment( root ); 695 } else { 696 LOG.logInfo( "Sending OGCWebServiceException to client with message: ." + e.getMessage() ); 697 response.setContentType( "application/xml" ); 698 errorResponse = new XMLFragment( doc.createElementNS( CommonNamespaces.OWSNS.toASCIIString(), 699 "ows:ExceptionReport" ) ); 700 Element errorMessage = XMLTools.appendElement( errorResponse.getRootElement(), defaultErrorNamespace, 701 exceptionCode ); 702 XMLTools.setNodeValue( errorMessage, e.getMessage() ); 703 } 704 PrintWriter writer = response.getWriter(); 705 writer.write( errorResponse.getAsPrettyString() ); 706 writer.flush(); 707 writer.close(); 708 } 709 }