001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.cxfbc.interceptors;
018
019 import java.util.ArrayList;
020 import java.util.List;
021
022 import javax.xml.XMLConstants;
023 import javax.xml.namespace.QName;
024 import javax.xml.stream.XMLStreamException;
025 import javax.xml.stream.XMLStreamReader;
026 import javax.xml.transform.Source;
027 import javax.xml.transform.dom.DOMSource;
028
029 import org.w3c.dom.Document;
030 import org.w3c.dom.Element;
031 import org.w3c.dom.Node;
032 import org.w3c.dom.NodeList;
033
034 import org.apache.cxf.binding.jbi.JBIConstants;
035 import org.apache.cxf.binding.jbi.JBIFault;
036 import org.apache.cxf.binding.soap.Soap11;
037 import org.apache.cxf.binding.soap.SoapHeader;
038 import org.apache.cxf.binding.soap.SoapMessage;
039 import org.apache.cxf.binding.soap.SoapVersion;
040 import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
041 import org.apache.cxf.binding.soap.model.SoapBindingInfo;
042 import org.apache.cxf.binding.soap.model.SoapHeaderInfo;
043 import org.apache.cxf.endpoint.Endpoint;
044 import org.apache.cxf.headers.Header;
045 import org.apache.cxf.helpers.DOMUtils;
046 import org.apache.cxf.interceptor.Fault;
047 import org.apache.cxf.message.Exchange;
048 import org.apache.cxf.message.Message;
049 import org.apache.cxf.phase.Phase;
050 import org.apache.cxf.service.model.BindingMessageInfo;
051 import org.apache.cxf.service.model.BindingOperationInfo;
052 import org.apache.cxf.service.model.MessagePartInfo;
053 import org.apache.cxf.service.model.SchemaInfo;
054 import org.apache.cxf.staxutils.DepthXMLStreamReader;
055 import org.apache.cxf.staxutils.PartialXMLStreamReader;
056 import org.apache.cxf.staxutils.StaxUtils;
057 import org.apache.servicemix.soap.util.DomUtil;
058
059 /**
060 * @author <a href="mailto:gnodet [at] gmail.com">Guillaume Nodet</a>
061 */
062 public class JbiInWsdl1Interceptor extends AbstractSoapInterceptor {
063
064 private boolean useJBIWrapper = true;
065 private boolean useSOAPEnvelope = true;
066
067 public JbiInWsdl1Interceptor(boolean useJBIWrapper, boolean useSOAPEnvelope) {
068 super(Phase.PRE_INVOKE);
069 addAfter(JbiOperationInterceptor.class.getName());
070 this.useJBIWrapper = useJBIWrapper;
071 this.useSOAPEnvelope = useSOAPEnvelope;
072 }
073
074 public void handleMessage(SoapMessage message) {
075 // Ignore faults messages
076 if (message.getContent(Exception.class) != null) {
077 return;
078 }
079 Document document = DomUtil.createDocument();
080
081 if (!useJBIWrapper) {
082
083 SoapVersion soapVersion = message.getVersion();
084 if (useSOAPEnvelope) {
085 Element soapEnv = DomUtil.createElement(document, new QName(
086 soapVersion.getEnvelope().getNamespaceURI(), soapVersion
087 .getEnvelope().getLocalPart(), soapVersion
088 .getPrefix()));
089 Element soapBody = DomUtil.createElement(soapEnv, new QName(
090 soapVersion.getBody().getNamespaceURI(), soapVersion
091 .getBody().getLocalPart(), soapVersion
092 .getPrefix()));
093 soapEnv.appendChild(soapBody);
094 Element body = getBodyElement(message);
095 if (body != null) {
096 soapBody.appendChild(soapBody.getOwnerDocument().importNode(body,
097 true));
098 }
099 } else {
100 Element body = getBodyElement(message);
101 if (body != null) {
102 document.appendChild(document.importNode(body, true));
103 }
104 }
105 } else {
106
107 BindingOperationInfo wsdlOperation = getOperation(message);
108 BindingMessageInfo wsdlMessage = !isRequestor(message) ? wsdlOperation
109 .getInput()
110 : wsdlOperation.getOutput();
111
112 document = DomUtil.createDocument();
113 Element root = DomUtil.createElement(document,
114 JbiConstants.WSDL11_WRAPPER_MESSAGE);
115 String typeNamespace = wsdlMessage.getMessageInfo().getName()
116 .getNamespaceURI();
117 if (typeNamespace == null || typeNamespace.length() == 0) {
118 throw new IllegalArgumentException(
119 "messageType namespace is null or empty");
120 }
121 root.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":"
122 + JbiConstants.WSDL11_WRAPPER_MESSAGE_PREFIX,
123 typeNamespace);
124
125 root.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":"
126 + JbiConstants.WSDL11_WRAPPER_XSD_PREFIX,
127 XMLConstants.W3C_XML_SCHEMA_NS_URI);
128
129 root.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":"
130 + JbiConstants.WSDL11_WRAPPER_XSI_PREFIX,
131 XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
132
133 String typeLocalName = wsdlMessage.getMessageInfo().getName()
134 .getLocalPart();
135 if (typeLocalName == null || typeLocalName.length() == 0) {
136 throw new IllegalArgumentException(
137 "messageType local name is null or empty");
138 }
139 root.setAttribute(JbiConstants.WSDL11_WRAPPER_TYPE,
140 JbiConstants.WSDL11_WRAPPER_MESSAGE_PREFIX + ":"
141 + typeLocalName);
142 String messageName = wsdlMessage.getMessageInfo().getName()
143 .getLocalPart();
144 root.setAttribute(JbiConstants.WSDL11_WRAPPER_NAME, messageName);
145 root.setAttribute(JbiConstants.WSDL11_WRAPPER_VERSION, "1.0");
146
147 SoapBindingInfo binding = (SoapBindingInfo) message.getExchange()
148 .get(Endpoint.class).getEndpointInfo().getBinding();
149 String style = binding.getStyle(wsdlOperation.getOperationInfo());
150 if (style == null) {
151 style = binding.getStyle();
152 }
153
154 Element body = getBodyElement(message);
155 if (body == null) {
156 return;
157 }
158
159 if (body.getLocalName().equals("Fault")) {
160 handleJBIFault(message, body);
161 return;
162 }
163 List<SoapHeaderInfo> headers = wsdlMessage
164 .getExtensors(SoapHeaderInfo.class);
165 List<Header> headerElement = message.getHeaders();
166 List<Object> parts = new ArrayList<Object>();
167 for (MessagePartInfo part : wsdlMessage.getMessageParts()) {
168 if ("document".equals(style)) {
169 parts.add(body);
170 } else /* rpc-style */ {
171 // SOAP:Body element is the operation name, children are
172 // operation parameters
173
174 Element param = DomUtil.getFirstChildElement(body);
175 boolean found = false;
176 while (param != null) {
177 if (part.getName().getLocalPart().equals(
178 param.getLocalName())) {
179 found = true;
180 parts.add(wrapNodeList(param.getChildNodes()));
181 break;
182 }
183 param = DomUtil.getNextSiblingElement(param);
184 }
185 if (!found) {
186 throw new Fault(new Exception("Missing part '"
187 + part.getName() + "'"));
188 }
189 }
190 }
191 processHeader(message, headers, headerElement, parts);
192 for (Object part : parts) {
193 if (part instanceof Node) {
194 addPart(root, (Node) part);
195 } else if (part instanceof NodeList) {
196 addPart(root, (NodeList) part);
197 } else if (part instanceof SoapHeader) {
198 addPart(root, (Node) ((SoapHeader) part).getObject());
199 }
200 }
201 }
202 message.setContent(Source.class, new DOMSource(document));
203 }
204
205 private void processHeader(SoapMessage message,
206 List<SoapHeaderInfo> headers, List<Header> headerElement,
207 List<Object> parts) {
208 if (headers != null) {
209 for (SoapHeaderInfo header : headers) {
210 MessagePartInfo part = header.getPart();
211 Header param = findHeader(headerElement, part);
212 int idx = part.getIndex();
213
214 if (idx > parts.size()) {
215 parts.add(param);
216 } else if (idx == -1) {
217 parts.add(0, param);
218 } else {
219 parts.add(idx, param);
220 }
221 }
222 }
223 }
224
225 void handleJBIFault(SoapMessage message, Element soapFault) {
226 Document doc = DomUtil.createDocument();
227 Element jbiFault = DomUtil.createElement(doc, new QName(
228 JBIConstants.NS_JBI_BINDING, JBIFault.JBI_FAULT_ROOT));
229 Node jbiFaultDetail = null;
230 if (message.getVersion() instanceof Soap11) {
231 NodeList nodeList = soapFault.getElementsByTagName("faultcode");
232 String faultCode = nodeList.item(0).getFirstChild().getTextContent();
233 String prefix = faultCode.substring(0, faultCode.indexOf(":"));
234 String localName = faultCode.substring(faultCode.indexOf(":") + 1);
235 message.put("faultcode", new QName(prefix, localName));
236 nodeList = soapFault.getElementsByTagName("faultstring");
237 message.put("faultstring", nodeList.item(0).getFirstChild().getTextContent());
238 nodeList = soapFault.getElementsByTagName("detail");
239 if (nodeList != null && nodeList.getLength() > 0
240 && nodeList.item(0).getFirstChild() != null) {
241 jbiFaultDetail = doc.importNode(nodeList.item(0).getFirstChild(), true);
242 } else {
243 message.put("hasdetail", false);
244 nodeList = soapFault.getElementsByTagName("faultstring");
245 jbiFaultDetail = doc.importNode(nodeList.item(0).getFirstChild(), true);
246 }
247
248 } else {
249 NodeList nodeList = soapFault.getElementsByTagName("soap:Code");
250 String faultCode = nodeList.item(0).getFirstChild().getTextContent();
251 String prefix = faultCode.substring(0, faultCode.indexOf(":"));
252 String localName = faultCode.substring(faultCode.indexOf(":") + 1);
253 message.put("faultcode", new QName(prefix, localName));
254 nodeList = soapFault.getElementsByTagName("soap:Reason");
255 message.put("faultstring", nodeList.item(0).getFirstChild().getTextContent());
256 nodeList = soapFault.getElementsByTagName("soap:Detail");
257 if (nodeList != null && nodeList.getLength() > 0
258 && nodeList.item(0).getFirstChild() != null) {
259 jbiFaultDetail = doc.importNode(nodeList.item(0).getFirstChild(), true);
260 } else {
261 message.put("hasdetail", false);
262 nodeList = soapFault.getElementsByTagName("faultstring");
263 jbiFaultDetail = doc.importNode(nodeList.item(0).getFirstChild(), true);
264 }
265
266 }
267 SchemaInfo schemaInfo =
268 getOperation(message).getBinding().getService().getSchema(jbiFaultDetail.getNamespaceURI());
269 if (schemaInfo != null && !schemaInfo.isElementFormQualified()) {
270 //that's unquailied fault
271 jbiFaultDetail = addEmptyDefaultTns((Element)jbiFaultDetail);
272
273 }
274 jbiFault.appendChild(jbiFaultDetail);
275 message.setContent(Source.class, new DOMSource(doc));
276 message.put("jbiFault", true);
277 }
278
279 private Element addEmptyDefaultTns(Element ret) {
280
281 if (!ret.hasAttribute("xmlns")) {
282 ret.setAttribute("xmlns", "");
283 }
284 NodeList nodes = ret.getChildNodes();
285 for (int i = 0; i < nodes.getLength(); i++) {
286 if (nodes.item(i) instanceof Element) {
287 Element ele = (Element) nodes.item(i);
288 ele = addEmptyDefaultTns(ele);
289
290 }
291 }
292 return ret;
293 }
294
295
296 private NodeList wrapNodeList(final NodeList childNodes) {
297 return new NodeList() {
298 public int getLength() {
299 return childNodes.getLength();
300 }
301
302 public Node item(int index) {
303 return childNodes.item(index);
304 }
305 };
306 }
307
308 protected BindingOperationInfo getOperation(Message message) {
309 BindingOperationInfo operation = message.getExchange().get(
310 BindingOperationInfo.class);
311 if (operation == null) {
312 throw new Fault(
313 new Exception("Operation not bound on this message"));
314 }
315 return operation;
316 }
317
318 /**
319 * Extract the content as DOM element
320 */
321 protected Element getBodyElement(SoapMessage message) {
322 try {
323 XMLStreamReader xmlReader = message
324 .getContent(XMLStreamReader.class);
325 XMLStreamReader filteredReader = new PartialXMLStreamReader(
326 xmlReader, message.getVersion().getBody());
327 //ensure the whitespace is passed
328 StaxUtils.toNextElement((DepthXMLStreamReader) filteredReader);
329 Document doc = DOMUtils.createDocument();
330 StaxUtils.readDocElements(doc, filteredReader, false);
331 return doc.getDocumentElement();
332 } catch (XMLStreamException e) {
333 throw new Fault(e);
334 }
335 }
336
337 protected Header getHeaderElement(SoapMessage message, QName name) {
338 Exchange exchange = message.getExchange();
339 BindingOperationInfo bop = exchange.get(BindingOperationInfo.class);
340 if (bop.isUnwrapped()) {
341 bop = bop.getWrappedOperation();
342 }
343 boolean client = isRequestor(message);
344 BindingMessageInfo bmi = client ? bop.getOutput() : bop.getInput();
345 if (bmi == null) {
346 // one way operation.
347 return null;
348 }
349 List<SoapHeaderInfo> headers = bmi.getExtensors(SoapHeaderInfo.class);
350 if (headers == null || headers.size() == 0) {
351 return null;
352 }
353 List<Header> headerElement = message.getHeaders();
354 for (SoapHeaderInfo header : headers) {
355 QName qname = header.getPart().isElement()
356 ? header.getPart().getElementQName() : header.getPart().getTypeQName();
357 if (qname.equals(name)) {
358 MessagePartInfo mpi = header.getPart();
359 return findHeader(headerElement, mpi);
360 }
361 }
362 return null;
363 }
364
365 /**
366 * Add a jbi:part to a normalized message document
367 */
368 private static void addPart(Element parent, Node partValue) {
369 Element element = DomUtil.createElement(parent,
370 JbiConstants.WSDL11_WRAPPER_PART);
371 element.appendChild(element.getOwnerDocument().importNode(partValue,
372 true));
373 }
374
375 /**
376 * Add a jbi:part to a normalized message document
377 */
378 private static void addPart(Element parent, NodeList nodes) {
379 Element element = DomUtil.createElement(parent,
380 JbiConstants.WSDL11_WRAPPER_PART);
381 for (int i = 0; i < nodes.getLength(); i++) {
382 Node node = nodes.item(i);
383 element.appendChild(element.getOwnerDocument().importNode(node,
384 true));
385 }
386 }
387
388 private static Header findHeader(List<Header> headerElement,
389 MessagePartInfo mpi) {
390 Header param = null;
391 if (headerElement != null) {
392 QName name = mpi.getConcreteName();
393 for (Header header : headerElement) {
394 if (mpi.isElement()) {
395 if (header.getName().getNamespaceURI() != null
396 && header.getName().getNamespaceURI().equals(
397 name.getNamespaceURI())
398 && header.getName().getLocalPart() != null
399 && header.getName().getLocalPart().equals(
400 name.getLocalPart())) {
401 param = header;
402 }
403 } else {
404 if (header.getName().getLocalPart() != null
405 && header.getName().getLocalPart().equals(
406 name.getLocalPart())) {
407 param = header;
408 }
409 }
410 }
411 }
412 return param;
413 }
414
415 protected boolean isRequestor(Message message) {
416 return Boolean.TRUE.equals(message.get(Message.REQUESTOR_ROLE));
417 }
418 }