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.OMText;
022: import org.apache.axiom.om.impl.dom.DOOMAbstractFactory;
023: import org.apache.axiom.om.impl.dom.DocumentImpl;
024: import org.apache.axiom.om.impl.dom.TextImpl;
025: import org.apache.axiom.om.util.Base64;
026: import org.apache.axis2.saaj.util.SAAJDataSource;
027: import org.apache.axis2.transport.http.HTTPConstants;
028:
029: import javax.activation.DataHandler;
030: import javax.xml.soap.AttachmentPart;
031: import javax.xml.soap.MimeHeader;
032: import javax.xml.soap.MimeHeaders;
033: import javax.xml.soap.SOAPException;
034: import javax.xml.transform.stream.StreamSource;
035: import java.awt.image.BufferedImage;
036: import java.io.ByteArrayInputStream;
037: import java.io.ByteArrayOutputStream;
038: import java.io.IOException;
039: import java.io.InputStream;
040: import java.io.OutputStream;
041: import java.util.Iterator;
042:
043: /**
044: *
045: */
046: public class AttachmentPartImpl extends AttachmentPart {
047: private DataHandler dataHandler;
048:
049: private MimeHeaders mimeHeaders = new MimeHeaders();
050: private String attachmentFile;
051:
052: private OMText omText;
053: private boolean isAttachmentReferenced;
054:
055: /**
056: * Check whether at least one of the headers of this object matches a provided header
057: *
058: * @param headers
059: * @return <b>true</b> if at least one header of this AttachmentPart matches a header in the
060: * provided <code>headers</code> parameter, <b>false</b> if none of the headers of this
061: * AttachmentPart matches at least one of the header in the provided
062: * <code>headers</code> parameter
063: */
064: public boolean matches(MimeHeaders headers) {
065: for (Iterator i = headers.getAllHeaders(); i.hasNext();) {
066: MimeHeader hdr = (javax.xml.soap.MimeHeader) i.next();
067: String values[] = mimeHeaders.getHeader(hdr.getName());
068: boolean found = false;
069: if (values != null) {
070: for (int j = 0; j < values.length; j++) {
071: if (!hdr.getValue().equalsIgnoreCase(values[j])) {
072: continue;
073: }
074: found = true;
075: break;
076: }
077: }
078: if (!found) {
079: return false;
080: }
081: }
082: return true;
083: }
084:
085: public boolean isAttachmentReferenced() {
086: return isAttachmentReferenced;
087: }
088:
089: public void setAttachmentReferenced(boolean attachmentReferenced) {
090: isAttachmentReferenced = attachmentReferenced;
091: }
092:
093: /**
094: * Returns the number of bytes in this <CODE> AttachmentPart</CODE> object.
095: *
096: * @return the size of this <CODE>AttachmentPart</CODE> object in bytes or -1 if the size cannot
097: * be determined
098: * @throws javax.xml.soap.SOAPException if the content of this attachment is corrupted of if
099: * there was an exception while trying to determine the
100: * size.
101: */
102: public int getSize() throws SOAPException {
103: if (dataHandler == null) {
104: return 0;
105: }
106: ByteArrayOutputStream bout = new ByteArrayOutputStream();
107: try {
108: dataHandler.writeTo(bout);
109: } catch (Exception ex) {
110: throw new SOAPException(ex);
111: }
112: return bout.size();
113: }
114:
115: /**
116: * Clears out the content of this <CODE> AttachmentPart</CODE> object. The MIME header portion
117: * is left untouched.
118: */
119: public void clearContent() {
120: dataHandler = null;
121: omText = null;
122: }
123:
124: /**
125: * Gets the content of this <code>AttachmentPart</code> object as a Java object. The type of the
126: * returned Java object depends on <ol> <li> the <code>DataContentHandler</code> object that is
127: * used to interpret the bytes </li> <li> the <code>Content-Type</code> given in the header</li>
128: * </ol>
129: * <p/>
130: * For the MIME content types "text/plain", "text/html" and "text/xml", the
131: * <code>DataContentHandler</code> object does the conversions to and from the Java types
132: * corresponding to the MIME types. For other MIME types,the <code>DataContentHandler</code>
133: * object can return an <code>InputStream</code> object that contains the content data as raw
134: * bytes.
135: * <p/>
136: * A JAXM-compliant implementation must, as a minimum, return a <code>java.lang.String</code>
137: * object corresponding to any content stream with a <code>Content-Type</code> value of
138: * <code>text/plain</code>, a <code>javax.xml.transform.StreamSource</code> object corresponding
139: * to a content stream with a <code>Content-Type</code> value of <code>text/xml</code>, a
140: * <code>java.awt.Image</code> object corresponding to a content stream with a
141: * <code>Content-Type</code> value of <code>image/gif</code> or <code>image/jpeg</code>. For
142: * those content types that an installed <code>DataContentHandler</code> object does not
143: * understand, the <code>DataContentHandler</code> object is required to return a
144: * <code>java.io.InputStream</code> object with the raw bytes.
145: *
146: * @return a Java object with the content of this <CODE> AttachmentPart</CODE> object
147: * @throws javax.xml.soap.SOAPException if there is no content set into this <CODE>AttachmentPart</CODE>
148: * object or if there was a data transformation error
149: */
150: public Object getContent() throws SOAPException {
151: if (dataHandler == null) {
152: throw new SOAPException(
153: "No content is present in this AttachmentPart");
154: }
155: try {
156: String contentType = dataHandler.getContentType();
157: if (contentType.equals(HTTPConstants.MEDIA_TYPE_TEXT_XML)
158: || contentType
159: .equals(HTTPConstants.MEDIA_TYPE_APPLICATION_XML)) {
160: StreamSource streamSource = new StreamSource();
161: streamSource.setInputStream(dataHandler
162: .getInputStream());
163: return streamSource;
164: } else if (contentType.equals("text/plain")
165: || contentType.equals("text/html")) {
166: return (String) dataHandler.getContent();
167: } else {
168: try {
169: return dataHandler.getContent();
170: } catch (Exception e) {
171: //If the underlying DataContentHandler can't handle the object contents,
172: //we will return an inputstream of raw bytes representing the content data
173: return dataHandler.getDataSource().getInputStream();
174: }
175: }
176: } catch (IOException e) {
177: throw new SOAPException(e.getMessage());
178: }
179: }
180:
181: /**
182: * Sets the content of this attachment part to that of the given <CODE>Object</CODE> and sets
183: * the value of the <CODE> Content-Type</CODE> header to the given type. The type of the
184: * <CODE>Object</CODE> should correspond to the value given for the <CODE>Content-Type</CODE>.
185: * This depends on the particular set of <CODE>DataContentHandler</CODE> objects in use.
186: *
187: * @param object the Java object that makes up the content for this attachment part
188: * @param contentType the MIME string that specifies the type of the content
189: * @throws IllegalArgumentException if the contentType does not match the type of the content
190: * object, or if there was no <CODE> DataContentHandler</CODE>
191: * object for this content object
192: * @see #getContent()
193: */
194: public void setContent(Object object, String contentType) {
195: SAAJDataSource source;
196: setMimeHeader(HTTPConstants.HEADER_CONTENT_TYPE, contentType);
197: Object contentObject;
198: if (object instanceof String) {
199: try {
200: String s = (String) object;
201: java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(
202: s.getBytes());
203: source = new SAAJDataSource(bais,
204: SAAJDataSource.MAX_MEMORY_DISK_CACHED,
205: contentType, true);
206: extractFilename(source);
207: this .dataHandler = new DataHandler(source);
208: contentObject = object;
209: } catch (java.io.IOException io) {
210: throw new java.lang.IllegalArgumentException(
211: "Illegal Argument");
212: }
213: } else if (object instanceof java.io.InputStream) {
214: try {
215:
216: source = new SAAJDataSource(
217: (java.io.InputStream) object,
218: SAAJDataSource.MIN_MEMORY_DISK_CACHED,
219: contentType, true);
220: extractFilename(source);
221: this .dataHandler = new DataHandler(source);
222: contentObject = null; // the stream has been consumed
223: } catch (java.io.IOException io) {
224: throw new java.lang.IllegalArgumentException(
225: "Illegal Argument");
226: }
227: } else if (object instanceof StreamSource) {
228: try {
229: source = new SAAJDataSource(((StreamSource) object)
230: .getInputStream(),
231: SAAJDataSource.MAX_MEMORY_DISK_CACHED,
232: contentType, true);
233: extractFilename(source);
234: this .dataHandler = new DataHandler(source);
235: contentObject = null; // the stream has been consumed
236: } catch (java.io.IOException io) {
237: throw new java.lang.IllegalArgumentException(
238: "Illegal Argument");
239: }
240: } else if (object instanceof BufferedImage) {
241: try {
242: this .dataHandler = new DataHandler(object, contentType);
243: contentObject = null; // the stream has been consumed
244: } catch (Exception e) {
245: throw new java.lang.IllegalArgumentException(e
246: .getMessage());
247: }
248: } else if (object instanceof byte[]) {
249: try {
250: contentObject = null;
251: java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(
252: (byte[]) object);
253: source = new SAAJDataSource(bais,
254: SAAJDataSource.MAX_MEMORY_DISK_CACHED,
255: contentType, true);
256: extractFilename(source);
257:
258: this .dataHandler = new DataHandler(source);
259: contentObject = object;
260: } catch (Exception e) {
261: throw new java.lang.IllegalArgumentException(e
262: .getMessage());
263: }
264: } else {
265: throw new java.lang.IllegalArgumentException(
266: "Illegal Argument");
267: }
268: }
269:
270: /**
271: * Gets the <CODE>DataHandler</CODE> object for this <CODE> AttachmentPart</CODE> object.
272: *
273: * @return the <CODE>DataHandler</CODE> object associated with this <CODE>AttachmentPart</CODE>
274: * object
275: * @throws javax.xml.soap.SOAPException if there is no data in this <CODE>AttachmentPart</CODE>
276: * object
277: */
278: public DataHandler getDataHandler() throws SOAPException {
279: //if (getContent() == null) {
280: // throw new SOAPException("No Content present in the Attachment part");
281: //}
282: //commented to fix AXIS2-778
283: if (dataHandler == null) {
284: throw new SOAPException(
285: "No Content present in the Attachment part");
286: }
287:
288: return dataHandler;
289: }
290:
291: /**
292: * Sets the given <CODE>DataHandler</CODE> object as the data handler for this
293: * <CODE>AttachmentPart</CODE> object. Typically, on an incoming message, the data handler is
294: * automatically set. When a message is being created and populated with content, the
295: * <CODE>setDataHandler</CODE> method can be used to get data from various data sources into the
296: * message.
297: *
298: * @param datahandler <CODE>DataHandler</CODE> object to be set
299: * @throws IllegalArgumentException if there was a problem with the specified <CODE>
300: * DataHandler</CODE> object
301: */
302: public void setDataHandler(DataHandler datahandler) {
303: if (datahandler != null) {
304: this .dataHandler = datahandler;
305: setMimeHeader(HTTPConstants.HEADER_CONTENT_TYPE,
306: datahandler.getContentType());
307: omText = DOOMAbstractFactory.getOMFactory().createOMText(
308: datahandler, true);
309: } else {
310: throw new IllegalArgumentException(
311: "Cannot set null DataHandler");
312: }
313: }
314:
315: /**
316: * Removes all MIME headers that match the given name.
317: *
318: * @param header - the string name of the MIME header/s to be removed
319: */
320: public void removeMimeHeader(String header) {
321: mimeHeaders.removeHeader(header);
322: }
323:
324: /** Removes all the MIME header entries. */
325: public void removeAllMimeHeaders() {
326: mimeHeaders.removeAllHeaders();
327: }
328:
329: /**
330: * Gets all the values of the header identified by the given <CODE>String</CODE>.
331: *
332: * @param name the name of the header; example: "Content-Type"
333: * @return a <CODE>String</CODE> array giving the value for the specified header
334: * @see #setMimeHeader(String, String) setMimeHeader(java.lang.String, java.lang.String)
335: */
336: public String[] getMimeHeader(String name) {
337: return mimeHeaders.getHeader(name);
338: }
339:
340: /**
341: * Changes the first header entry that matches the given name to the given value, adding a new
342: * header if no existing header matches. This method also removes all matching headers but the
343: * first.
344: * <p/>
345: * <P>Note that RFC822 headers can only contain US-ASCII characters.</P>
346: *
347: * @param name a <CODE>String</CODE> giving the name of the header for which to search
348: * @param value a <CODE>String</CODE> giving the value to be set for the header whose name
349: * matches the given name
350: * @throws IllegalArgumentException if there was a problem with the specified mime header name
351: * or value
352: */
353: public void setMimeHeader(String name, String value) {
354: mimeHeaders.setHeader(name, value);
355: }
356:
357: /**
358: * Adds a MIME header with the specified name and value to this <CODE>AttachmentPart</CODE>
359: * object.
360: * <p/>
361: * <P>Note that RFC822 headers can contain only US-ASCII characters.</P>
362: *
363: * @param name a <CODE>String</CODE> giving the name of the header to be added
364: * @param value a <CODE>String</CODE> giving the value of the header to be added
365: * @throws IllegalArgumentException if there was a problem with the specified mime header name
366: * or value
367: */
368: public void addMimeHeader(String name, String value) {
369: mimeHeaders.addHeader(name, value);
370: }
371:
372: /**
373: * Retrieves all the headers for this <CODE> AttachmentPart</CODE> object as an iterator over
374: * the <CODE> MimeHeader</CODE> objects.
375: *
376: * @return an <CODE>Iterator</CODE> object with all of the Mime headers for this
377: * <CODE>AttachmentPart</CODE> object
378: */
379: public Iterator getAllMimeHeaders() {
380: return mimeHeaders.getAllHeaders();
381: }
382:
383: /**
384: * Retrieves all <CODE>MimeHeader</CODE> objects that match a name in the given array.
385: *
386: * @param names a <CODE>String</CODE> array with the name(s) of the MIME headers to be returned
387: * @return all of the MIME headers that match one of the names in the given array as an
388: * <CODE>Iterator</CODE> object
389: */
390: public Iterator getMatchingMimeHeaders(String names[]) {
391: return mimeHeaders.getMatchingHeaders(names);
392: }
393:
394: /**
395: * Retrieves all <CODE>MimeHeader</CODE> objects whose name does not match a name in the given
396: * array.
397: *
398: * @param names a <CODE>String</CODE> array with the name(s) of the MIME headers not to be
399: * returned
400: * @return all of the MIME headers in this <CODE> AttachmentPart</CODE> object except those that
401: * match one of the names in the given array. The nonmatching MIME headers are returned
402: * as an <CODE>Iterator</CODE> object.
403: */
404: public Iterator getNonMatchingMimeHeaders(String names[]) {
405: return mimeHeaders.getNonMatchingHeaders(names);
406: }
407:
408: public InputStream getBase64Content() throws SOAPException {
409: byte[] rawData = getRawContentBytes();
410: ByteArrayOutputStream out = new ByteArrayOutputStream();
411: try {
412: Base64.encode(rawData, 0, rawData.length, out);
413: return new ByteArrayInputStream(out.toByteArray());
414: } catch (IOException e) {
415: throw new SOAPException(e);
416: }
417: }
418:
419: /**
420: * Gets the content of this AttachmentPart object as an InputStream as if a call had been made
421: * to getContent and no DataContentHandler had been registered for the content-type of this
422: * AttachmentPart.Note that reading from the returned InputStream would result in consuming the
423: * data in the stream. It is the responsibility of the caller to reset the InputStream
424: * appropriately before calling a Subsequent API. If a copy of the raw attachment content is
425: * required then the getRawContentBytes() API should be used instead.
426: *
427: * @return an InputStream from which the raw data contained by the AttachmentPart can be
428: * accessed.
429: * @throws SOAPException - if there is no content set into this AttachmentPart object or if
430: * there was a data transformation error.
431: * @since SAAJ 1.3
432: */
433: public InputStream getRawContent() throws SOAPException {
434: try {
435: if (dataHandler == null) {
436: throw new SOAPException("No content set");
437: }
438: return dataHandler.getInputStream();
439: } catch (IOException e) {
440: throw new SOAPException(e);
441: }
442: }
443:
444: /**
445: * Gets the content of this AttachmentPart object as a byte[] array as if a call had been made
446: * to getContent and no DataContentHandler had been registered for the content-type of this
447: * AttachmentPart.
448: *
449: * @return a byte[] array containing the raw data of the AttachmentPart.
450: * @throws SOAPException - if there is no content set into this AttachmentPart object or if
451: * there was a data transformation error.
452: * @since SAAJ 1.3
453: */
454: public byte[] getRawContentBytes() throws SOAPException {
455: if (dataHandler == null) {
456: throw new SOAPException("Content is null");
457: }
458: ByteArrayOutputStream bout = new ByteArrayOutputStream();
459: try {
460: dataHandler.writeTo(bout);
461: } catch (Exception ex) {
462: throw new SOAPException(ex);
463: }
464: return bout.toByteArray();
465: }
466:
467: /**
468: * Sets the content of this attachment part from the Base64 source InputStream and sets the
469: * value of the Content-Type header to the value contained in contentType, This method would
470: * first decode the base64 input and write the resulting raw bytes to the attachment. A
471: * subsequent call to getSize() may not be an exact measure of the content size.
472: *
473: * @param content - the base64 encoded data to add to the attachment part contentType - the
474: * value to set into the Content-Type header
475: * @throws SOAPException - if there is an error in setting the content java.lang.NullPointerException
476: * - if content is null
477: */
478: public void setBase64Content(InputStream content, String contentType)
479: throws SOAPException {
480: if (content == null) {
481: throw new SOAPException("Content is null");
482: }
483: OutputStream outputStream = new ByteArrayOutputStream();
484: byte[] buffer = new byte[1024];
485: int read;
486: try {
487: while ((read = content.read(buffer, 0, buffer.length)) > 0) {
488: outputStream.write(buffer, 0, read);
489: }
490: String contentString = outputStream.toString();
491: if (Base64.isValidBase64Encoding(contentString)) {
492: setContent(Base64.decode(contentString), contentType);
493: } else {
494: throw new SOAPException("Not a valid Base64 encoding");
495: }
496: } catch (IOException ex) {
497: throw new SOAPException(ex);
498: }
499: }
500:
501: /**
502: * Sets the content of this attachment part to that contained by the InputStream content and
503: * sets the value of the Content-Type header to the value contained in contentType.A subsequent
504: * call to getSize() may not be an exact measure of the content size.
505: *
506: * @param content - the raw data to add to the attachment part contentType - the value to set
507: * into the Content-Type header
508: * @throws SOAPException - if there is an error in setting the content java.lang.NullPointerException
509: * - if content is null
510: */
511: public void setRawContent(InputStream content, String contentType)
512: throws SOAPException {
513: if (content == null) {
514: throw new SOAPException("content is null");
515: }
516: setContent(content, contentType);
517: }
518:
519: /**
520: * Sets the content of this attachment part to that contained by the byte[] array content and
521: * sets the value of the Content-Type header to the value contained in contentType.
522: *
523: * @param content - the raw data to add to the attachment part contentType - the value to set
524: * into the Content-Type header offset - the offset in the byte array of the
525: * content len - the number of bytes that form the content
526: * @throws SOAPException - if an there is an error in setting the content or content is null
527: * @since SAAJ 1.3
528: */
529:
530: public void setRawContentBytes(byte[] content, int offset, int len,
531: String contentType) throws SOAPException {
532: //TODO - how to use offset & len?
533: if (content == null) {
534: throw new SOAPException("Content is null");
535: }
536: setContent(content, contentType);
537: }
538:
539: /**
540: * Retrieve the OMText
541: *
542: * @return the OMText
543: * @throws SOAPException If omText is not available
544: */
545: public OMText getOMText() throws SOAPException {
546: if (omText == null) {
547: throw new SOAPException("OMText set to null");
548: }
549: return omText;
550: }
551:
552: public TextImpl getText(DocumentImpl doc) {
553: return new TextImpl(doc, omText.getText(), doc.getOMFactory());
554: }
555:
556: /**
557: * Set the filename of this attachment part.
558: *
559: * @param path the new file path
560: */
561: protected void setAttachmentFile(String path) {
562: attachmentFile = path;
563: }
564:
565: /**
566: * Detach the attachment file from this class, so it is not cleaned up. This has the side-effect
567: * of making subsequent calls to getAttachmentFile() return <code>null</code>.
568: */
569: public void detachAttachmentFile() {
570: attachmentFile = null;
571: }
572:
573: /**
574: * Get the filename of this attachment.
575: *
576: * @return the filename or null for an uncached file
577: */
578: public String getAttachmentFile() {
579: return attachmentFile;
580: }
581:
582: private void extractFilename(SAAJDataSource source) {
583: if (source.getDiskCacheFile() != null) {
584: String path = source.getDiskCacheFile().getAbsolutePath();
585: setAttachmentFile(path);
586: }
587: }
588: }
|