001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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 }