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.soap.interceptors.jbi;
018
019 import java.util.ArrayList;
020 import java.util.Collection;
021 import java.util.List;
022 import java.util.Collections;
023 import java.util.Iterator;
024
025 import javax.xml.namespace.QName;
026 import javax.xml.transform.Source;
027 import javax.xml.transform.dom.DOMSource;
028 import javax.xml.stream.XMLStreamReader;
029 import javax.xml.stream.XMLStreamException;
030
031 import org.apache.servicemix.soap.api.Fault;
032 import org.apache.servicemix.soap.api.Message;
033 import org.apache.servicemix.soap.api.model.Operation;
034 import org.apache.servicemix.soap.bindings.soap.model.wsdl1.Wsdl1SoapMessage;
035 import org.apache.servicemix.soap.bindings.soap.model.wsdl1.Wsdl1SoapOperation;
036 import org.apache.servicemix.soap.bindings.soap.model.wsdl1.Wsdl1SoapPart;
037 import org.apache.servicemix.soap.bindings.soap.model.wsdl1.Wsdl1SoapBinding;
038 import org.apache.servicemix.soap.bindings.soap.model.wsdl1.Wsdl1SoapBinding.Style;
039 import org.apache.servicemix.soap.core.AbstractInterceptor;
040 import org.apache.servicemix.soap.util.DomUtil;
041 import org.apache.servicemix.soap.util.QNameUtil;
042 import org.apache.servicemix.soap.util.stax.StaxUtil;
043 import org.apache.servicemix.soap.util.stax.FragmentStreamReader;
044 import org.apache.servicemix.soap.util.stax.StaxSource;
045
046 import org.w3c.dom.Document;
047 import org.w3c.dom.DocumentFragment;
048 import org.w3c.dom.Element;
049 import org.w3c.dom.Node;
050 import org.w3c.dom.NodeList;
051
052 /**
053 * @author <a href="mailto:gnodet [at] gmail.com">Guillaume Nodet</a>
054 */
055 public class JbiOutWsdl1Interceptor extends AbstractInterceptor {
056
057 private final boolean server;
058
059 public JbiOutWsdl1Interceptor(boolean server) {
060 this.server = server;
061 }
062
063 public void handleMessage(Message message) {
064 // Ignore faults messages
065 if (message.getContent(Exception.class) != null) {
066 return;
067 }
068 // Check if we should not use the JBI wrapper
069 if (message.get(JbiConstants.USE_JBI_WRAPPER) instanceof Boolean && ((Boolean) message.get(JbiConstants.USE_JBI_WRAPPER)) == false) {
070 return;
071 }
072 // Check if we can bypass DOM
073 Wsdl1SoapOperation wsdlOperation = getOperation(message);
074 Wsdl1SoapMessage wsdlMessage = server ? wsdlOperation.getOutput() : wsdlOperation.getInput();
075 if (wsdlOperation.getStyle() == Style.DOCUMENT
076 && tail(wsdlMessage.getParts()).isBody()
077 /*&& !(message.getContent(Source.class) instanceof DOMSource)*/) {
078 XMLStreamReader r = StaxUtil.createReader(message.getContent(Source.class));
079 try {
080 r.nextTag();
081 if (!JbiConstants.WSDL11_WRAPPER_MESSAGE.equals(r.getName())) {
082 throw new Fault("Message wrapper element is '" + QNameUtil.toString(r.getName())
083 + "' but expected '{" + JbiConstants.WSDL11_WRAPPER_NAMESPACE + "}message'");
084 }
085 for (Wsdl1SoapPart p : wsdlMessage.getParts()) {
086 r.nextTag();
087 if (!JbiConstants.WSDL11_WRAPPER_PART.equals(r.getName())) {
088 throw new Fault("Unexpected part wrapper element '" + QNameUtil.toString(r.getName())
089 + "' expected '{" + JbiConstants.WSDL11_WRAPPER_NAMESPACE + "}part'");
090 }
091 r.nextTag();
092 if (p.isBody()) {
093 message.setContent(Source.class, StaxUtil.createSource(r));
094 } else {
095 Element e = StaxUtil.createElement(r);
096 DocumentFragment frag = e.getOwnerDocument().createDocumentFragment();
097 frag.appendChild(e.getOwnerDocument().importNode(e, true));
098 message.getSoapHeaders().put(p.getElement(), frag);
099 r.nextTag();
100 }
101 }
102 /*
103 r.nextTag();
104 if (!JbiConstants.WSDL11_WRAPPER_PART.equals(r.getName())) {
105 throw new Fault("Unexpected part wrapper element '" + QNameUtil.toString(r.getName())
106 + "' expected '{" + JbiConstants.WSDL11_WRAPPER_NAMESPACE + "}part'");
107 }
108 r.nextTag();
109 message.setContent(Source.class, new StaxSource(new FragmentStreamReader(r)));
110 */
111 return;
112 } catch (XMLStreamException e) {
113 throw new Fault("Error parsing message: " + e, e);
114 }
115 }
116 // Use DOM for other requests
117 Document document = createDomMessage(message);
118 message.setContent(Source.class, new DOMSource(document));
119 }
120
121 private <T> T tail(Collection<T> parts) {
122 T last = null;
123 for (Iterator<T> it = parts.iterator(); it.hasNext(); last = it.next());
124 return last;
125 }
126
127 protected Document createDomMessage(Message message) {
128 Source source = message.getContent(Source.class);
129 Element element = StaxUtil.createElement(StaxUtil.createReader(source));
130 if (!JbiConstants.WSDL11_WRAPPER_NAMESPACE.equals(element.getNamespaceURI()) ||
131 !JbiConstants.WSDL11_WRAPPER_MESSAGE_LOCALNAME.equals(element.getLocalName())) {
132 throw new Fault("Message wrapper element is '" + QNameUtil.toString(element)
133 + "' but expected '{" + JbiConstants.WSDL11_WRAPPER_NAMESPACE + "}message'");
134 }
135 List<NodeList> partsContent = new ArrayList<NodeList>();
136 Element partWrapper = DomUtil.getFirstChildElement(element);
137 while (partWrapper != null) {
138 if (!JbiConstants.WSDL11_WRAPPER_NAMESPACE.equals(element.getNamespaceURI()) ||
139 !JbiConstants.WSDL11_WRAPPER_PART_LOCALNAME.equals(partWrapper.getLocalName())) {
140 throw new Fault("Unexpected part wrapper element '" + QNameUtil.toString(partWrapper)
141 + "' expected '{" + JbiConstants.WSDL11_WRAPPER_NAMESPACE + "}part'");
142 }
143 NodeList nodes = partWrapper.getChildNodes();
144 partsContent.add(nodes);
145 partWrapper = DomUtil.getNextSiblingElement(partWrapper);
146 }
147
148 Wsdl1SoapOperation wsdlOperation = getOperation(message);
149 Wsdl1SoapMessage wsdlMessage = server ? wsdlOperation.getOutput() : wsdlOperation.getInput();
150 Collection orderedParts = wsdlMessage.getParts();
151 if (orderedParts.size() != partsContent.size()) {
152 throw new Fault("Message contains " + partsContent.size() + " part(s) but expected "
153 + orderedParts.size() + " parts");
154 }
155
156 Document document = DomUtil.createDocument();
157 Node body = null;
158 if (wsdlOperation.getStyle() == Style.RPC) {
159 body = DomUtil.createElement(document, wsdlMessage.getElementName());
160 }
161 int idxPart = 0;
162 for (Wsdl1SoapPart part : wsdlMessage.getParts()) {
163 NodeList nodes = partsContent.get(idxPart++);
164 if (part.isBody()) {
165 if (wsdlOperation.getStyle() == Style.DOCUMENT) {
166 Element e = null;
167 for (int i = 0; i < nodes.getLength(); i++) {
168 Node n = nodes.item(i);
169 if (n instanceof Element) {
170 if (e != null) {
171 throw new Fault("Body part '" + part.getName() + "' contains more than one element; expected a single element.");
172 } else {
173 e = (Element) n;
174 }
175 }
176 }
177 if (e == null) {
178 throw new Fault("Body part '" + part.getName() + "' contains no element; expected a single element.");
179 }
180 if (!wsdlMessage.getElementName().equals(DomUtil.getQName(e))) {
181 throw new Fault("Body part '" + part.getName() + "' element '" + DomUtil.getQName(e) + " doesn't match expected element '" + QNameUtil.toString(wsdlMessage.getElementName()) + "'");
182 }
183 body = document.importNode(e, true);
184 document.appendChild(body);
185 } else /* rpc-style */ {
186 for (int j = 0; j < nodes.getLength(); j++) {
187 /* note: we don't do any validation on RPC-style part value types */
188 Element e = document.createElementNS(wsdlMessage.getElementName().getNamespaceURI(), part.getName());
189 body.appendChild(e);
190 e.appendChild(document.importNode(nodes.item(j), true));
191 }
192 }
193 } else {
194 DocumentFragment frag = document.createDocumentFragment();
195 Element e = null;
196 for (int i = 0; i < nodes.getLength(); i++) {
197 Node n = nodes.item(i);
198 if (n instanceof Element) {
199 if (e != null) {
200 throw new Fault("Header part '" + part.getName() + "' contains more than one element; expected a single element.");
201 } else {
202 e = (Element) n;
203 }
204 }
205 }
206 if (e == null) {
207 throw new Fault("Header part '" + part.getName() + "' contains no element; expected a single element.");
208 }
209 QName headerName = part.getElement();
210 if (!headerName.equals(DomUtil.getQName(e))) {
211 throw new Fault("Header part '" + part.getName() + "' element '" + DomUtil.getQName(e) + " doesn't match expected element '" + QNameUtil.toString(headerName) + "'");
212 }
213 for (int j=0; j<nodes.getLength(); j++) {
214 frag.appendChild(document.importNode(nodes.item(j), true));
215 }
216 message.getSoapHeaders().put(headerName, frag);
217 }
218 }
219 return document;
220 }
221
222 protected Wsdl1SoapOperation getOperation(Message message) {
223 Operation operation = message.get(Operation.class);
224 if (operation == null) {
225 throw new Fault("Operation not bound on this message");
226 }
227 if (operation instanceof Wsdl1SoapOperation == false) {
228 throw new Fault("Message is not bound to a WSDL 1.1 SOAP operation");
229 }
230 return (Wsdl1SoapOperation) operation;
231 }
232
233 protected Wsdl1SoapMessage getMessage(Message message) {
234 org.apache.servicemix.soap.api.model.Message msg = message.get(org.apache.servicemix.soap.api.model.Message.class);
235 if (msg == null) {
236 throw new Fault("Message not bound on this message");
237 }
238 if (msg instanceof Wsdl1SoapMessage == false) {
239 throw new Fault("Message is not bound to a WSDL 1.1 SOAP operation message");
240 }
241 return (Wsdl1SoapMessage) msg;
242 }
243
244 }