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.ByteArrayOutputStream;
020 import java.io.FilterOutputStream;
021 import java.io.IOException;
022 import java.io.OutputStream;
023 import java.net.URI;
024 import java.util.ArrayList;
025 import java.util.Enumeration;
026 import java.util.Iterator;
027 import java.util.List;
028 import java.util.Map;
029 import java.util.Properties;
030
031 import javax.activation.DataHandler;
032 import javax.mail.Header;
033 import javax.mail.Session;
034 import javax.mail.internet.MimeBodyPart;
035 import javax.mail.internet.MimeMessage;
036 import javax.mail.internet.MimeMultipart;
037 import javax.xml.XMLConstants;
038 import javax.xml.namespace.QName;
039 import javax.xml.parsers.ParserConfigurationException;
040 import javax.xml.stream.XMLStreamException;
041 import javax.xml.stream.XMLStreamReader;
042 import javax.xml.stream.XMLStreamWriter;
043 import javax.xml.transform.Source;
044 import javax.xml.transform.TransformerException;
045 import javax.xml.transform.dom.DOMSource;
046 import javax.xml.transform.stream.StreamResult;
047
048 import org.apache.servicemix.jbi.jaxp.SourceTransformer;
049 import org.apache.servicemix.jbi.jaxp.W3CDOMStreamReader;
050 import org.apache.servicemix.jbi.jaxp.XMLStreamHelper;
051 import org.apache.servicemix.jbi.util.ByteArrayDataSource;
052 import org.apache.servicemix.soap.SoapFault;
053 import org.w3c.dom.DocumentFragment;
054 import org.w3c.dom.Element;
055 import org.w3c.dom.Node;
056 import org.w3c.dom.NodeList;
057 import org.xml.sax.SAXException;
058
059 /**
060 *
061 * @author Guillaume Nodet
062 * @version $Revision: 1.5 $
063 * @since 3.0
064 */
065 public class SoapWriter {
066
067 public static final String SOAP_PART_ID = "soap-request";
068
069 private SoapMessage message;
070
071 private String contentType;
072
073 private SoapMarshaler marshaler;
074
075 private MimeMultipart parts;
076
077 public SoapWriter(SoapMarshaler marshaler, SoapMessage message) {
078 this.marshaler = marshaler;
079 this.message = message;
080 this.contentType = prepare();
081 }
082
083 public String getContentType() {
084 return contentType;
085 }
086
087 public void write(OutputStream out) throws Exception {
088 if (message.hasAttachments()) {
089 writeMultipartMessage(out);
090 } else {
091 writeSimpleMessage(out);
092 }
093 }
094
095 private String prepare() {
096 if (message.hasAttachments()) {
097 parts = new MimeMultipart("related; type=\"text/xml\"; start=\"<" + SOAP_PART_ID + ">\"");
098 return parts.getContentType();
099 } else {
100 return "text/xml";
101 }
102 }
103
104 private void writeSimpleMessage(OutputStream out) throws Exception {
105 if (message.getDocument() != null) {
106 marshaler.getSourceTransformer().toResult(new DOMSource(message.getDocument()), new StreamResult(out));
107 return;
108 }
109 XMLStreamWriter writer = marshaler.getOutputFactory().createXMLStreamWriter(out);
110 writer.writeStartDocument();
111 if (marshaler.isSoap()) {
112 writeSoapEnvelope(writer);
113 } else {
114 if (message.hasHeaders()) {
115 throw new IllegalStateException("SOAP headers found on non-soap message");
116 }
117 if (message.getFault() != null) {
118 if (message.getFault().getDetails() != null) {
119 XMLStreamReader reader = marshaler.getSourceTransformer().toXMLStreamReader(
120 message.getFault().getDetails());
121 XMLStreamHelper.copy(reader, writer);
122 } else {
123 throw new IllegalStateException("Cannot write non xml faults for non soap messages");
124 }
125 } else if (message.getSource() != null) {
126 writeContents(writer);
127 }
128 }
129 writer.writeEndDocument();
130 writer.flush();
131 }
132
133 private void writeMultipartMessage(OutputStream out) throws Exception {
134 Session session = Session.getDefaultInstance(new Properties(), null);
135 MimeMessage mime = new MimeMessage(session);
136 // Add soap part
137 MimeBodyPart soapPart = new MimeBodyPart();
138 soapPart.setContentID("<" + SOAP_PART_ID + ">");
139 ByteArrayOutputStream baos = new ByteArrayOutputStream();
140 writeSimpleMessage(baos);
141 soapPart.setDataHandler(new DataHandler(new ByteArrayDataSource(baos.toByteArray(), "text/xml")));
142 parts.addBodyPart(soapPart);
143 // Add attachments
144 for (Iterator itr = message.getAttachments().entrySet().iterator(); itr.hasNext();) {
145 Map.Entry entry = (Map.Entry) itr.next();
146 String id = (String) entry.getKey();
147 DataHandler dh = (DataHandler) entry.getValue();
148 MimeBodyPart part = new MimeBodyPart();
149 part.setDataHandler(dh);
150 part.setContentID("<" + id + ">");
151 parts.addBodyPart(part);
152 }
153 mime.setContent(parts);
154 mime.setHeader(SoapMarshaler.MIME_CONTENT_TYPE, getContentType());
155 // We do not want headers, so
156 // * retrieve all headers
157 // * skip first 2 bytes (CRLF)
158 mime.saveChanges();
159 Enumeration headersEnum = mime.getAllHeaders();
160 List headersList = new ArrayList();
161 while (headersEnum.hasMoreElements()) {
162 headersList.add(((Header) headersEnum.nextElement()).getName().toLowerCase());
163 }
164 String[] headers = (String[]) headersList.toArray(new String[0]);
165 // Skip first 2 bytes
166 OutputStream os = new FilterOutputStream(out) {
167 private int nb = 0;
168 public void write(int b) throws IOException {
169 if (++nb > 2) {
170 super.write(b);
171 }
172 }
173 };
174 // Write
175 mime.writeTo(os, headers);
176 }
177
178 public void writeSoapEnvelope(XMLStreamWriter writer) throws Exception {
179 QName envelope = getEnvelopeName();
180 String soapUri = envelope.getNamespaceURI();
181 String soapPrefix = envelope.getPrefix();
182 writer.setPrefix(soapPrefix, soapUri);
183 writer.writeStartElement(soapPrefix, SoapMarshaler.ENVELOPE, soapUri);
184 if (!marshaler.isRepairingNamespace()) {
185 writer.writeNamespace(soapPrefix, soapUri);
186 // XMLStreamHelper.writeNamespacesExcludingPrefixAndNamespace(out, in, soapPrefix, soapUri);
187 }
188 // Write Header
189 if (message.getHeaders() != null && message.getHeaders().size() > 0) {
190 writer.writeStartElement(soapPrefix, SoapMarshaler.HEADER, soapUri);
191 for (Iterator it = message.getHeaders().values().iterator(); it.hasNext();) {
192 DocumentFragment df = (DocumentFragment) it.next();
193 Element e = (Element) df.getFirstChild();
194 XMLStreamHelper.copy(new W3CDOMStreamReader(e), writer);
195 }
196 writer.writeEndElement();
197 }
198 // Write Body
199 writer.writeStartElement(soapPrefix, SoapMarshaler.BODY, soapUri);
200 if (message.getFault() != null) {
201 writeFault(writer);
202 } else if (message.getSource() != null) {
203 writeContents(writer);
204 }
205 writer.writeEndElement();
206 writer.writeEndElement();
207 }
208
209 private void writeContents(XMLStreamWriter writer) throws Exception {
210 XMLStreamReader reader = marshaler.getSourceTransformer().toXMLStreamReader(message.getSource());
211 XMLStreamHelper.copy(reader, writer);
212 }
213
214 private void writeFault(XMLStreamWriter writer) throws Exception {
215 QName envelope = getEnvelopeName();
216 String soapUri = envelope.getNamespaceURI();
217 if (SoapMarshaler.SOAP_11_URI.equals(soapUri)) {
218 writeSoap11Fault(writer);
219 } else if (SoapMarshaler.SOAP_12_URI.equals(soapUri)) {
220 writeSoap12Fault(writer);
221 } else {
222 throw new IllegalStateException("Unknown soap namespace: " + soapUri);
223 }
224 }
225
226 private void writeSoap11Fault(XMLStreamWriter writer) throws Exception {
227 QName envelope = getEnvelopeName();
228 String soapUri = envelope.getNamespaceURI();
229 String soapPrefix = envelope.getPrefix();
230 writer.setPrefix(soapPrefix, soapUri);
231 SoapFault fault = message.getFault();
232 fault.translateCodeTo11();
233
234 writer.writeStartElement(soapPrefix, SoapMarshaler.FAULT, soapUri);
235 QName code = fault.getCode();
236 if (code != null) {
237 XMLStreamHelper.writeStartElement(writer, SoapMarshaler.SOAP_11_FAULTCODE);
238 XMLStreamHelper.writeTextQName(writer, code);
239 writer.writeEndElement();
240 }
241 String reason = fault.getReason();
242 if (reason == null && fault.getCause() != null) {
243 reason = fault.getCause().toString();
244 }
245 XMLStreamHelper.writeStartElement(writer, SoapMarshaler.SOAP_11_FAULTSTRING);
246 if (reason != null) {
247 writer.writeCharacters(reason);
248 }
249 writer.writeEndElement();
250 URI node = fault.getNode();
251 if (node != null) {
252 XMLStreamHelper.writeStartElement(writer, SoapMarshaler.SOAP_11_FAULTACTOR);
253 writer.writeCharacters(node.toString());
254 writer.writeEndElement();
255 }
256 Source details = fault.getDetails();
257 if (details != null) {
258 XMLStreamHelper.writeStartElement(writer, SoapMarshaler.SOAP_11_FAULTDETAIL);
259 writeDetails(writer, details);
260 writer.writeEndElement();
261 }
262
263 writer.writeEndElement();
264 }
265
266 private void writeSoap12Fault(XMLStreamWriter writer) throws Exception {
267 QName envelope = getEnvelopeName();
268 String soapUri = envelope.getNamespaceURI();
269 String soapPrefix = envelope.getPrefix();
270 writer.setPrefix(soapPrefix, soapUri);
271 SoapFault fault = message.getFault();
272 fault.translateCodeTo12();
273
274 writer.writeStartElement(soapPrefix, SoapMarshaler.FAULT, soapUri);
275 QName code = fault.getCode();
276 if (code != null) {
277 XMLStreamHelper.writeStartElement(writer, SoapMarshaler.SOAP_12_FAULTCODE);
278 XMLStreamHelper.writeStartElement(writer, SoapMarshaler.SOAP_12_FAULTVALUE);
279 XMLStreamHelper.writeTextQName(writer, code);
280 writer.writeEndElement();
281 QName subcode = fault.getSubcode();
282 if (subcode != null) {
283 XMLStreamHelper.writeStartElement(writer, SoapMarshaler.SOAP_12_FAULTSUBCODE);
284 XMLStreamHelper.writeStartElement(writer, SoapMarshaler.SOAP_12_FAULTVALUE);
285 XMLStreamHelper.writeTextQName(writer, subcode);
286 writer.writeEndElement();
287 writer.writeEndElement();
288 }
289 writer.writeEndElement();
290 }
291 String reason = fault.getReason();
292 if (reason == null && fault.getCause() != null) {
293 reason = fault.getCause().toString();
294 }
295 XMLStreamHelper.writeStartElement(writer, SoapMarshaler.SOAP_12_FAULTREASON);
296 XMLStreamHelper.writeStartElement(writer, SoapMarshaler.SOAP_12_FAULTTEXT);
297 writer.writeAttribute(XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI, "lang", "en");
298 if (reason != null) {
299 writer.writeCharacters(reason);
300 }
301 writer.writeEndElement();
302 writer.writeEndElement();
303 URI node = fault.getNode();
304 if (node != null) {
305 XMLStreamHelper.writeStartElement(writer, SoapMarshaler.SOAP_12_FAULTNODE);
306 writer.writeCharacters(node.toString());
307 writer.writeEndElement();
308 }
309
310 URI role = fault.getRole();
311 if (role != null) {
312 XMLStreamHelper.writeStartElement(writer, SoapMarshaler.SOAP_12_FAULTROLE);
313 writer.writeCharacters(role.toString());
314 writer.writeEndElement();
315 }
316
317 Source details = fault.getDetails();
318 if (details != null) {
319 XMLStreamHelper.writeStartElement(writer, SoapMarshaler.SOAP_12_FAULTDETAIL);
320 writeDetails(writer, details);
321 writer.writeEndElement();
322 }
323
324 writer.writeEndElement();
325 }
326
327 private void writeDetails(XMLStreamWriter writer, Source details) throws ParserConfigurationException, IOException, SAXException, TransformerException, XMLStreamException {
328 SourceTransformer st = new SourceTransformer();
329 DOMSource domDetails = st.toDOMSource(details);
330 Node detailsNode = domDetails.getNode().getFirstChild();
331 if ( SoapMarshaler.MULTIPLE_DETAILS_NODE_WRAPPER.equals(detailsNode.getNodeName()) ) {
332 NodeList children = detailsNode.getChildNodes();
333 for ( int i = 0; i < children.getLength(); i++ ) {
334 Node node = children.item(i);
335 if ( node.getNodeType() == Node.ELEMENT_NODE ) {
336 XMLStreamReader reader = marshaler.getSourceTransformer().toXMLStreamReader(new DOMSource(node));
337 XMLStreamHelper.copy(reader, writer);
338 }
339 }
340 } else {
341 XMLStreamReader reader = marshaler.getSourceTransformer().toXMLStreamReader(details);
342 XMLStreamHelper.copy(reader, writer);
343 }
344 }
345
346 protected QName getEnvelopeName() {
347 QName name = message.getEnvelopeName();
348 if (name == null) {
349 name = new QName(marshaler.getSoapUri(), SoapMarshaler.ENVELOPE, marshaler.getPrefix());
350 } else if (name.getPrefix() == null) {
351 name = new QName(name.getNamespaceURI(), name.getLocalPart(), marshaler.getPrefix());
352 }
353 return name;
354 }
355
356 }