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.marshalers;
018
019 import java.io.ByteArrayInputStream;
020 import java.io.InputStream;
021 import java.io.SequenceInputStream;
022 import java.net.URI;
023 import java.util.Properties;
024
025 import javax.mail.Session;
026 import javax.mail.internet.ContentType;
027 import javax.mail.internet.MimeBodyPart;
028 import javax.mail.internet.MimeMessage;
029 import javax.mail.internet.MimeMultipart;
030 import javax.xml.namespace.QName;
031 import javax.xml.parsers.DocumentBuilder;
032 import javax.xml.parsers.ParserConfigurationException;
033 import javax.xml.stream.XMLStreamConstants;
034 import javax.xml.stream.XMLStreamReader;
035 import javax.xml.transform.Source;
036 import javax.xml.transform.dom.DOMSource;
037 import javax.xml.transform.stream.StreamSource;
038
039 import org.apache.servicemix.common.util.DOMUtil;
040 import org.apache.servicemix.jbi.jaxp.ExtendedXMLStreamReader;
041 import org.apache.servicemix.jbi.jaxp.FragmentStreamReader;
042 import org.apache.servicemix.jbi.jaxp.SourceTransformer;
043 import org.apache.servicemix.jbi.jaxp.StaxSource;
044 import org.apache.servicemix.jbi.jaxp.StringSource;
045 import org.apache.servicemix.soap.SoapFault;
046 import org.w3c.dom.Document;
047 import org.w3c.dom.DocumentFragment;
048 import org.w3c.dom.Element;
049 import org.w3c.dom.NodeList;
050
051 /**
052 *
053 * @author Guillaume Nodet
054 * @version $Revision: 1.5 $
055 * @since 3.0
056 */
057 public class SoapReader {
058
059 private SoapMarshaler marshaler;
060
061 protected static final Source EMPTY_CONTENT = new StringSource("<payload/>");
062
063 public SoapReader(SoapMarshaler marshaler) {
064 this.marshaler = marshaler;
065 }
066
067 public SoapMessage read(InputStream is, String contentType)
068 throws Exception {
069 if (contentType != null && startsWithCaseInsensitive(contentType, SoapMarshaler.MULTIPART_CONTENT)) {
070 Session session = Session.getDefaultInstance(new Properties());
071 is = new SequenceInputStream(new ByteArrayInputStream(new byte[] { 13, 10 }), is);
072 MimeMessage mime = new MimeMessage(session, is);
073 mime.setHeader(SoapMarshaler.MIME_CONTENT_TYPE, contentType);
074 return read(mime);
075 } else {
076 return read(is);
077 }
078 }
079
080 static boolean startsWithCaseInsensitive(String s1, String s2) {
081 return s1.regionMatches(true, 0, s2, 0, s2.length());
082 }
083
084 public SoapMessage read(InputStream is) throws Exception {
085 if (marshaler.isSoap()) {
086 if (marshaler.isUseDom()) {
087 return readSoapUsingDom(is);
088 } else {
089 return readSoapUsingStax(is);
090 }
091 } else {
092 SoapMessage message = new SoapMessage();
093 message.setSource(new StreamSource(is));
094 return message;
095 }
096 }
097
098 private SoapMessage readSoapUsingDom(InputStream is) throws Exception {
099 SoapMessage message = new SoapMessage();
100 DocumentBuilder builder = DOMUtil.getBuilder();
101 try {
102 Document doc = builder.parse(is);
103 message.setDocument(doc);
104 } finally {
105 DOMUtil.releaseBuilder(builder);
106 }
107 Element env = message.getDocument().getDocumentElement();
108 QName envName = DOMUtil.getQName(env);
109 if (!envName.getLocalPart().equals(SoapMarshaler.ENVELOPE)) {
110 throw new SoapFault(SoapFault.SENDER, "Unrecognized element: "
111 + envName + ". Expecting 'Envelope'.");
112 }
113 message.setEnvelopeName(envName);
114 // Check soap 1.1 or 1.2
115 String soapUri = envName.getNamespaceURI();
116 if (!SoapMarshaler.SOAP_11_URI.equals(soapUri) && !SoapMarshaler.SOAP_12_URI.equals(soapUri)) {
117 throw new SoapFault(SoapFault.SENDER, "Unrecognized namespace: " + soapUri
118 + " for element 'Envelope'.");
119 }
120 // Check Headers
121 Element child = DOMUtil.getFirstChildElement(env);
122 if (DOMUtil.getQName(child).equals(new QName(soapUri, SoapMarshaler.HEADER))) {
123 parseHeaders(message, child);
124 child = DOMUtil.getNextSiblingElement(child);
125 }
126 // Check Body
127 if (!DOMUtil.getQName(child).equals(new QName(soapUri, SoapMarshaler.BODY))) {
128 throw new SoapFault(SoapFault.SENDER, "Unrecognized element: "
129 + DOMUtil.getQName(child) + ". Expecting 'Body'.");
130 }
131 // Create Source for content
132 child = DOMUtil.getFirstChildElement(child);
133 if (child != null) {
134 QName childName = DOMUtil.getQName(child);
135 message.setBodyName(childName);
136 // Check for fault
137 if (childName.equals(new QName(soapUri, SoapMarshaler.FAULT))) {
138 message.setFault(readFaultUsingDom(child));
139 } else {
140 message.setSource(new DOMSource(child));
141 }
142 }
143 child = DOMUtil.getNextSiblingElement(child);
144 if (child != null) {
145 throw new SoapFault(SoapFault.RECEIVER, "Body element has more than one child element.");
146 }
147 return message;
148 }
149
150 private void parseHeaders(SoapMessage message, Element headers) {
151 for (Element child = DOMUtil.getFirstChildElement(headers);
152 child != null;
153 child = DOMUtil.getNextSiblingElement(child)) {
154 DocumentFragment df = child.getOwnerDocument().createDocumentFragment();
155 df.appendChild(child.cloneNode(true));
156 message.addHeader(DOMUtil.getQName(child), df);
157 }
158 }
159
160 private SoapMessage readSoapUsingStax(InputStream is) throws Exception {
161 SoapMessage message = new SoapMessage();
162 XMLStreamReader reader = marshaler.getInputFactory().createXMLStreamReader(is);
163 reader = new ExtendedXMLStreamReader(reader);
164 reader.nextTag();
165 // Check Envelope tag
166 if (!reader.getLocalName().equals(SoapMarshaler.ENVELOPE)) {
167 throw new SoapFault(SoapFault.SENDER, "Unrecognized element: "
168 + reader.getName() + " at ["
169 + reader.getLocation().getLineNumber() + ","
170 + reader.getLocation().getColumnNumber()
171 + "]. Expecting 'Envelope'.");
172 }
173 message.setEnvelopeName(reader.getName());
174 // Check soap 1.1 or 1.2
175 String soapUri = reader.getNamespaceURI();
176 if (!SoapMarshaler.SOAP_11_URI.equals(soapUri) && !SoapMarshaler.SOAP_12_URI.equals(soapUri)) {
177 throw new SoapFault(SoapFault.SENDER, "Unrecognized namespace: " + soapUri
178 + " for element 'Envelope' at ["
179 + reader.getLocation().getLineNumber() + ","
180 + reader.getLocation().getColumnNumber()
181 + "]. Expecting 'Envelope'.");
182 }
183 // Check Headers
184 reader.nextTag();
185 if (reader.getName().equals(new QName(soapUri, SoapMarshaler.HEADER))) {
186 parseHeaders(message, reader);
187 reader.nextTag();
188 }
189 // Check Body
190 if (!reader.getName().equals(new QName(soapUri, SoapMarshaler.BODY))) {
191 throw new SoapFault(SoapFault.SENDER, "Unrecognized element: "
192 + reader.getName() + " at ["
193 + reader.getLocation().getLineNumber() + ","
194 + reader.getLocation().getColumnNumber()
195 + "]. Expecting 'Body'.");
196 }
197 // Create Source for content
198 if (reader.nextTag() != XMLStreamConstants.END_ELEMENT) {
199 QName childName = reader.getName();
200 message.setBodyName(childName);
201 // Check for fault
202 if (childName.equals(new QName(soapUri, SoapMarshaler.FAULT))) {
203 message.setFault(readFaultUsingStax(reader));
204 } else {
205 message.setSource(new StaxSource(new FragmentStreamReader(reader)));
206 }
207 }
208 return message;
209 }
210
211 private SoapFault readFaultUsingDom(Element element) throws SoapFault {
212 QName code = null;
213 QName subcode = null;
214 String reason = null;
215 URI node = null;
216 URI role = null;
217 Source details = null;
218 // Parse soap 1.1 faults
219 if (element.getNamespaceURI().equals(SoapMarshaler.SOAP_11_URI)) {
220 // Fault code
221 Element child = DOMUtil.getFirstChildElement(element);
222 checkElementName(child, SoapMarshaler.SOAP_11_FAULTCODE);
223 code = DOMUtil.createQName(child, DOMUtil.getElementText(child));
224 // Fault string
225 child = DOMUtil.getNextSiblingElement(child);
226 checkElementName(child, SoapMarshaler.SOAP_11_FAULTSTRING);
227 reason = DOMUtil.getElementText(child);
228 child = DOMUtil.getNextSiblingElement(child);
229 QName childname = DOMUtil.getQName(child);
230 // Fault actor
231 if (SoapMarshaler.SOAP_11_FAULTACTOR.equals(childname)) {
232 node = URI.create(DOMUtil.getElementText(child));
233 child = DOMUtil.getNextSiblingElement(child);
234 childname = DOMUtil.getQName(child);
235 }
236 // Fault details
237 if (SoapMarshaler.SOAP_11_FAULTDETAIL.equals(childname)) {
238 details = getDetailsAsSource(child);
239 child = DOMUtil.getNextSiblingElement(child);
240 childname = DOMUtil.getQName(child);
241 }
242 // Nothing should be left
243 if (childname != null) {
244 throw new SoapFault(SoapFault.SENDER, "Unexpected element: " + childname);
245 }
246 // Parse soap 1.2 faults
247 } else {
248 // Fault code
249 Element child = DOMUtil.getFirstChildElement(element);
250 checkElementName(child, SoapMarshaler.SOAP_12_FAULTCODE);
251 Element subchild = DOMUtil.getFirstChildElement(child);
252 checkElementName(subchild, SoapMarshaler.SOAP_12_FAULTVALUE);
253 code = DOMUtil.createQName(subchild, DOMUtil.getElementText(subchild));
254 if (!SoapMarshaler.SOAP_12_CODE_DATAENCODINGUNKNOWN.equals(code) &&
255 !SoapMarshaler.SOAP_12_CODE_MUSTUNDERSTAND.equals(code) &&
256 !SoapMarshaler.SOAP_12_CODE_RECEIVER.equals(code) &&
257 !SoapMarshaler.SOAP_12_CODE_SENDER.equals(code) &&
258 !SoapMarshaler.SOAP_12_CODE_VERSIONMISMATCH.equals(code)) {
259 throw new SoapFault(SoapFault.SENDER, "Unexpected fault code: " + code);
260 }
261 subchild = DOMUtil.getNextSiblingElement(subchild);
262 if (subchild != null) {
263 checkElementName(subchild, SoapMarshaler.SOAP_12_FAULTSUBCODE);
264 Element subsubchild = DOMUtil.getFirstChildElement(subchild);
265 checkElementName(subsubchild, SoapMarshaler.SOAP_12_FAULTVALUE);
266 subcode = DOMUtil.createQName(subsubchild, DOMUtil.getElementText(subsubchild));
267 subsubchild = DOMUtil.getNextSiblingElement(subsubchild);
268 if (subsubchild != null) {
269 checkElementName(subsubchild, SoapMarshaler.SOAP_12_FAULTSUBCODE);
270 throw new SoapFault(SoapFault.RECEIVER, "Unsupported nested subcodes");
271 }
272 }
273 // Fault reason
274 child = DOMUtil.getNextSiblingElement(child);
275 checkElementName(child, SoapMarshaler.SOAP_12_FAULTREASON);
276 subchild = DOMUtil.getFirstChildElement(child);
277 checkElementName(subchild, SoapMarshaler.SOAP_12_FAULTTEXT);
278 reason = DOMUtil.getElementText(subchild);
279 subchild = DOMUtil.getNextSiblingElement(subchild);
280 if (subchild != null) {
281 throw new SoapFault(SoapFault.RECEIVER, "Unsupported multiple reasons");
282 }
283 // Fault node
284 child = DOMUtil.getNextSiblingElement(child);
285 QName childname = DOMUtil.getQName(child);
286 if (SoapMarshaler.SOAP_12_FAULTNODE.equals(childname)) {
287 node = URI.create(DOMUtil.getElementText(child));
288 child = DOMUtil.getNextSiblingElement(child);
289 childname = DOMUtil.getQName(child);
290 }
291 // Fault role
292 if (SoapMarshaler.SOAP_12_FAULTROLE.equals(childname)) {
293 role = URI.create(DOMUtil.getElementText(child));
294 child = DOMUtil.getNextSiblingElement(child);
295 childname = DOMUtil.getQName(child);
296 }
297 // Fault details
298 if (SoapMarshaler.SOAP_12_FAULTDETAIL.equals(childname)) {
299 details = getDetailsAsSource(child);
300 child = DOMUtil.getNextSiblingElement(child);
301 childname = DOMUtil.getQName(child);
302 }
303 // Nothing should be left
304 if (childname != null) {
305 throw new SoapFault(SoapFault.SENDER, "Unexpected element: " + childname);
306 }
307 }
308 SoapFault fault = new SoapFault(code, subcode, reason, node, role, details);
309 return fault;
310 }
311
312 private Source getDetailsAsSource(Element parent) throws SoapFault {
313 Element main = DOMUtil.getFirstChildElement(parent);
314 Source details = null;
315 if (main != null && DOMUtil.getNextSiblingElement(main) == null) {
316 details = new DOMSource(main);
317 } else if (main != null) {
318 // Wrap nodes in a parent element
319 Document document = null;
320 try {
321 document = new SourceTransformer().createDocument();
322 } catch (ParserConfigurationException e) {
323 throw new SoapFault(e);
324 }
325 Element parentNode = document.createElement(SoapMarshaler.MULTIPLE_DETAILS_NODE_WRAPPER);
326 NodeList nodes = parent.getChildNodes();
327 for (int i = 0; i < nodes.getLength(); i++) {
328 parentNode.appendChild(document.importNode(nodes.item(i), true));
329 }
330 document.appendChild(parentNode);
331 details = new DOMSource(document);
332 }
333 return details;
334 }
335
336 private SoapFault readFaultUsingStax(XMLStreamReader reader) throws SoapFault {
337 try {
338 FragmentStreamReader rh = new FragmentStreamReader(reader);
339 Document doc = (Document) marshaler.getSourceTransformer().toDOMNode(
340 new StaxSource(rh));
341 return readFaultUsingDom(doc.getDocumentElement());
342 } catch (SoapFault e) {
343 throw e;
344 } catch (Exception e) {
345 throw new SoapFault(e);
346 }
347 }
348
349 private void checkElementName(Element element, QName expected) throws SoapFault {
350 QName name= DOMUtil.getQName(element);
351 if (!expected.equals(name)) {
352 throw new SoapFault(SoapFault.SENDER, "Expected element: " + expected + " but found " + name);
353 }
354 }
355
356 private void parseHeaders(SoapMessage message, XMLStreamReader reader)
357 throws Exception {
358 while (reader.nextTag() != XMLStreamConstants.END_ELEMENT) {
359 QName hn = reader.getName();
360 FragmentStreamReader rh = new FragmentStreamReader(reader);
361 Document doc = (Document) marshaler.getSourceTransformer().toDOMNode(
362 new StaxSource(rh));
363 DocumentFragment df = doc.createDocumentFragment();
364 df.appendChild(doc.getDocumentElement());
365 message.addHeader(hn, df);
366 }
367 }
368
369 public SoapMessage read(MimeMessage mime) throws Exception {
370 final Object content = mime.getContent();
371 if (content instanceof MimeMultipart) {
372 MimeMultipart multipart = (MimeMultipart) content;
373 ContentType type = new ContentType(mime.getContentType());
374 String contentId = type.getParameter("start");
375 // Get request
376 MimeBodyPart contentPart = null;
377 if (contentId != null) {
378 contentPart = (MimeBodyPart) multipart.getBodyPart(contentId);
379 } else {
380 for (int i = 0; i < multipart.getCount(); i++) {
381 MimeBodyPart contentPart2 = (MimeBodyPart) multipart.getBodyPart(i);
382 String contentType = contentPart2.getContentType();
383
384 if (contentType.indexOf("xml") >= 0) {
385 contentPart = contentPart2;
386 break;
387 }
388 }
389 }
390
391 SoapMessage message = null;
392 if (contentPart != null) {
393 message = read(contentPart.getInputStream());
394 } else {
395 message = new SoapMessage();
396 message.setSource(EMPTY_CONTENT);
397 }
398
399 // Get attachments
400 for (int i = 0; i < multipart.getCount(); i++) {
401 MimeBodyPart part = (MimeBodyPart) multipart.getBodyPart(i);
402 if (part != contentPart) {
403 String id = part.getContentID();
404 if (id == null) {
405 id = "Part" + i;
406 } else if (id.startsWith("<")) {
407 id = id.substring(1, id.length() - 1);
408 }
409 message.addAttachment(id, part.getDataHandler());
410 }
411 }
412 return message;
413 } else {
414 throw new UnsupportedOperationException(
415 "Expected a javax.mail.internet.MimeMultipart object");
416 }
417 }
418
419 }