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 public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) 216 throws IOException, ServletException { 217 ServletRequestWrapper requestWrapper = null; 218 if ( request instanceof ServletRequestWrapper ) { 219 LOG.logDebug( "the incoming request is actually an org.deegree.enterprise.servlet.RequestWrapper, so not creating new instance." ); 220 requestWrapper = (ServletRequestWrapper) request; 221 } else { 222 requestWrapper = new ServletRequestWrapper( (HttpServletRequest) request ); 223 } 224 if ( requestWrapper.getMethod().equalsIgnoreCase( "GET" ) ) { 225 // check for the wsdl parameter, if it is given, return the wsdl (Web Services 226 // Description Language 227 // (WSDL) 1.1) for this service. 228 Map<String, String[]> params = requestWrapper.getParameterMap(); 229 if ( params != null ) { 230 if ( params.containsKey( "wsdl" ) ) { 231 if ( params.keySet().size() > 1 ) { 232 sendException( 233 response, 234 new OGCWebServiceException( 235 "If the wsdl keyword is supplied, no other parameters are allowed.", 236 ExceptionCode.INVALIDPARAMETERVALUE ), false ); 237 } else { 238 try { 239 sendWSDL( response ); 240 } catch ( OGCWebServiceException e ) { 241 LOG.logError( e.getMessage(), e ); 242 sendException( response, e, false ); 243 } 244 } 245 return; 246 } 247 } 248 if ( !onlyExceptSoap ) { 249 chain.doFilter( requestWrapper, response ); 250 } else { 251 sendException( 252 response, 253 new OGCWebServiceException( 254 "This service only excepts soap version 1.1 encoded requests.", 255 ExceptionCode.INVALID_FORMAT ), false ); 256 } 257 } else { 258 response.setCharacterEncoding( CharsetUtils.getSystemCharset() ); 259 260 BufferedReader reader = new BufferedReader( new InputStreamReader( requestWrapper.getInputStream() ) ); 261 String firstLine = reader.readLine(); 262 LOG.logDebug( "first line of request: " + firstLine ); 263 if ( firstLine == null ) { 264 LOG.logInfo( "No request characters found, not handling request" ); 265 // chain.doFilter( requestWrapper, response ); 266 sendException( response, 267 new OGCWebServiceException( "No request characters found, not handling request", 268 ExceptionCode.INVALIDPARAMETERVALUE ), false ); 269 return; 270 } 271 if ( LOG.isDebug() ) { 272 LOG.logDebug( "OUTPUTING as Strings" ); 273 LOG.logDebug( firstLine ); 274 while ( reader.ready() ) { 275 LOG.logDebug( reader.readLine() ); 276 } 277 } 278 279 LOG.logDebug( "Contentype of the request: " + requestWrapper.getContentType() ); 280 281 // These values will be set according to the request properties 282 boolean usingMultiparts = ( requestWrapper.getContentType() != null ) 283 && ( requestWrapper.getContentType().contains( "multipart/form-data" ) ) 284 && multiPartHandler != null; 285 XMLFragment resultingRequest = new XMLFragment(); 286 XMLFragment[] mimeParts = null; 287 if ( usingMultiparts ) { 288 // because we have some multiparts, we will insert them into the request body 289 try { 290 mimeParts = multiPartHandler.handleMultiparts( requestWrapper ); 291 if ( mimeParts.length > 0 ) { 292 resultingRequest = mimeParts[0]; 293 } 294 } catch ( OGCWebServiceException e ) { 295 LOG.logError( e.getMessage(), e ); 296 sendException( response, e, false ); 297 return; 298 } 299 if ( resultingRequest == null ) { 300 LOG.logDebug( "could not generate an xml-dom representation out of the multiparts, returning an error message" ); 301 sendException( 302 response, 303 new OGCWebServiceException( 304 "Could not generate an XML-DOM-representation out of the multiparts", 305 ExceptionCode.INVALID_FORMAT ), false ); 306 return; 307 } 308 } else {// not a mime-multipart 309 try { 310 resultingRequest.load( requestWrapper.getInputStream(), XMLFragment.DEFAULT_URL ); 311 } catch ( XMLException e ) { 312 LOG.logError( e.getMessage(), e ); 313 sendException( response, 314 new OGCWebServiceException( "An error occurred while parsing request: " 315 + e.getMessage(), ExceptionCode.INVALID_FORMAT ), false ); 316 return; 317 } catch ( SAXException e ) { 318 LOG.logError( e.getMessage(), e ); 319 sendException( response, 320 new OGCWebServiceException( "An error occurred while parsing request: " 321 + e.getMessage(), ExceptionCode.INVALID_FORMAT ), false ); 322 return; 323 } 324 } 325 if ( resultingRequest.getRootElement() == null ) { 326 sendException( 327 response, 328 new OGCWebServiceException( 329 "Could not validate your request, please check your parameters.", 330 ExceptionCode.INVALID_FORMAT ), false ); 331 return; 332 333 } 334 335 String s = resultingRequest.getRootElement().getNamespaceURI(); 336 LOG.logDebug( "Namespace of root element: " + s ); 337 338 // checking if the root elements node name equals the root name of a SOAP message 339 // document. If so the SOAP 340 // body must be accessed to be forwarded to the the filter/servlet 341 boolean usingSoap = s.equals( W3SOAP_ENVELOPE_1_1.toASCIIString() ); 342 if ( usingSoap ) { 343 try { 344 resultingRequest = handleSOAPRequest( resultingRequest ); 345 } catch ( XMLParsingException e ) { 346 LOG.logError( e.getMessage(), e ); 347 sendException( response, 348 new OGCWebServiceException( e.getMessage(), ExceptionCode.INVALID_FORMAT ), 349 usingSoap ); 350 return; 351 } catch ( OGCWebServiceException e ) { 352 LOG.logError( e.getMessage(), e ); 353 sendException( response, e, usingSoap ); 354 return; 355 } 356 } else if ( onlyExceptSoap ) { 357 sendException( 358 response, 359 new OGCWebServiceException( 360 "This service only excepts soap version 1.1 encoded requests.", 361 ExceptionCode.INVALID_FORMAT ), false ); 362 return; 363 } 364 365 if ( usingMultiparts ) { 366 // append the multiparts to the root request which is stripped of any soap envelope. 367 Document doc = resultingRequest.getRootElement().getOwnerDocument(); 368 for ( XMLFragment multipart : mimeParts ) { 369 if ( multipart != null ) { 370 Element rootElement = multipart.getRootElement(); 371 if ( rootElement != null ) { 372 String nameID = rootElement.getAttribute( "originalNameID" ); 373 if ( nameID != null && !"".equals( nameID ) ) { 374 Element parent = multiPartHandler.getElementForId( resultingRequest, nameID ); 375 if ( parent == null ) { 376 LOG.logError( "No element was given to append the multipart node with id: " 377 + nameID ); 378 sendException( response, 379 new OGCWebServiceException( 380 "An error occurred while processing multipart with id: " 381 + nameID, 382 ExceptionCode.INTERNAL_SERVER_ERROR ), 383 usingSoap ); 384 return; 385 } 386 Element imported = (Element) doc.importNode( multipart.getRootElement(), true ); 387 parent.appendChild( imported ); 388 } else { 389 LOG.logError( "No nameID found in the originalNameID attribute, this is strange!!" ); 390 } 391 } else { 392 LOG.logError( "One of the mime multiparts does not contain a root element, this is strange!!" ); 393 } 394 } else { 395 LOG.logError( "One of the mime multiparts is null, this is strange!!" ); 396 } 397 } 398 } 399 400 // the original request has been changed, set the request accordingly. Deegree will be 401 // able to handle it. 402 if ( usingSoap || usingMultiparts ) { 403 ByteArrayOutputStream bos = new ByteArrayOutputStream( 50000 ); 404 String encoding = requestWrapper.getCharacterEncoding(); 405 if ( encoding == null ) { 406 encoding = CharsetUtils.getSystemCharset(); 407 } 408 OutputStreamWriter osw = new OutputStreamWriter( bos, encoding ); 409 resultingRequest.write( osw ); 410 requestWrapper.setInputStreamAsByteArray( bos.toByteArray() ); 411 } 412 ServletResponseWrapper responseWrapper = new ServletResponseWrapper( (HttpServletResponse) response ); 413 chain.doFilter( requestWrapper, responseWrapper ); 414 if ( usingMultiparts ) { 415 // send response using multiparts. 416 LOG.logInfo( "Sending multiparted response is not supported yet" ); 417 } 418 if ( usingSoap ) { 419 try { 420 createSoapResponse( responseWrapper ); 421 } catch ( OGCWebServiceException e ) { 422 LOG.logError( e.getMessage(), e ); 423 sendException( responseWrapper, e, usingSoap ); 424 return; 425 } 426 } 427 OutputStream os = responseWrapper.getOutputStream(); 428 String encoding = requestWrapper.getCharacterEncoding(); 429 LOG.logDebug( "The request uses following character encoding: " + encoding ); 430 if ( !CharsetUtils.getSystemCharset().equals( encoding ) ) { 431 LOG.logDebug( "The request uses following character encoding: " + encoding 432 + " setting to CharsetUtils.getSystemCharsset: " + CharsetUtils.getSystemCharset() ); 433 encoding = CharsetUtils.getSystemCharset(); 434 } 435 String responseString = ( (ServletResponseWrapper.ProxyServletOutputStream) os ).toString( encoding ); 436 os.close(); 437 if ( LOG.isDebug() ) { 438 LOG.logDebug( "Responding with: " + responseString ); 439 } 440 PrintWriter writer = response.getWriter(); 441 writer.write( responseString ); 442 writer.flush(); 443 writer.close(); 444 } 445 446 } 447 448 /** 449 * @param responseWrapper 450 * @throws OGCWebServiceException 451 * representing the exception which was found inside the response 452 */ 453 private void createSoapResponse( ServletResponseWrapper responseWrapper ) 454 throws OGCWebServiceException { 455 String contentType = responseWrapper.getContentType(); 456 LOG.logDebug( "Creating soap response with content type: " + contentType ); 457 if ( contentType != null ) { 458 if ( contentType.contains( "xml" ) || contentType.contains( "gml" ) ) { 459 try { 460 OutputStream os = responseWrapper.getOutputStream(); 461 String responseString = ( (ServletResponseWrapper.ProxyServletOutputStream) os ).toString( CharsetUtils.getSystemCharset() ); 462 XMLFragment responseTree = new XMLFragment( new StringReader( responseString ), 463 XMLFragment.DEFAULT_URL ); 464 LOG.logDebug( "The original response was (which will be wrapped): " 465 + responseTree.getAsPrettyString() ); 466 Element root = responseTree.getRootElement(); 467 if ( root.getLocalName().toLowerCase().contains( "exception" ) ) { 468 responseWrapper.reset(); 469 throw new OGCWebServiceException( responseTree.getAsPrettyString(), ExceptionCode.SOAP_SERVER ); 470 } 471 responseWrapper.setContentType( "text/xml" ); 472 Document doc = XMLTools.create(); 473 Element responseRoot = doc.createElementNS( W3SOAP_ENVELOPE_1_1.toASCIIString(), W3SOAP_1_1_PREFIX 474 + ":Envelope" ); 475 Element body = appendElement( responseRoot, W3SOAP_ENVELOPE_1_1, W3SOAP_1_1_PREFIX + ":Body" ); 476 Node result = doc.importNode( root, true ); 477 body.appendChild( result ); 478 responseTree = new XMLFragment( responseRoot ); 479 if ( LOG.isDebug() ) { 480 LOG.logDebug( "The soap-response will be: " + responseTree.getAsPrettyString() ); 481 } 482 responseWrapper.reset(); 483 os = responseWrapper.getOutputStream(); 484 responseTree.prettyPrint( os ); 485 } catch ( IOException e ) { 486 LOG.logError( e.getMessage(), e ); 487 throw new OGCWebServiceException( 488 "Following error occurred while creating a soap envelope of the service response: " 489 + e.getMessage(), 490 ExceptionCode.SOAP_SERVER ); 491 } catch ( SAXException e ) { 492 LOG.logError( e.getMessage(), e ); 493 throw new OGCWebServiceException( 494 "Following error occurred while creating a soap envelope of the service response: " 495 + e.getMessage(), 496 ExceptionCode.SOAP_SERVER ); 497 } catch ( TransformerException e ) { 498 LOG.logError( e.getMessage(), e ); 499 throw new OGCWebServiceException( 500 "Following error occurred while creating a soap envelope of the service response: " 501 + e.getMessage(), 502 ExceptionCode.SOAP_SERVER ); 503 } 504 } else { 505 LOG.logInfo( "Response did not contain a known xml contentype, therefore it cannot be embedded in a soap envelope" ); 506 } 507 } else { 508 LOG.logInfo( "Response did not contain a known xml contentype, therefore it cannot be embedded in a soap envelope" ); 509 } 510 // responseWrapper.reset(); 511 } 512 513 /** 514 * @param response 515 * to write to. 516 * @throws IOException 517 * if a given exception could not be written to the stream 518 * @throws OGCWebServiceException 519 * if no wsdl file was given. 520 */ 521 private void sendWSDL( ServletResponse response ) 522 throws IOException, OGCWebServiceException { 523 if ( wsdlDescription == null ) { 524 throw new OGCWebServiceException( "No wsdl description document available.", 525 ExceptionCode.INTERNAL_SERVER_ERROR ); 526 } 527 response.setCharacterEncoding( CharsetUtils.getSystemCharset() ); 528 response.setContentType( "application/xml" ); 529 PrintWriter writer = response.getWriter(); 530 writer.write( wsdlDescription.getAsPrettyString() ); 531 writer.flush(); 532 writer.close(); 533 534 } 535 536 /** 537 * Handles a SOAP 1.1. envelope request. The given xml-dom tree will be traversed and the content of the body will 538 * be returned. 539 * 540 * @param xmlReq 541 * the xml-dom representation of the original request, it should be a soap-envelope bound to the 542 * namespace: http://schemas.xmlsoap.org/soap/envelope/ 543 * @return the contents of the soap-body never <code>null</code> 544 * @throws XMLParsingException 545 * if the body could not be parsed 546 * @throws OGCWebServiceException 547 * if one of the header elements was not configured to be understood. 548 * @throws IllegalArgumentException 549 * if the xmlReq is <code>null</code> 550 */ 551 protected XMLFragment handleSOAPRequest( XMLFragment xmlReq ) 552 throws XMLParsingException, OGCWebServiceException, IllegalArgumentException { 553 if ( xmlReq == null ) { 554 throw new IllegalArgumentException( "The xmlReq element may not be null" ); 555 } 556 LOG.logDebug( "Handling SOAP request" ); 557 // check header elements for mustUnderstand attributes. 558 if ( LOG.isDebug() ) { 559 LOG.logDebug( xmlReq.getAsPrettyString() ); 560 } 561 Element rootElement = xmlReq.getRootElement(); 562 if ( rootElement == null ) { 563 throw new OGCWebServiceException( 564 "The request does not contain a root node, hence the request cannot be handled." ); 565 } 566 checkMustUnderstandAttributes( getElement( xmlReq.getRootElement(), W3SOAP_1_1_PREFIX + ":Header", nsContext ) ); 567 Element elem = XMLTools.getRequiredElement( xmlReq.getRootElement(), W3SOAP_1_1_PREFIX + ":Body", nsContext ); 568 // use first child element 569 elem = XMLTools.getElement( elem, "*[1]", nsContext ); 570 // extract SOAPBody 571 Document doc = XMLTools.create(); 572 Element root = (Element) doc.importNode( elem, true ); 573 XMLFragment result = new XMLFragment( root ); 574 575 if ( LOG.isDebug() ) { 576 LOG.logDebug( "Extracted request", result.getAsPrettyString() ); 577 } 578 579 return result; 580 } 581 582 /** 583 * Check all direct children of the given headerElement for the mustUndertand attribute. If one is set to '1' the 584 * {namespace}:localName will be checked against the configured names. If such an Element is not configured as 585 * understandable an SOAP-Fault will be sent, as described in the soap 1.1 specification. 586 * 587 * @param headerElement 588 * which top-level child-nodes will be checked for mustUnderstand attributes. If <code>null</code> 589 * nothing will be done. 590 * @throws OGCWebServiceException 591 * if one of the found children was not configured to be understood. 592 * @throws XMLParsingException 593 * if an error occurs while retrieving the child elements of the headerelement 594 */ 595 protected void checkMustUnderstandAttributes( Element headerElement ) 596 throws OGCWebServiceException, XMLParsingException { 597 if ( headerElement != null ) { 598 List<Element> children = getElements( headerElement, "*", nsContext ); 599 for ( Element child : children ) { 600 if ( child != null ) { 601 String mustUnderstand = child.getAttributeNS( W3SOAP_ENVELOPE_1_1.toASCIIString(), "mustUnderstand" ); 602 if ( mustUnderstand != null && !"".equals( mustUnderstand.trim() ) ) { 603 if ( "1".equals( mustUnderstand ) ) { 604 StringBuilder sb = new StringBuilder( 200 ); 605 String namespace = child.getNamespaceURI(); 606 if ( namespace != null ) { 607 sb.append( "{" ).append( namespace ).append( "}:" ); 608 } 609 sb.append( child.getLocalName() ); 610 if ( !soapUnderstanding.contains( sb.toString().trim() ) ) { 611 throw new OGCWebServiceException( "The element: " + sb.toString() 612 + " is not understood by this SOAP-Server.", 613 ExceptionCode.SOAP_MUST_UNDERSTAND ); 614 } 615 616 } 617 } 618 619 } 620 } 621 } 622 } 623 624 public void destroy() { 625 // implements nottin. 626 } 627 628 /** 629 * Sends the passed <tt>OGCWebServiceException</tt> to the calling client and flushes/closes the writer. 630 * 631 * @param response 632 * to write the exception message to. 633 * @param e 634 * the exception to 'send' e.g. write to the stream. 635 * @param usingSoap 636 * true if the exception should be wrapped inside a soap body. 637 * @throws IOException 638 * if an error occurred while getting the writer of the response. 639 */ 640 protected void sendException( ServletResponse response, OGCWebServiceException e, boolean usingSoap ) 641 throws IOException { 642 if ( LOG.isDebug() ) { 643 Thread.dumpStack(); 644 } 645 if ( response instanceof ServletResponseWrapper ) { 646 ( (ServletResponseWrapper) response ).reset(); 647 } 648 Document doc = XMLTools.create(); 649 XMLFragment errorResponse = null; 650 ExceptionCode code = e.getCode(); 651 String exceptionCode = ExceptionCode.NOAPPLICABLECODE.value; 652 if ( code != null && code.value != null ) { 653 exceptionCode = code.value; 654 } 655 656 if ( usingSoap ) { 657 // the specification says following content-type 658 response.setContentType( "text/xml" ); 659 Element root = doc.createElementNS( W3SOAP_ENVELOPE_1_1.toASCIIString(), W3SOAP_1_1_PREFIX + ":Envelope" ); 660 Element body = appendElement( root, W3SOAP_ENVELOPE_1_1, W3SOAP_1_1_PREFIX + ":Body" ); 661 Element fault = appendElement( body, W3SOAP_ENVELOPE_1_1, W3SOAP_1_1_PREFIX + ":Fault" ); 662 if ( !( "VersionMismatch".equalsIgnoreCase( exceptionCode ) 663 || "MustUnderStand".equalsIgnoreCase( exceptionCode ) || "Client".equalsIgnoreCase( exceptionCode ) || "Server".equalsIgnoreCase( exceptionCode ) ) ) { 664 exceptionCode = "Server." + exceptionCode; 665 } else { 666 // if a soap error occurred set the internal server error 500. 667 ( (HttpServletResponse) response ).setStatus( HttpServletResponse.SC_INTERNAL_SERVER_ERROR ); 668 } 669 appendElement( fault, W3SOAP_ENVELOPE_1_1, W3SOAP_1_1_PREFIX + ":faultcode", exceptionCode ); 670 String message = e.getMessage(); 671 if ( message != null && !"".equals( message.trim() ) ) { 672 // this is definitely an xml file, please put it in the detailed section. 673 if ( message.startsWith( "<?xml" ) ) { 674 Element detail = appendElement( fault, W3SOAP_ENVELOPE_1_1, W3SOAP_1_1_PREFIX + ":detail" ); 675 try { 676 XMLFragment errorMessage = new XMLFragment( new StringReader( message ), 677 XMLFragment.DEFAULT_URL ); 678 Node imported = doc.importNode( errorMessage.getRootElement(), true ); 679 detail.appendChild( imported ); 680 } catch ( SAXException e1 ) { 681 LOG.logError( e1.getMessage(), e1 ); 682 sendException( 683 response, 684 new OGCWebServiceException( 685 "The server responded with an error message, but unable to create a valid soap response from it.", 686 ExceptionCode.SOAP_SERVER ), false ); 687 return; 688 } 689 } else { 690 appendElement( fault, W3SOAP_ENVELOPE_1_1, W3SOAP_1_1_PREFIX + ":faultstring", message ); 691 } 692 } 693 errorResponse = new XMLFragment( root ); 694 } else { 695 LOG.logInfo( "Sending OGCWebServiceException to client with message: ." + e.getMessage() ); 696 response.setContentType( "application/xml" ); 697 errorResponse = new XMLFragment( doc.createElementNS( CommonNamespaces.OWSNS.toASCIIString(), 698 "ows:ExceptionReport" ) ); 699 Element errorMessage = XMLTools.appendElement( errorResponse.getRootElement(), defaultErrorNamespace, 700 exceptionCode ); 701 XMLTools.setNodeValue( errorMessage, e.getMessage() ); 702 } 703 PrintWriter writer = response.getWriter(); 704 writer.write( errorResponse.getAsPrettyString() ); 705 writer.flush(); 706 writer.close(); 707 } 708 }