001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/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.ogcbase.CommonNamespaces.W3SOAP_ENVELOPE;
039 import static org.deegree.ogcbase.CommonNamespaces.W3SOAP_ENVELOPE_PREFIX;
040
041 import java.io.ByteArrayInputStream;
042 import java.io.ByteArrayOutputStream;
043 import java.io.IOException;
044 import java.io.OutputStream;
045 import java.io.StringReader;
046 import java.util.Iterator;
047 import java.util.List;
048
049 import javax.servlet.Filter;
050 import javax.servlet.FilterChain;
051 import javax.servlet.FilterConfig;
052 import javax.servlet.ServletException;
053 import javax.servlet.ServletRequest;
054 import javax.servlet.ServletResponse;
055 import javax.servlet.http.HttpServletRequest;
056 import javax.servlet.http.HttpServletResponse;
057
058 import org.deegree.framework.log.ILogger;
059 import org.deegree.framework.log.LoggerFactory;
060 import org.deegree.framework.util.StringTools;
061 import org.deegree.framework.xml.NamespaceContext;
062 import org.deegree.framework.xml.XMLException;
063 import org.deegree.framework.xml.XMLFragment;
064 import org.deegree.framework.xml.XMLParsingException;
065 import org.deegree.framework.xml.XMLTools;
066 import org.deegree.ogcbase.CommonNamespaces;
067 import org.w3c.dom.Element;
068 import org.w3c.dom.NodeList;
069 import org.xml.sax.SAXException;
070
071 /**
072 *
073 *
074 * @version $Revision: 18195 $
075 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
076 * @author last edited by: $Author: mschneider $
077 *
078 * @version 1.0. $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
079 *
080 */
081 public class SOAPFacadeServletFilter implements Filter {
082
083 private static ILogger LOG = LoggerFactory.getLogger( SOAPFacadeServletFilter.class );
084
085 private static NamespaceContext nsContext = CommonNamespaces.getNamespaceContext();
086
087 /**
088 * @param filterConfig
089 */
090 public void init( FilterConfig filterConfig )
091 throws ServletException {
092 // nothing to do
093 }
094
095 /**
096 *
097 */
098 public void destroy() {
099 // nothing to do
100 }
101
102 /**
103 * @param request
104 * @param response
105 * @throws IOException
106 * @throws ServletException
107 */
108 public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain )
109 throws IOException, ServletException {
110 if ( ( (HttpServletRequest) request ).getMethod().equalsIgnoreCase( "GET" ) ) {
111 LOG.logDebug( "simply forward because request method is HTTP GET" );
112 chain.doFilter( request, response );
113 } else {
114 ServletRequestWrapper reqWrapper = new ServletRequestWrapper( (HttpServletRequest) request );
115
116 XMLFragment xml = new XMLFragment();
117 try {
118 xml.load( reqWrapper.getInputStream(), XMLFragment.DEFAULT_URL );
119 } catch ( XMLException e ) {
120 LOG.logError( "parsing request as XML", e );
121 throw new ServletException( StringTools.stackTraceToString( e ) );
122 } catch ( SAXException e ) {
123 LOG.logError( "parsing request as XML", e );
124 throw new ServletException( StringTools.stackTraceToString( e ) );
125 }
126 String s = xml.getRootElement().getNamespaceURI();
127
128 // checking if the root elements node name equals the root name of
129 // a SOAP message document. If so the SOAP body must be accessed
130 // to be forwarded to the the filter/servlet
131 if ( s.equals( W3SOAP_ENVELOPE.toASCIIString() ) ) {
132 LOG.logDebug( "handle SOAP request" );
133 try {
134 handleSOAPRequest( reqWrapper, (HttpServletResponse) response, chain, xml );
135 } catch ( Exception e ) {
136 LOG.logError( "handling SOAP request", e );
137 throw new ServletException( StringTools.stackTraceToString( e ) );
138 }
139 } else {
140 LOG.logDebug( "just forward request to next filter or servlet" );
141 chain.doFilter( reqWrapper, response );
142 }
143 }
144
145 }
146
147 /**
148 * handles a SOAP request. It is assumed that SOAP messaging has been used and the request to be performed against a
149 * OWS is wrapped within the SOAPBody.
150 *
151 * @param request
152 * @param response
153 * @param chain
154 * @param xmlReq
155 * @throws IOException
156 * @throws ServletException
157 * @throws SAXException
158 * @throws XMLParsingException
159 */
160 private void handleSOAPRequest( HttpServletRequest request, HttpServletResponse response, FilterChain chain,
161 XMLFragment xmlReq )
162 throws IOException, ServletException, SAXException, XMLParsingException {
163
164 XMLFragment sm = null;
165 if ( hasMandatoryHeader( xmlReq ) ) {
166 sm = handleMustUnderstandFault();
167 } else {
168 String s = W3SOAP_ENVELOPE_PREFIX + ":Body";
169 Element elem = XMLTools.getRequiredElement( xmlReq.getRootElement(), s, nsContext );
170
171 // use first child element
172 elem = XMLTools.getElement( elem, "child::*[1]", nsContext );
173
174 // extract SOAPBody and wrap it into a ServletWrapper
175 XMLFragment xml = new XMLFragment( elem );
176
177 if ( LOG.isDebug() ) {
178 LOG.logDebug( "Extracted request", xml.getAsPrettyString() );
179 }
180
181 ByteArrayOutputStream bos = new ByteArrayOutputStream( 50000 );
182 xml.write( bos );
183
184 ServletRequestWrapper forward = new ServletRequestWrapper( request );
185 forward.setInputStreamAsByteArray( bos.toByteArray() );
186 bos.close();
187 ServletResponseWrapper resWrapper = new ServletResponseWrapper( response );
188 chain.doFilter( forward, resWrapper );
189
190 OutputStream os = resWrapper.getOutputStream();
191 byte[] b = ( (ServletResponseWrapper.ProxyServletOutputStream) os ).toByteArray();
192 os.close();
193
194 sm = createResponseMessage( b );
195 }
196
197 response.setContentType( "application/soap+xml" );
198
199 // write into stream to calling client
200 OutputStream os = response.getOutputStream();
201 sm.write( os );
202 os.close();
203
204 }
205
206 private XMLFragment handleMustUnderstandFault()
207 throws SAXException, IOException {
208 String s = StringTools.concat( 300, "<?xml version='1.0' encoding='UTF-8'?>",
209 "<soapenv:Envelope xmlns:soapenv='" + W3SOAP_ENVELOPE + "'><soapenv:Body>",
210 "<soapenv:Fault><soapenv:Code><soapenv:Value>soapenv:MustUnderstand",
211 "</soapenv:Value></soapenv:Code><soapenv:Reason><soapenv:Text ",
212 "xml:lang='en'>One or more mandatory SOAP header blocks not ",
213 "understood</soapenv:Text></soapenv:Reason></soapenv:Fault>",
214 "</soapenv:Body></soapenv:Envelope>" );
215 StringReader sr = new StringReader( s );
216 return new XMLFragment( sr, XMLFragment.DEFAULT_URL );
217 }
218
219 /**
220 * returns true if the passed SOAP message contains a header that must be understood by a handling node
221 *
222 * @param xmlReq
223 * @return true if the passed SOAP message contains a header that must be understood by a handling node
224 * @throws XMLParsingException
225 */
226 private boolean hasMandatoryHeader( XMLFragment xmlReq )
227 throws XMLParsingException {
228 List<Element> list = XMLTools.getElements( xmlReq.getRootElement(), "soap:Header", nsContext );
229 for ( Iterator<Element> iter = list.iterator(); iter.hasNext(); ) {
230 Element element = iter.next();
231 NodeList nl = element.getChildNodes();
232 for ( int i = 0; i < nl.getLength(); i++ ) {
233 if ( nl.item( i ) instanceof Element ) {
234 Element el = (Element) nl.item( i );
235 if ( XMLTools.getNode( el, "@soap:mustUnderstand", nsContext ) != null ) {
236 return true;
237 }
238 }
239 }
240 }
241 return false;
242 }
243
244 /*
245 * private Map collectNamespaces( Element node, Map xmlns ) {
246 *
247 * NamedNodeMap nnm = node.getAttributes(); for (int i = 0; i < nnm.getLength(); i++) { Node nd = nnm.item( i ); if
248 * ( node.getNodeName().startsWith( "xmlns" ) ) { xmlns.put( node.getNodeName(), nd.getNodeValue() ); } } NodeList
249 * nl = node.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { if ( nl.item( i ) instanceof Element ) {
250 * xmlns = collectNamespaces( (Element) nl.item( i ), xmlns ); } }
251 *
252 * return xmlns; }
253 */
254
255 /**
256 * creates a SOAP message where the response of a OWS call is wrapped whithin the SOAPBody
257 *
258 * @param b
259 * response to embed into the body of the SOAP response message
260 * @return SOAP response message
261 */
262 private XMLFragment createResponseMessage( byte[] b )
263 throws IOException, SAXException {
264
265 XMLFragment xml = new XMLFragment();
266 xml.load( new ByteArrayInputStream( b ), XMLFragment.DEFAULT_URL );
267
268 String s = StringTools.concat( 200, "<?xml version='1.0' encoding='UTF-8'?>",
269 "<soapenv:Envelope xmlns:soapenv='", W3SOAP_ENVELOPE,
270 "'><soapenv:Body></soapenv:Body></soapenv:Envelope>" );
271 StringReader sr = new StringReader( s );
272 XMLFragment message = new XMLFragment( sr, XMLFragment.DEFAULT_URL );
273
274 if ( xml.getRootElement().getLocalName().equals( "ExceptionReport" ) ) {
275 Element e = (Element) message.getRootElement().getFirstChild();
276 e = XMLTools.appendElement( e, W3SOAP_ENVELOPE, "soapenv:Fault" );
277 e = XMLTools.appendElement( e, W3SOAP_ENVELOPE, "soapenv:Detail" );
278 XMLTools.insertNodeInto( xml.getRootElement(), e );
279 } else {
280 XMLTools.insertNodeInto( xml.getRootElement(), message.getRootElement().getFirstChild() );
281 }
282
283 /*
284 * can not be used for CSW because CSW DE-profile requires SOAP 1.2 MessageFactory factory =
285 * MessageFactory.newInstance(); SOAPMessage message = factory.createMessage();
286 * message.getSOAPBody().addDocument( doc );
287 */
288
289 return message;
290 }
291
292 }