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.HashMap;
021 import java.util.List;
022 import java.util.Map;
023
024 import javax.xml.XMLConstants;
025 import javax.xml.namespace.QName;
026 import javax.xml.stream.XMLStreamException;
027 import javax.xml.stream.XMLStreamReader;
028 import javax.xml.transform.Source;
029 import javax.xml.transform.dom.DOMSource;
030
031 import org.w3c.dom.Document;
032 import org.w3c.dom.Element;
033 import org.w3c.dom.NamedNodeMap;
034 import org.w3c.dom.Node;
035 import org.w3c.dom.NodeList;
036
037 import org.apache.cxf.binding.soap.Soap11;
038 import org.apache.cxf.binding.soap.Soap12;
039 import org.apache.cxf.binding.soap.SoapHeader;
040 import org.apache.cxf.binding.soap.SoapMessage;
041 import org.apache.cxf.binding.soap.SoapVersion;
042 import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
043 import org.apache.cxf.binding.soap.model.SoapBindingInfo;
044 import org.apache.cxf.binding.soap.model.SoapHeaderInfo;
045 import org.apache.cxf.endpoint.Endpoint;
046 import org.apache.cxf.headers.Header;
047 import org.apache.cxf.helpers.DOMUtils;
048 import org.apache.cxf.interceptor.Fault;
049 import org.apache.cxf.message.Exchange;
050 import org.apache.cxf.message.Message;
051 import org.apache.cxf.phase.Phase;
052 import org.apache.cxf.service.model.BindingMessageInfo;
053 import org.apache.cxf.service.model.BindingOperationInfo;
054 import org.apache.cxf.service.model.MessagePartInfo;
055 import org.apache.cxf.service.model.SchemaInfo;
056 import org.apache.cxf.staxutils.DepthXMLStreamReader;
057 import org.apache.cxf.staxutils.PartialXMLStreamReader;
058 import org.apache.cxf.staxutils.StaxUtils;
059 import org.apache.servicemix.soap.util.DomUtil;
060 import org.apache.servicemix.soap.util.stax.StaxSource;
061
062 /**
063 * @author <a href="mailto:gnodet [at] gmail.com">Guillaume Nodet</a>
064 */
065 public class JbiInWsdl1Interceptor extends AbstractSoapInterceptor {
066
067 private boolean useJBIWrapper = true;
068 private boolean useSOAPEnvelope = true;
069
070 public JbiInWsdl1Interceptor(boolean useJBIWrapper, boolean useSOAPEnvelope) {
071 super(Phase.PRE_INVOKE);
072 addAfter(JbiOperationInterceptor.class.getName());
073 this.useJBIWrapper = useJBIWrapper;
074 this.useSOAPEnvelope = useSOAPEnvelope;
075 }
076
077 public void handleMessage(SoapMessage message) {
078 // Ignore faults messages
079 if (message.getContent(Exception.class) != null) {
080 return;
081 }
082 Document document = DomUtil.createDocument();
083
084 if (!useJBIWrapper) {
085 SoapVersion soapVersion = message.getVersion();
086 if (useSOAPEnvelope) {
087 Element soapEnv = DomUtil.createElement(document, new QName(
088 soapVersion.getEnvelope().getNamespaceURI(), soapVersion
089 .getEnvelope().getLocalPart(), soapVersion
090 .getPrefix()));
091 Element soapBody = DomUtil.createElement(soapEnv, new QName(
092 soapVersion.getBody().getNamespaceURI(), soapVersion
093 .getBody().getLocalPart(), soapVersion
094 .getPrefix()));
095 setExtraPrefix(message, soapEnv);
096
097 soapEnv.appendChild(soapBody);
098 Element body = getBodyElement(message);
099
100 if (body != null) {
101 soapBody.appendChild(soapBody.getOwnerDocument().importNode(body,
102 true));
103 }
104 } else {
105 Element body = getBodyElement(message);
106 if (body != null) {
107 document.appendChild(document.importNode(body, true));
108 }
109 }
110 message.setContent(Source.class, new DOMSource(document));
111 } else {
112 try {
113 XMLStreamReader reader = new StaxJbiWrapper(message);
114 message.setContent(Source.class, new StaxSource(reader));
115 } catch (UnsupportedOperationException e) {
116 document = createDOMWrapper(message);
117 if (document != null) {
118 message.setContent(Source.class, new DOMSource(document));
119 }
120 }
121 }
122
123 }
124
125 private void setExtraPrefix(SoapMessage message, Element soapEnv) {
126 Document savedEnv = (Document) message.getContent(Node.class);
127 if (savedEnv != null) {
128 NamedNodeMap attrs = savedEnv.getFirstChild().getAttributes();
129 Map<String, String> nsMap = message.getEnvelopeNs();
130 if (nsMap == null) {
131 nsMap = new HashMap<String, String>();
132 }
133 for (int i = 0; i < attrs.getLength(); i++) {
134 Node node = attrs.item(i);
135 if (!node.getNodeValue().equals(Soap11.SOAP_NAMESPACE)
136 && !node.getNodeValue().equals(Soap12.SOAP_NAMESPACE)) {
137 //set extra prefix
138 soapEnv.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":"
139 + node.getLocalName(),
140 node.getNodeValue());
141 nsMap.put(node.getLocalName(), node.getNodeValue());
142 }
143
144 }
145 if (nsMap.size() > 0) {
146 message.put("soap.env.ns.map", nsMap);
147 }
148 }
149 }
150
151 private Document createDOMWrapper(SoapMessage message) {
152 Document document;
153 BindingOperationInfo wsdlOperation = getOperation(message);
154 BindingMessageInfo wsdlMessage = !isRequestor(message) ? wsdlOperation
155 .getInput()
156 : wsdlOperation.getOutput();
157
158 document = DomUtil.createDocument();
159 Element root = DomUtil.createElement(document,
160 CxfJbiConstants.WSDL11_WRAPPER_MESSAGE);
161 String typeNamespace = wsdlMessage.getMessageInfo().getName()
162 .getNamespaceURI();
163 if (typeNamespace == null || typeNamespace.length() == 0) {
164 throw new IllegalArgumentException(
165 "messageType namespace is null or empty");
166 }
167 root.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":"
168 + CxfJbiConstants.WSDL11_WRAPPER_MESSAGE_PREFIX,
169 typeNamespace);
170
171 setExtraPrefix(message, root);
172
173 String typeLocalName = wsdlMessage.getMessageInfo().getName()
174 .getLocalPart();
175 if (typeLocalName == null || typeLocalName.length() == 0) {
176 throw new IllegalArgumentException(
177 "messageType local name is null or empty");
178 }
179 root.setAttribute(CxfJbiConstants.WSDL11_WRAPPER_TYPE,
180 CxfJbiConstants.WSDL11_WRAPPER_MESSAGE_PREFIX + ":"
181 + typeLocalName);
182 String messageName = wsdlMessage.getMessageInfo().getName()
183 .getLocalPart();
184 root.setAttribute(CxfJbiConstants.WSDL11_WRAPPER_NAME, messageName);
185 root.setAttribute(CxfJbiConstants.WSDL11_WRAPPER_VERSION, "1.0");
186
187 SoapBindingInfo binding = (SoapBindingInfo) message.getExchange()
188 .get(Endpoint.class).getEndpointInfo().getBinding();
189 String style = binding.getStyle(wsdlOperation.getOperationInfo());
190 if (style == null) {
191 style = binding.getStyle();
192 }
193
194 Element body = getBodyElement(message);
195 if (body == null) {
196 return null;
197 }
198
199 if (body.getLocalName().equals("Fault")) {
200 handleJBIFault(message, body);
201 return null;
202 }
203 List<SoapHeaderInfo> headers = wsdlMessage
204 .getExtensors(SoapHeaderInfo.class);
205 List<Header> headerElement = message.getHeaders();
206 List<Object> parts = new ArrayList<Object>();
207 for (MessagePartInfo part : wsdlMessage.getMessageParts()) {
208 if ("document".equals(style)) {
209 parts.add(body);
210 } else /* rpc-style */ {
211 // SOAP:Body element is the operation name, children are
212 // operation parameters
213
214 Element param = DomUtil.getFirstChildElement(body);
215 boolean found = false;
216 while (param != null) {
217 if (part.getName().getLocalPart().equals(
218 param.getLocalName())) {
219 found = true;
220 parts.add(wrapNodeList(param.getChildNodes()));
221 break;
222 }
223 param = DomUtil.getNextSiblingElement(param);
224 }
225 if (!found) {
226 throw new Fault(new Exception("Missing part '"
227 + part.getName() + "'"));
228 }
229 }
230 }
231 processHeader(message, headers, headerElement, parts);
232 for (Object part : parts) {
233 if (part instanceof Node) {
234 addPart(root, (Node) part);
235 } else if (part instanceof NodeList) {
236 addPart(root, (NodeList) part);
237 } else if (part instanceof SoapHeader) {
238 addPart(root, (Node) ((SoapHeader) part).getObject());
239 }
240 }
241 return document;
242 }
243
244 private void processHeader(SoapMessage message,
245 List<SoapHeaderInfo> headers, List<Header> headerElement,
246 List<Object> parts) {
247 if (headers != null) {
248 for (SoapHeaderInfo header : headers) {
249 MessagePartInfo part = header.getPart();
250 Header param = findHeader(headerElement, part);
251 int idx = part.getIndex();
252 if (idx > parts.size()) {
253 parts.add(param);
254 } else if (idx == -1) {
255 parts.add(0, param);
256 } else {
257 parts.add(idx, param);
258 }
259 }
260 }
261 }
262
263 private void handleJBIFault(SoapMessage message, Element soapFault) {
264 try {
265 Document doc = DomUtil.createDocument();
266 Element jbiFault = DomUtil.createElement(doc, new QName(
267 CxfJbiConstants.WSDL11_WRAPPER_NAMESPACE,
268 JbiFault.JBI_FAULT_ROOT));
269 Node jbiFaultDetail = null;
270 if (message.getVersion() instanceof Soap11) {
271 NodeList nodeList = soapFault.getElementsByTagName("faultcode");
272 String faultCode = nodeList.item(0).getFirstChild()
273 .getTextContent();
274 String prefix = "";
275 if (faultCode.indexOf(":") != -1) {
276 prefix = faultCode.substring(0, faultCode.indexOf(":"));
277 }
278 String localName = faultCode
279 .substring(faultCode.indexOf(":") + 1);
280 message.put("faultcode", new QName(prefix, localName));
281 nodeList = soapFault.getElementsByTagName("faultstring");
282 message.put("faultstring", nodeList.item(0).getFirstChild()
283 .getTextContent());
284 nodeList = soapFault.getElementsByTagName("detail");
285 if (nodeList != null
286 && nodeList.getLength() > 0
287 && DomUtil.getFirstChildElement(nodeList.item(0)) != null) {
288 jbiFaultDetail = doc.importNode(DomUtil
289 .getFirstChildElement(nodeList.item(0)), true);
290 } else {
291 message.put("hasdetail", false);
292 nodeList = soapFault.getElementsByTagName("faultstring");
293 jbiFaultDetail = doc.importNode(nodeList.item(0)
294 .getFirstChild(), true);
295 }
296
297 } else {
298 NodeList nodeList = soapFault.getElementsByTagName("soap:Code");
299 String faultCode = DomUtil.getFirstChildElement(
300 nodeList.item(0)).getTextContent();
301 String prefix = "";
302 if (faultCode.indexOf(":") != -1) {
303 prefix = faultCode.substring(0, faultCode.indexOf(":"));
304 }
305 String localName = faultCode
306 .substring(faultCode.indexOf(":") + 1);
307 message.put("faultcode", new QName(prefix, localName));
308 nodeList = soapFault.getElementsByTagName("soap:Reason");
309 message.put("faultstring", DomUtil.getFirstChildElement(
310 nodeList.item(0)).getTextContent());
311 nodeList = soapFault.getElementsByTagName("soap:Detail");
312 if (nodeList != null
313 && nodeList.getLength() > 0
314 && DomUtil.getFirstChildElement(nodeList.item(0)) != null) {
315 jbiFaultDetail = doc.importNode(DomUtil
316 .getFirstChildElement(nodeList.item(0)), true);
317 } else {
318 message.put("hasdetail", false);
319 nodeList = soapFault.getElementsByTagName("faultstring");
320 jbiFaultDetail = doc.importNode(DomUtil
321 .getFirstChildElement(nodeList.item(0)), true);
322 }
323
324 }
325
326 SchemaInfo schemaInfo = getOperation(message).getBinding()
327 .getService().getSchema(jbiFaultDetail.getNamespaceURI());
328 if (schemaInfo != null && !schemaInfo.isElementFormQualified()
329 && (jbiFaultDetail instanceof Element)) {
330 // that's unquailied fault
331 jbiFaultDetail = addEmptyDefaultTns((Element) jbiFaultDetail);
332 }
333 jbiFault.appendChild(jbiFaultDetail);
334 message.setContent(Source.class, new DOMSource(doc));
335 message.put("jbiFault", true);
336 } catch (Exception e) {
337 String eMessage = e.getMessage();
338 if (eMessage == null) {
339 eMessage = e.getClass().getName();
340 }
341 throw new RuntimeException("the fault message can't be parsed:" + eMessage);
342
343 }
344 }
345
346 private Element addEmptyDefaultTns(Element ret) {
347
348 if (!ret.hasAttribute("xmlns")) {
349 ret.setAttribute("xmlns", "");
350 }
351 NodeList nodes = ret.getChildNodes();
352 for (int i = 0; i < nodes.getLength(); i++) {
353 if (nodes.item(i) instanceof Element) {
354 Element ele = (Element) nodes.item(i);
355 ele = addEmptyDefaultTns(ele);
356
357 }
358 }
359 return ret;
360 }
361
362
363 private NodeList wrapNodeList(final NodeList childNodes) {
364 return new NodeList() {
365 public int getLength() {
366 return childNodes.getLength();
367 }
368
369 public Node item(int index) {
370 return childNodes.item(index);
371 }
372 };
373 }
374
375 protected BindingOperationInfo getOperation(Message message) {
376 BindingOperationInfo operation = message.getExchange().get(
377 BindingOperationInfo.class);
378 if (operation == null) {
379 throw new Fault(
380 new Exception("Operation not bound on this message"));
381 }
382 return operation;
383 }
384
385 /**
386 * Extract the content as DOM element
387 */
388 protected Element getBodyElement(SoapMessage message) {
389 try {
390 XMLStreamReader xmlReader = message
391 .getContent(XMLStreamReader.class);
392 XMLStreamReader filteredReader = new PartialXMLStreamReader(
393 xmlReader, message.getVersion().getBody());
394 //ensure the whitespace is passed
395 StaxUtils.toNextElement((DepthXMLStreamReader) filteredReader);
396 Document doc = DOMUtils.createDocument();
397 StaxUtils.readDocElements(doc, filteredReader, true);
398 return doc.getDocumentElement();
399 } catch (XMLStreamException e) {
400 throw new Fault(e);
401 }
402 }
403
404 protected Header getHeaderElement(SoapMessage message, QName name) {
405 Exchange exchange = message.getExchange();
406 BindingOperationInfo bop = exchange.get(BindingOperationInfo.class);
407 if (bop.isUnwrapped()) {
408 bop = bop.getWrappedOperation();
409 }
410 boolean client = isRequestor(message);
411 BindingMessageInfo bmi = client ? bop.getOutput() : bop.getInput();
412 if (bmi == null) {
413 // one way operation.
414 return null;
415 }
416 List<SoapHeaderInfo> headers = bmi.getExtensors(SoapHeaderInfo.class);
417 if (headers == null || headers.size() == 0) {
418 return null;
419 }
420 List<Header> headerElement = message.getHeaders();
421 for (SoapHeaderInfo header : headers) {
422 QName qname = header.getPart().isElement()
423 ? header.getPart().getElementQName() : header.getPart().getTypeQName();
424 if (qname.equals(name)) {
425 MessagePartInfo mpi = header.getPart();
426 return findHeader(headerElement, mpi);
427 }
428 }
429 return null;
430 }
431
432 /**
433 * Add a jbi:part to a normalized message document
434 */
435 private static void addPart(Element parent, Node partValue) {
436 Element element = DomUtil.createElement(parent,
437 CxfJbiConstants.WSDL11_WRAPPER_PART);
438 element.appendChild(element.getOwnerDocument().importNode(partValue,
439 true));
440 }
441
442 /**
443 * Add a jbi:part to a normalized message document
444 */
445 private static void addPart(Element parent, NodeList nodes) {
446 Element element = DomUtil.createElement(parent,
447 CxfJbiConstants.WSDL11_WRAPPER_PART);
448 for (int i = 0; i < nodes.getLength(); i++) {
449 Node node = nodes.item(i);
450 element.appendChild(element.getOwnerDocument().importNode(node,
451 true));
452 }
453 }
454
455 private static Header findHeader(List<Header> headerElement,
456 MessagePartInfo mpi) {
457 Header param = null;
458 if (headerElement != null) {
459 QName name = mpi.getConcreteName();
460 for (Header header : headerElement) {
461 if (mpi.isElement()) {
462 if (header.getName().getNamespaceURI() != null
463 && header.getName().getNamespaceURI().equals(
464 name.getNamespaceURI())
465 && header.getName().getLocalPart() != null
466 && header.getName().getLocalPart().equals(
467 name.getLocalPart())) {
468 param = header;
469 }
470 } else {
471 if (header.getName().getLocalPart() != null
472 && header.getName().getLocalPart().equals(
473 name.getLocalPart())) {
474 param = header;
475 }
476 }
477 }
478 }
479 return param;
480 }
481
482 }