001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.axis2.saaj;
020:
021: import org.apache.axiom.om.OMOutputFormat;
022: import org.apache.axiom.soap.impl.dom.soap11.SOAP11Factory;
023: import org.apache.axiom.soap.impl.dom.soap12.SOAP12Factory;
024: import org.apache.axis2.transport.http.HTTPConstants;
025:
026: import javax.xml.soap.AttachmentPart;
027: import javax.xml.soap.MimeHeader;
028: import javax.xml.soap.MimeHeaders;
029: import javax.xml.soap.SOAPBody;
030: import javax.xml.soap.SOAPElement;
031: import javax.xml.soap.SOAPException;
032: import javax.xml.soap.SOAPHeader;
033: import javax.xml.soap.SOAPMessage;
034: import javax.xml.soap.SOAPPart;
035: import java.io.IOException;
036: import java.io.InputStream;
037: import java.io.OutputStream;
038: import java.util.ArrayList;
039: import java.util.Collection;
040: import java.util.Hashtable;
041: import java.util.Iterator;
042: import java.util.Map;
043:
044: public class SOAPMessageImpl extends SOAPMessage {
045:
046: private SOAPPart soapPart;
047: private Collection attachmentParts = new ArrayList();
048: private MimeHeadersEx mimeHeaders;
049:
050: private Map props = new Hashtable();
051: private boolean saveRequired;
052:
053: public SOAPMessageImpl(SOAPEnvelopeImpl soapEnvelope) {
054: String contentType = null;
055:
056: if (mimeHeaders != null) {
057: String contentTypes[] = mimeHeaders
058: .getHeader(HTTPConstants.CONTENT_TYPE);
059: contentType = (contentTypes != null) ? contentTypes[0]
060: : null;
061: } else {
062: this .mimeHeaders = new MimeHeadersEx();
063: if (soapEnvelope.getOMFactory() instanceof SOAP11Factory) {
064: contentType = HTTPConstants.MEDIA_TYPE_TEXT_XML;
065: this .mimeHeaders.addHeader("content-type", contentType);
066: } else if (soapEnvelope.getOMFactory() instanceof SOAP12Factory) {
067: contentType = HTTPConstants.MEDIA_TYPE_APPLICATION_SOAP_XML;
068: this .mimeHeaders.addHeader("content-type", contentType);
069: }
070: }
071:
072: setCharsetEncoding(contentType);
073: soapPart = new SOAPPartImpl(this , soapEnvelope);
074: }
075:
076: public SOAPMessageImpl(InputStream inputstream,
077: javax.xml.soap.MimeHeaders mimeHeaders)
078: throws SOAPException {
079: String contentType = null;
080: String tmpContentType = "";
081: if (mimeHeaders != null) {
082: String contentTypes[] = mimeHeaders
083: .getHeader(HTTPConstants.CONTENT_TYPE);
084: if (contentTypes != null && contentTypes.length > 0) {
085: tmpContentType = contentTypes[0];
086: //tmpContentType can be like 'application/soap+xml; charset=UTF-8;'
087: //Only the first part is important
088: if (tmpContentType.indexOf(";") > -1) {
089: contentType = tmpContentType.substring(0,
090: tmpContentType.indexOf(";"));
091: } else {
092: contentType = tmpContentType;
093: }
094: }
095: }
096: //Setting the whole content-type string to CharsetEncoding.
097: //Is this correct?
098: setCharsetEncoding(tmpContentType);
099: if (contentType != null) {
100: soapPart = new SOAPPartImpl(this , inputstream, mimeHeaders);
101: } else {
102: soapPart = new SOAPPartImpl(this , inputstream);
103: }
104:
105: this .mimeHeaders = (mimeHeaders == null) ? new MimeHeadersEx()
106: : new MimeHeadersEx(mimeHeaders);
107: }
108:
109: /**
110: * Retrieves a description of this <CODE>SOAPMessage</CODE> object's content.
111: *
112: * @return a <CODE>String</CODE> describing the content of this message or <CODE>null</CODE> if
113: * no description has been set
114: * @see #setContentDescription(String) setContentDescription(java.lang.String)
115: */
116: public String getContentDescription() {
117: String values[] = mimeHeaders
118: .getHeader(HTTPConstants.HEADER_CONTENT_DESCRIPTION);
119: if (values != null && values.length > 0) {
120: return values[0];
121: }
122: return null;
123: }
124:
125: /**
126: * Sets the description of this <CODE>SOAPMessage</CODE> object's content with the given
127: * description.
128: *
129: * @param description a <CODE>String</CODE> describing the content of this message
130: * @see #getContentDescription() getContentDescription()
131: */
132: public void setContentDescription(String description) {
133: mimeHeaders.setHeader(HTTPConstants.HEADER_CONTENT_DESCRIPTION,
134: description);
135: }
136:
137: /**
138: * Gets the SOAP part of this <CODE>SOAPMessage</CODE> object.
139: * <p/>
140: * <p/>
141: * <P>If a <CODE>SOAPMessage</CODE> object contains one or more attachments, the SOAP Part must
142: * be the first MIME body part in the message.</P>
143: *
144: * @return the <CODE>SOAPPart</CODE> object for this <CODE> SOAPMessage</CODE> object
145: */
146: public SOAPPart getSOAPPart() {
147: return soapPart;
148: }
149:
150: /**
151: * Removes all <CODE>AttachmentPart</CODE> objects that have been added to this
152: * <CODE>SOAPMessage</CODE> object.
153: * <p/>
154: * <P>This method does not touch the SOAP part.</P>
155: */
156: public void removeAllAttachments() {
157: attachmentParts.clear();
158: }
159:
160: /**
161: * Gets a count of the number of attachments in this message. This count does not include the
162: * SOAP part.
163: *
164: * @return the number of <CODE>AttachmentPart</CODE> objects that are part of this
165: * <CODE>SOAPMessage</CODE> object
166: */
167: public int countAttachments() {
168: return attachmentParts.size();
169: }
170:
171: /**
172: * Retrieves all the <CODE>AttachmentPart</CODE> objects that are part of this
173: * <CODE>SOAPMessage</CODE> object.
174: *
175: * @return an iterator over all the attachments in this message
176: */
177: public Iterator getAttachments() {
178: return attachmentParts.iterator();
179: }
180:
181: /**
182: * Retrieves all the AttachmentPart objects that have header entries that match the specified
183: * headers. Note that a returned attachment could have headers in addition to those specified.
184: *
185: * @param headers a {@link javax.xml.soap.MimeHeaders} object containing the MIME headers for
186: * which to search
187: * @return an iterator over all attachments({@link javax.xml.soap.AttachmentPart}) that have a
188: * header that matches one of the given headers
189: */
190: public Iterator getAttachments(javax.xml.soap.MimeHeaders headers) {
191: Collection matchingAttachmentParts = new ArrayList();
192: Iterator iterator = getAttachments();
193: {
194: AttachmentPartImpl part;
195: while (iterator.hasNext()) {
196: part = (AttachmentPartImpl) iterator.next();
197: if (part.matches(headers)) {
198: matchingAttachmentParts.add(part);
199: }
200: }
201: }
202: return matchingAttachmentParts.iterator();
203: }
204:
205: /**
206: * Adds the given <CODE>AttachmentPart</CODE> object to this <CODE>SOAPMessage</CODE> object. An
207: * <CODE> AttachmentPart</CODE> object must be created before it can be added to a message.
208: *
209: * @param attachmentPart an <CODE> AttachmentPart</CODE> object that is to become part of this
210: * <CODE>SOAPMessage</CODE> object
211: * @throws IllegalArgumentException
212: *
213: */
214: public void addAttachmentPart(AttachmentPart attachmentPart) {
215: if (attachmentPart != null) {
216: attachmentParts.add(attachmentPart);
217: mimeHeaders.setHeader(HTTPConstants.CONTENT_TYPE,
218: "multipart/related");
219: }
220: }
221:
222: /**
223: * Creates a new empty <CODE>AttachmentPart</CODE> object. Note that the method
224: * <CODE>addAttachmentPart</CODE> must be called with this new <CODE>AttachmentPart</CODE>
225: * object as the parameter in order for it to become an attachment to this
226: * <CODE>SOAPMessage</CODE> object.
227: *
228: * @return a new <CODE>AttachmentPart</CODE> object that can be populated and added to this
229: * <CODE>SOAPMessage</CODE> object
230: */
231: public AttachmentPart createAttachmentPart() {
232: return new AttachmentPartImpl();
233: }
234:
235: /**
236: * Returns all the transport-specific MIME headers for this <CODE>SOAPMessage</CODE> object in a
237: * transport-independent fashion.
238: *
239: * @return a <CODE>MimeHeaders</CODE> object containing the <CODE>MimeHeader</CODE> objects
240: */
241: public javax.xml.soap.MimeHeaders getMimeHeaders() {
242: return mimeHeaders;
243: }
244:
245: /**
246: * Updates this <CODE>SOAPMessage</CODE> object with all the changes that have been made to it.
247: * This method is called automatically when a message is sent or written to by the methods
248: * <CODE>ProviderConnection.send</CODE>, <CODE> SOAPConnection.call</CODE>, or <CODE>
249: * SOAPMessage.writeTo</CODE>. However, if changes are made to a message that was received or to
250: * one that has already been sent, the method <CODE>saveChanges</CODE> needs to be called
251: * explicitly in order to save the changes. The method <CODE>saveChanges</CODE> also generates
252: * any changes that can be read back (for example, a MessageId in profiles that support a
253: * message id). All MIME headers in a message that is created for sending purposes are
254: * guaranteed to have valid values only after <CODE>saveChanges</CODE> has been called.
255: * <p/>
256: * <P>In addition, this method marks the point at which the data from all constituent
257: * <CODE>AttachmentPart</CODE> objects are pulled into the message.</P>
258: *
259: * @throws SOAPException if there was a problem saving changes to this message.
260: */
261: public void saveChanges() throws SOAPException {
262: saveRequired = false;
263: // TODO not sure of the implementation
264: }
265:
266: public void setSaveRequired() {
267: this .saveRequired = true;
268: }
269:
270: /**
271: * Indicates whether this <CODE>SOAPMessage</CODE> object has had the method {@link
272: * #saveChanges()} called on it.
273: *
274: * @return <CODE>true</CODE> if <CODE>saveChanges</CODE> has been called on this message at
275: * least once; <CODE> false</CODE> otherwise.
276: */
277: public boolean saveRequired() {
278: return saveRequired;
279: }
280:
281: /**
282: * Writes this <CODE>SOAPMessage</CODE> object to the given output stream. The externalization
283: * format is as defined by the SOAP 1.1 with Attachments specification.
284: * <p/>
285: * <P>If there are no attachments, just an XML stream is written out. For those messages that
286: * have attachments, <CODE>writeTo</CODE> writes a MIME-encoded byte stream.</P>
287: *
288: * @param out the <CODE>OutputStream</CODE> object to which this <CODE>SOAPMessage</CODE> object
289: * will be written
290: * @throws SOAPException if there was a problem in externalizing this SOAP message
291: * @throws IOException if an I/O error occurs
292: */
293: public void writeTo(OutputStream out) throws SOAPException,
294: IOException {
295: try {
296: OMOutputFormat format = new OMOutputFormat();
297: format
298: .setCharSetEncoding((String) getProperty(CHARACTER_SET_ENCODING));
299: String writeXmlDecl = (String) getProperty(WRITE_XML_DECLARATION);
300: if (writeXmlDecl == null || writeXmlDecl.equals("false")) {
301:
302: //SAAJ default case doesn't send XML decl
303: format.setIgnoreXMLDeclaration(true);
304: }
305:
306: //the writeTo method forces the elements to be built!!!
307: ((SOAPEnvelopeImpl) soapPart.getEnvelope()).getOMEnvelope()
308: .serialize(out, format);
309: saveChanges();
310: } catch (Exception e) {
311: throw new SOAPException(e);
312: }
313: }
314:
315: /**
316: * Associates the specified value with the specified property. If there was already a value
317: * associated with this property, the old value is replaced.
318: * <p/>
319: * The valid property names include <code>WRITE_XML_DECLARATION</code> and
320: * <code>CHARACTER_SET_ENCODING</code>. All of these standard SAAJ properties are prefixed by
321: * "javax.xml.soap". Vendors may also add implementation specific properties. These properties
322: * must be prefixed with package names that are unique to the vendor.
323: * <p/>
324: * Setting the property <code>WRITE_XML_DECLARATION</code> to <code>"true"</code> will cause an
325: * XML Declaration to be written out at the start of the SOAP message. The default value of
326: * "false" suppresses this declaration.
327: * <p/>
328: * The property <code>CHARACTER_SET_ENCODING</code> defaults to the value <code>"utf-8"</code>
329: * which causes the SOAP message to be encoded using UTF-8. Setting
330: * <code>CHARACTER_SET_ENCODING</code> to <code>"utf-16"</code> causes the SOAP message to be
331: * encoded using UTF-16.
332: * <p/>
333: * Some implementations may allow encodings in addition to UTF-8 and UTF-16. Refer to your
334: * vendor's documentation for details.
335: *
336: * @param property the property with which the specified value is to be associated
337: * @param value the value to be associated with the specified property
338: */
339: public void setProperty(String property, Object value) {
340: props.put(property, value);
341: }
342:
343: /**
344: * Retrieves value of the specified property.
345: *
346: * @param property the name of the property to retrieve
347: * @return the value of the property or <code>null</code> if no such property exists
348: * @throws SOAPException if the property name is not recognized
349: */
350: public Object getProperty(String property) throws SOAPException {
351: return props.get(property);
352: }
353:
354: /**
355: * Returns an AttachmentPart object that is associated with an attachment that is referenced by
356: * this SOAPElement or null if no such attachment exists. References can be made via an href
357: * attribute as described in SOAP Messages with Attachments (http://www.w3.org/TR/SOAPattachments#SOAPReferenceToAttachements)
358: * , or via a single Text child node containing a URI as described in the WS-I Attachments
359: * Profile 1.0 for elements of schema type ref:swaRef(ref:swaRef (http://www.wsi.org/Profiles/AttachmentsProfile-1.0-2004-08-24.html")
360: * ). These two mechanisms must be supported. The support for references via href attribute also
361: * implies that this method should also be supported on an element that is an xop:Include
362: * element (XOP (http://www.w3.org/2000/xp/Group/3/06/Attachments/XOP.html) ). other reference
363: * mechanisms may be supported by individual implementations of this standard. Contact your
364: * vendor for details.
365: *
366: * @param element - The SOAPElement containing the reference to an Attachment
367: * @return the referenced AttachmentPart or null if no such AttachmentPart exists or no
368: * reference can be found in this SOAPElement.
369: * @throws SOAPException - if there is an error in the attempt to access the attachment
370: */
371: public AttachmentPart getAttachment(SOAPElement soapelement)
372: throws SOAPException {
373: //TODO read strings from constants
374: Iterator iterator = getAttachments();
375: {
376: AttachmentPartImpl attachmentPart;
377: while (iterator.hasNext()) {
378: attachmentPart = (AttachmentPartImpl) iterator.next();
379: String[] contentIds = attachmentPart
380: .getMimeHeader("Content-Id");
381:
382: //References can be made via an href attribute as described in SOAP Messages
383: //with Attachments or via a single Text child node containing a URI
384: String reference = soapelement.getAttribute("href");
385: if (reference == null || reference.trim().length() == 0) {
386: reference = soapelement.getValue();
387: if (reference == null
388: || reference.trim().length() == 0) {
389: return null;
390: }
391: }
392:
393: for (int a = 0; a < contentIds.length; a++) {
394: //eg: cid:gifImage scenario
395: String idPart = reference.substring(reference
396: .indexOf(":") + 1);
397: idPart = "<" + idPart + ">";
398: if (idPart.equals(contentIds[a])) {
399: return attachmentPart;
400: }
401: }
402:
403: String[] contentLocations = attachmentPart
404: .getMimeHeader("Content-Location");
405: if (!(contentLocations == null)) {
406: //uri scenario
407: for (int b = 0; b < contentLocations.length; b++) {
408: if (reference.equals(contentLocations[b])) {
409: return attachmentPart;
410: }
411: }
412: }
413: }
414: }
415: return null;
416: }
417:
418: /**
419: * Removes all the AttachmentPart objects that have header entries that match the specified
420: * headers. Note that the removed attachment could have headers in addition to those specified.
421: *
422: * @param headers - a MimeHeaders object containing the MIME headers for which to search
423: * @since SAAJ 1.3
424: */
425: public void removeAttachments(MimeHeaders headers) {
426: Collection newAttachmentParts = new ArrayList();
427: Iterator attachmentPartsItr = attachmentParts.iterator();
428: for (Iterator iter = attachmentPartsItr; iter.hasNext();) {
429: AttachmentPart attachmentPart = (AttachmentPart) iter
430: .next();
431:
432: //Get all the headers
433: Iterator allMIMEHeaders = headers.getAllHeaders();
434: for (Iterator iterator = allMIMEHeaders; iterator.hasNext();) {
435: MimeHeader mimeHeader = (MimeHeader) iterator.next();
436: String[] headerValues = attachmentPart
437: .getMimeHeader(mimeHeader.getName());
438: //if values for this header name, do not remove it
439: if (headerValues.length != 0) {
440: if (!(headerValues[0].equals(mimeHeader.getValue()))) {
441: newAttachmentParts.add(attachmentPart);
442: }
443: }
444: }
445: }
446: attachmentParts.clear();
447: this .attachmentParts = newAttachmentParts;
448: }
449:
450: /**
451: * Gets the SOAP Header contained in this <code>SOAPMessage</code> object.
452: *
453: * @return the <code>SOAPHeader</code> object contained by this <code>SOAPMessage</code> object
454: * @throws javax.xml.soap.SOAPException if the SOAP Header does not exist or cannot be
455: * retrieved
456: */
457: public SOAPHeader getSOAPHeader() throws SOAPException {
458: return this .soapPart.getEnvelope().getHeader();
459: }
460:
461: /**
462: * Gets the SOAP Body contained in this <code>SOAPMessage</code> object.
463: *
464: * @return the <code>SOAPBody</code> object contained by this <code>SOAPMessage</code> object
465: * @throws javax.xml.soap.SOAPException if the SOAP Body does not exist or cannot be retrieved
466: */
467: public SOAPBody getSOAPBody() throws SOAPException {
468: return this .soapPart.getEnvelope().getBody();
469: }
470:
471: /**
472: * Set the character encoding based on the <code>contentType</code> parameter
473: *
474: * @param contentType
475: */
476: private void setCharsetEncoding(final String contentType) {
477: if (contentType != null) {
478: int delimiterIndex = contentType.lastIndexOf("charset");
479: if (delimiterIndex > 0) {
480: String charsetPart = contentType
481: .substring(delimiterIndex);
482: int charsetIndex = charsetPart.indexOf('=');
483: String charset = charsetPart
484: .substring(charsetIndex + 1).trim();
485: if ((charset.startsWith("\"") || charset
486: .startsWith("\'"))) {
487: charset = charset.substring(1, charset.length());
488: }
489: if ((charset.endsWith("\"") || charset.endsWith("\'"))) {
490: charset = charset
491: .substring(0, charset.length() - 1);
492: }
493: int index = charset.indexOf(';');
494: if (index != -1) {
495: charset = charset.substring(0, index);
496: }
497: setProperty(SOAPMessage.CHARACTER_SET_ENCODING, charset);
498: }
499: }
500: }
501: }
|