001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/enterprise/servlet/SOAPFacadeServletFilter.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/deegree/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     This library is free software; you can redistribute it and/or
012     modify it under the terms of the GNU Lesser General Public
013     License as published by the Free Software Foundation; either
014     version 2.1 of the License, or (at your option) any later version.
015    
016     This library is distributed in the hope that it will be useful,
017     but WITHOUT ANY WARRANTY; without even the implied warranty of
018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019     Lesser General Public License for more details.
020    
021     You should have received a copy of the GNU Lesser General Public
022     License along with this library; if not, write to the Free Software
023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024    
025     Contact:
026    
027     Andreas Poth
028     lat/lon GmbH
029     Aennchenstr. 19
030     53115 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Prof. Dr. Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: greve@giub.uni-bonn.de
041    
042     ---------------------------------------------------------------------------*/
043    package org.deegree.enterprise.servlet;
044    
045    import static org.deegree.ogcbase.CommonNamespaces.W3SOAP_ENVELOPE_PREFIX;
046    import static org.deegree.ogcbase.CommonNamespaces.W3SOAP_ENVELOPE;
047    
048    import java.io.ByteArrayInputStream;
049    import java.io.ByteArrayOutputStream;
050    import java.io.IOException;
051    import java.io.OutputStream;
052    import java.io.StringReader;
053    import java.net.URI;
054    import java.net.URISyntaxException;
055    import java.util.Iterator;
056    import java.util.List;
057    
058    import javax.servlet.Filter;
059    import javax.servlet.FilterChain;
060    import javax.servlet.FilterConfig;
061    import javax.servlet.ServletException;
062    import javax.servlet.ServletRequest;
063    import javax.servlet.ServletResponse;
064    import javax.servlet.http.HttpServletRequest;
065    import javax.servlet.http.HttpServletResponse;
066    import javax.xml.soap.SOAPException;
067    
068    import org.deegree.framework.log.ILogger;
069    import org.deegree.framework.log.LoggerFactory;
070    import org.deegree.framework.util.BootLogger;
071    import org.deegree.framework.util.StringTools;
072    import org.deegree.framework.xml.NamespaceContext;
073    import org.deegree.framework.xml.XMLException;
074    import org.deegree.framework.xml.XMLFragment;
075    import org.deegree.framework.xml.XMLParsingException;
076    import org.deegree.framework.xml.XMLTools;
077    import org.deegree.ogcbase.CommonNamespaces;
078    import org.w3c.dom.Element;
079    import org.w3c.dom.NodeList;
080    import org.xml.sax.SAXException;
081    
082    /**
083     * 
084     * 
085     * @version $Revision: 9635 $
086     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
087     * @author last edited by: $Author: aschmitz $
088     * 
089     * @version 1.0. $Revision: 9635 $, $Date: 2008-01-18 14:40:52 +0100 (Fr, 18 Jan 2008) $
090     * 
091     * @since 2.0
092     */
093    public class SOAPFacadeServletFilter implements Filter {
094    
095        private static ILogger LOG = LoggerFactory.getLogger( SOAPFacadeServletFilter.class );
096    
097        private static String SOAPNS = "http://www.w3.org/2003/05/soap-envelope";
098    
099        private static NamespaceContext nsContext = new NamespaceContext();
100    
101        static {
102            try {
103                nsContext.addNamespace( "csw", CommonNamespaces.CSWNS );
104                nsContext.addNamespace( "soap", new URI( SOAPNS ) );
105            } catch ( URISyntaxException e ) {
106                BootLogger.logError( "Error initializing namespace node.", e );
107            }
108        }
109    
110        /**
111         * @param filterConfig
112         */
113        public void init( FilterConfig filterConfig )
114                                throws ServletException {
115            // nothing to do
116        }
117    
118        /**
119         * 
120         */
121        public void destroy() {
122            // nothing to do
123        }
124    
125        /**
126         * @param request
127         * @param response
128         * @throws IOException
129         * @throws ServletException
130         */
131        public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain )
132                                throws IOException, ServletException {
133    
134            if ( ( (HttpServletRequest) request ).getMethod().equalsIgnoreCase( "GET" ) ) {
135                chain.doFilter( request, response );
136            } else {
137                ServletRequestWrapper reqWrapper = new ServletRequestWrapper( (HttpServletRequest) request );
138    
139                XMLFragment xml = new XMLFragment();
140                try {
141                    xml.load( reqWrapper.getInputStream(), XMLFragment.DEFAULT_URL );
142                } catch ( XMLException e ) {
143                    LOG.logError( "parsing request as XML", e );
144                    throw new ServletException( StringTools.stackTraceToString( e ) );
145                } catch ( SAXException e ) {
146                    LOG.logError( "parsing request as XML", e );
147                    throw new ServletException( StringTools.stackTraceToString( e ) );
148                }
149                String s = xml.getRootElement().getNamespaceURI();
150                // checking if the root elements node name equals the root name of
151                // a SOAP message document. If so the SOAP body must be accessed
152                // to be forwarded to the the filter/servlet
153                if ( s.equals( SOAPNS ) ) {
154                    LOG.logDebug( "handle SOAP request" );
155                    try {
156                        handleSOAPRequest( reqWrapper, (HttpServletResponse) response, chain, xml );
157                    } catch ( Exception e ) {
158                        LOG.logError( "handling SOAP request", e );
159                        throw new ServletException( StringTools.stackTraceToString( e ) );
160                    }
161                } else {
162                    LOG.logDebug( "just forward request to next filter or servlet" );
163                    chain.doFilter( reqWrapper, response );
164                }
165            }
166    
167        }
168    
169        /**
170         * handles a SOAP request. It is assumed that SOAP messaging has been used and the request to be
171         * performed against a OWS is wrapped within the SOAPBody.
172         * 
173         * @param request
174         * @param response
175         * @param chain
176         * @param xmlReq
177         * @throws IOException
178         * @throws ServletException
179         * @throws SAXException
180         * @throws XMLParsingException
181         */
182        private void handleSOAPRequest( HttpServletRequest request, HttpServletResponse response, FilterChain chain,
183                                        XMLFragment xmlReq )
184                                throws IOException, ServletException, SAXException, XMLParsingException {
185    
186            XMLFragment sm = null;
187            if ( hasMandatoryHeader( xmlReq ) ) {
188                sm = handleMustUnderstandFault();
189            } else {
190                String s = W3SOAP_ENVELOPE_PREFIX + ":Body";
191                Element elem = XMLTools.getRequiredElement( xmlReq.getRootElement(), s, nsContext );
192    
193                // use first child element
194                elem = XMLTools.getElement( elem, "child::*[1]", nsContext );
195    
196                // extract SOAPBody and wrap it into a ServletWrapper
197                XMLFragment xml = new XMLFragment( elem );
198    
199                if ( LOG.isDebug() ) {
200                    LOG.logDebug( "Extracted request", xml.getAsPrettyString() );
201                }
202    
203                ByteArrayOutputStream bos = new ByteArrayOutputStream( 50000 );
204                xml.write( bos );
205    
206                ServletRequestWrapper forward = new ServletRequestWrapper( request );
207                forward.setInputStreamAsByteArray( bos.toByteArray() );
208                bos.close();
209                ServletResponseWrapper resWrapper = new ServletResponseWrapper( response );
210                chain.doFilter( forward, resWrapper );
211    
212                OutputStream os = resWrapper.getOutputStream();
213                byte[] b = ( (ServletResponseWrapper.ProxyServletOutputStream) os ).toByteArray();
214                os.close();
215    
216                sm = createResponseMessage( b );
217            }
218    
219            // write into stream to calling client
220            OutputStream os = response.getOutputStream();
221            sm.write( os );
222            os.close();
223    
224        }
225    
226        private XMLFragment handleMustUnderstandFault()
227                                throws SAXException, IOException {
228            String s = StringTools.concat( 300, "<?xml version='1.0' encoding='UTF-8'?>",
229                                           "<soapenv:Envelope xmlns:soapenv='" + SOAPNS + "'><soapenv:Body>",
230                                           "<soapenv:Fault><soapenv:Code><soapenv:Value>soapenv:MustUnderstand",
231                                           "</soapenv:Value></soapenv:Code><soapenv:Reason><soapenv:Text ",
232                                           "xml:lang='en'>One or more mandatory SOAP header blocks not ",
233                                           "understood</soapenv:Text></soapenv:Reason></soapenv:Fault>",
234                                           "</soapenv:Body></soapenv:Envelope>" );
235            StringReader sr = new StringReader( s );
236            return new XMLFragment( sr, XMLFragment.DEFAULT_URL );
237        }
238    
239        /**
240         * returns true if the passed SOAP meassage contains a header that must be understood by a
241         * handling node
242         * 
243         * @param xmlReq
244         * @return true if the passed SOAP meassage contains a header that must be understood by a
245         *         handling node
246         * @throws XMLParsingException
247         */
248        private boolean hasMandatoryHeader( XMLFragment xmlReq )
249                                throws XMLParsingException {
250            List<Element> list = XMLTools.getElements( xmlReq.getRootElement(), "soap:Header", nsContext );
251            for ( Iterator<Element> iter = list.iterator(); iter.hasNext(); ) {
252                Element element = iter.next();
253                NodeList nl = element.getChildNodes();
254                for ( int i = 0; i < nl.getLength(); i++ ) {
255                    if ( nl.item( i ) instanceof Element ) {
256                        Element el = (Element) nl.item( i );
257                        if ( XMLTools.getNode( el, "@soap:mustUnderstand", nsContext ) != null ) {
258                            return true;
259                        }
260                    }
261                }
262            }
263            return false;
264        }
265    
266        /*
267         * private Map collectNamespaces( Element node, Map xmlns ) {
268         * 
269         * NamedNodeMap nnm = node.getAttributes(); for (int i = 0; i < nnm.getLength(); i++) { Node nd =
270         * nnm.item( i ); if ( node.getNodeName().startsWith( "xmlns" ) ) { xmlns.put(
271         * node.getNodeName(), nd.getNodeValue() ); } } NodeList nl = node.getChildNodes(); for (int i =
272         * 0; i < nl.getLength(); i++) { if ( nl.item( i ) instanceof Element ) { xmlns =
273         * collectNamespaces( (Element) nl.item( i ), xmlns ); } }
274         * 
275         * return xmlns; }
276         */
277    
278        /**
279         * creates a SOAP message where the response of a OWS call is wrapped whithin the SOAPBody
280         * 
281         * @param b
282         *            response to embed into the body of the SOAP response message
283         * @return SOAP response message
284         * @throws SOAPException
285         */
286        private XMLFragment createResponseMessage( byte[] b )
287                                throws IOException, SAXException {
288    
289            XMLFragment xml = new XMLFragment();
290            xml.load( new ByteArrayInputStream( b ), XMLFragment.DEFAULT_URL );
291    
292            String s = StringTools.concat( 200, "<?xml version='1.0' encoding='UTF-8'?>",
293                                           "<soapenv:Envelope xmlns:soapenv='", SOAPNS,
294                                           "'><soapenv:Body></soapenv:Body></soapenv:Envelope>" );
295            StringReader sr = new StringReader( s );
296            XMLFragment message = new XMLFragment( sr, XMLFragment.DEFAULT_URL );
297    
298            if ( xml.getRootElement().getLocalName().equals( "ExceptionReport" ) ) {
299                Element e = (Element) message.getRootElement().getFirstChild();
300                e = XMLTools.appendElement( e, W3SOAP_ENVELOPE, "soapenv:Fault" );
301                e = XMLTools.appendElement( e, W3SOAP_ENVELOPE, "soapenv:Detail" );
302                XMLTools.insertNodeInto( xml.getRootElement(), e );
303            } else {
304                XMLTools.insertNodeInto( xml.getRootElement(), message.getRootElement().getFirstChild() );
305            }
306    
307            /*
308             * can not be used for CSW because CSW DE-profile requires SOAP 1.2 MessageFactory factory =
309             * MessageFactory.newInstance(); SOAPMessage message = factory.createMessage();
310             * message.getSOAPBody().addDocument( doc );
311             */
312    
313            return message;
314        }
315    
316    }