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    }