View Javadoc

1   /*
2    * Copyright 2006 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.soap.axiom;
18  
19  import java.io.IOException;
20  import java.io.OutputStream;
21  import java.io.StringWriter;
22  import java.io.UnsupportedEncodingException;
23  import java.util.Iterator;
24  import javax.activation.DataHandler;
25  import javax.xml.stream.XMLStreamException;
26  
27  import org.apache.axiom.attachments.Attachments;
28  import org.apache.axiom.om.OMElement;
29  import org.apache.axiom.om.OMException;
30  import org.apache.axiom.om.OMOutputFormat;
31  import org.apache.axiom.om.impl.MIMEOutputUtils;
32  import org.apache.axiom.om.impl.MTOMConstants;
33  import org.apache.axiom.soap.SOAPBody;
34  import org.apache.axiom.soap.SOAPEnvelope;
35  import org.apache.axiom.soap.SOAPFactory;
36  import org.apache.axiom.soap.SOAPMessage;
37  import org.apache.axiom.soap.SOAPProcessingException;
38  
39  import org.springframework.util.Assert;
40  import org.springframework.util.StringUtils;
41  import org.springframework.ws.mime.Attachment;
42  import org.springframework.ws.soap.AbstractSoapMessage;
43  import org.springframework.ws.soap.SoapEnvelope;
44  import org.springframework.ws.soap.SoapMessage;
45  import org.springframework.ws.soap.SoapVersion;
46  import org.springframework.ws.soap.support.SoapUtils;
47  import org.springframework.ws.transport.TransportConstants;
48  import org.springframework.ws.transport.TransportOutputStream;
49  
50  /**
51   * AXIOM-specific implementation of the {@link SoapMessage} interface. Created via the {@link AxiomSoapMessageFactory},
52   * wraps a {@link SOAPMessage}.
53   *
54   * @author Arjen Poutsma
55   * @see SOAPMessage
56   * @since 1.0.0
57   */
58  public class AxiomSoapMessage extends AbstractSoapMessage {
59  
60      private static final String EMPTY_SOAP_ACTION = "\"\"";
61  
62      private SOAPMessage axiomMessage;
63  
64      private final SOAPFactory axiomFactory;
65  
66      private final Attachments attachments;
67  
68      private final boolean payloadCaching;
69  
70      private AxiomSoapEnvelope envelope;
71  
72      private String soapAction;
73  
74      /**
75       * Create a new, empty <code>AxiomSoapMessage</code>.
76       *
77       * @param soapFactory the AXIOM SOAPFactory
78       */
79      public AxiomSoapMessage(SOAPFactory soapFactory) {
80          this(soapFactory, true);
81      }
82  
83      /**
84       * Create a new, empty <code>AxiomSoapMessage</code>.
85       *
86       * @param soapFactory the AXIOM SOAPFactory
87       */
88      public AxiomSoapMessage(SOAPFactory soapFactory, boolean payloadCaching) {
89          SOAPEnvelope soapEnvelope = soapFactory.getDefaultEnvelope();
90          axiomFactory = soapFactory;
91          axiomMessage = axiomFactory.createSOAPMessage(soapEnvelope, soapEnvelope.getBuilder());
92          attachments = new Attachments();
93          this.payloadCaching = payloadCaching;
94          soapAction = EMPTY_SOAP_ACTION;
95      }
96  
97      /**
98       * Create a new <code>AxiomSoapMessage</code> based on the given AXIOM <code>SOAPMessage</code>.
99       *
100      * @param soapMessage    the AXIOM SOAPMessage
101      * @param soapAction     the value of the SOAP Action header
102      * @param payloadCaching whether the contents of the SOAP body should be cached or not
103      */
104     public AxiomSoapMessage(SOAPMessage soapMessage, String soapAction, boolean payloadCaching) {
105         this(soapMessage, new Attachments(), soapAction, payloadCaching);
106     }
107 
108     /**
109      * Create a new <code>AxiomSoapMessage</code> based on the given AXIOM <code>SOAPMessage</code> and attachments.
110      *
111      * @param soapMessage    the AXIOM SOAPMessage
112      * @param attachments    the attachments
113      * @param soapAction     the value of the SOAP Action header
114      * @param payloadCaching whether the contents of the SOAP body should be cached or not
115      */
116     public AxiomSoapMessage(SOAPMessage soapMessage,
117                             Attachments attachments,
118                             String soapAction,
119                             boolean payloadCaching) {
120         Assert.notNull(soapMessage, "'soapMessage' must not be null");
121         Assert.notNull(attachments, "'attachments' must not be null");
122         axiomMessage = soapMessage;
123         axiomFactory = (SOAPFactory) soapMessage.getSOAPEnvelope().getOMFactory();
124         this.attachments = attachments;
125         if (!StringUtils.hasLength(soapAction)) {
126             soapAction = EMPTY_SOAP_ACTION;
127         }
128         this.soapAction = soapAction;
129         this.payloadCaching = payloadCaching;
130     }
131 
132     /** Return the AXIOM <code>SOAPMessage</code> that this <code>AxiomSoapMessage</code> is based on. */
133     public final SOAPMessage getAxiomMessage() {
134         return axiomMessage;
135     }
136 
137     /**
138      * Sets the AXIOM <code>SOAPMessage</code> that this <code>AxiomSoapMessage</code> is based on.
139      * <p/>
140      * Calling this method also clears the SOAP Action property.
141      */
142     public final void setAxiomMessage(SOAPMessage axiomMessage) {
143         Assert.notNull(axiomMessage, "'axiomMessage' must not be null");
144         this.axiomMessage = axiomMessage;
145         this.envelope = null;
146         this.soapAction = EMPTY_SOAP_ACTION;
147     }
148 
149     public SoapEnvelope getEnvelope() {
150         if (envelope == null) {
151             try {
152                 envelope = new AxiomSoapEnvelope(axiomMessage.getSOAPEnvelope(), axiomFactory, payloadCaching);
153             }
154             catch (SOAPProcessingException ex) {
155                 throw new AxiomSoapEnvelopeException(ex);
156             }
157         }
158         return envelope;
159     }
160 
161     public String getSoapAction() {
162         return soapAction;
163     }
164 
165     public void setSoapAction(String soapAction) {
166         soapAction = SoapUtils.escapeAction(soapAction);
167         this.soapAction = soapAction;
168     }
169 
170     public boolean isXopPackage() {
171         try {
172             return MTOMConstants.MTOM_TYPE.equals(attachments.getAttachmentSpecType());
173         }
174         catch (NullPointerException ex) {
175             // gotta love Axis2
176             return false;
177         }
178     }
179 
180     public boolean convertToXopPackage() {
181         return false;
182     }
183 
184     public Attachment getAttachment(String contentId) {
185         Assert.hasLength(contentId, "contentId must not be empty");
186         if (contentId.startsWith("<") && contentId.endsWith(">")) {
187             contentId = contentId.substring(1, contentId.length() - 1);
188         }
189         DataHandler dataHandler = attachments.getDataHandler(contentId);
190         return dataHandler != null ? new AxiomAttachment(contentId, dataHandler) : null;
191     }
192 
193     public Iterator getAttachments() {
194         return new AxiomAttachmentIterator();
195     }
196 
197     public Attachment addAttachment(String contentId, DataHandler dataHandler) {
198         Assert.hasLength(contentId, "contentId must not be empty");
199         Assert.notNull(dataHandler, "dataHandler must not be null");
200         attachments.addDataHandler(contentId, dataHandler);
201         return new AxiomAttachment(contentId, dataHandler);
202     }
203 
204     public void writeTo(OutputStream outputStream) throws IOException {
205         try {
206             String charsetEncoding = axiomMessage.getCharsetEncoding();
207 
208             OMOutputFormat format = new OMOutputFormat();
209             format.setCharSetEncoding(charsetEncoding);
210             format.setSOAP11(getVersion() == SoapVersion.SOAP_11);
211             boolean hasAttachments = !attachments.getContentIDSet().isEmpty();
212             if (hasAttachments) {
213                 if (isXopPackage()) {
214                     format.setDoOptimize(true);
215                 }
216                 else {
217                     format.setDoingSWA(true);
218                 }
219             }
220             if (outputStream instanceof TransportOutputStream) {
221                 TransportOutputStream transportOutputStream = (TransportOutputStream) outputStream;
222                 String contentType = format.getContentType();
223                 if (!hasAttachments) {
224                     contentType += "; charset=\"" + charsetEncoding + "\"";
225                 }
226                 if (SoapVersion.SOAP_11 == getVersion()) {
227                     transportOutputStream.addHeader(TransportConstants.HEADER_SOAP_ACTION, soapAction);
228                 }
229                 else if (SoapVersion.SOAP_12 == getVersion()) {
230                     contentType += "; action=" + soapAction;
231                 }
232                 transportOutputStream.addHeader(TransportConstants.HEADER_CONTENT_TYPE, contentType);
233             }
234             if (!(format.isOptimized()) & format.isDoingSWA()) {
235                 writeSwAMessage(outputStream, format);
236             }
237             else {
238                 if (payloadCaching) {
239                     axiomMessage.serialize(outputStream, format);
240                 }
241                 else {
242                     axiomMessage.serializeAndConsume(outputStream, format);
243                 }
244             }
245             outputStream.flush();
246         }
247         catch (XMLStreamException ex) {
248             throw new AxiomSoapMessageException("Could not write message to OutputStream: " + ex.getMessage(), ex);
249         }
250         catch (OMException ex) {
251             throw new AxiomSoapMessageException("Could not write message to OutputStream: " + ex.getMessage(), ex);
252         }
253     }
254 
255     private void writeSwAMessage(OutputStream outputStream, OMOutputFormat format)
256             throws XMLStreamException, UnsupportedEncodingException {
257         StringWriter writer = new StringWriter();
258         SOAPEnvelope envelope = axiomMessage.getSOAPEnvelope();
259         if (payloadCaching) {
260             envelope.serialize(writer, format);
261         }
262         else {
263             envelope.serializeAndConsume(writer, format);
264         }
265         MIMEOutputUtils.writeSOAPWithAttachmentsMessage(writer, outputStream, attachments, format);
266     }
267 
268     public String toString() {
269         StringBuffer buffer = new StringBuffer("AxiomSoapMessage");
270         if (payloadCaching) {
271             try {
272                 SOAPEnvelope envelope = axiomMessage.getSOAPEnvelope();
273                 if (envelope != null) {
274                     SOAPBody body = envelope.getBody();
275                     if (body != null) {
276                         OMElement bodyElement = body.getFirstElement();
277                         if (bodyElement != null) {
278                             buffer.append(' ');
279                             buffer.append(bodyElement.getQName());
280                         }
281                     }
282                 }
283             }
284             catch (OMException ex) {
285                 // ignore
286             }
287         }
288         return buffer.toString();
289     }
290 
291     private class AxiomAttachmentIterator implements Iterator {
292 
293         private final Iterator iterator;
294 
295         private AxiomAttachmentIterator() {
296             iterator = attachments.getContentIDSet().iterator();
297         }
298 
299         public boolean hasNext() {
300             return iterator.hasNext();
301         }
302 
303         public Object next() {
304             String contentId = (String) iterator.next();
305             DataHandler dataHandler = attachments.getDataHandler(contentId);
306             return new AxiomAttachment(contentId, dataHandler);
307         }
308 
309         public void remove() {
310             iterator.remove();
311         }
312     }
313 
314 }