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    }