| /* |
| * Copyright 2001-2004 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.axis.attachments; |
| |
| import org.apache.axis.Part; |
| import org.apache.axis.components.logger.LogFactory; |
| import org.apache.axis.transport.http.HTTPConstants; |
| import org.apache.axis.utils.Messages; |
| import org.apache.axis.utils.SessionUtils; |
| import org.apache.axis.utils.IOUtils; |
| import org.apache.commons.logging.Log; |
| |
| import javax.activation.DataHandler; |
| import javax.imageio.ImageIO; |
| import javax.xml.soap.SOAPException; |
| import javax.xml.transform.stream.StreamSource; |
| import java.util.Iterator; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.InputStream; |
| |
| /** |
| * An attachment part. |
| * |
| * |
| */ |
| public class AttachmentPart extends javax.xml.soap.AttachmentPart |
| implements Part { |
| |
| /** Field log */ |
| protected static Log log = |
| LogFactory.getLog(AttachmentPart.class.getName()); |
| |
| /** |
| * The data handler. |
| * <p> |
| * TODO: make private? |
| * */ |
| javax.activation.DataHandler datahandler = null; |
| |
| /** Field mimeHeaders. */ |
| private javax.xml.soap.MimeHeaders mimeHeaders = |
| new javax.xml.soap.MimeHeaders(); |
| |
| private Object contentObject; |
| |
| /** |
| * The name of a file used to store the data. |
| */ |
| private String attachmentFile; |
| |
| /** |
| * Bulds a new <code>AttachmentPart</code>. |
| */ |
| public AttachmentPart() { |
| setMimeHeader(HTTPConstants.HEADER_CONTENT_ID, SessionUtils.generateSessionId()); |
| } |
| |
| /** |
| * Bulds a new <code>AttachmentPart</code> with a <code>DataHandler</code>. |
| * |
| * @param dh the <code>DataHandler</code> |
| */ |
| public AttachmentPart(javax.activation.DataHandler dh) { |
| setMimeHeader(HTTPConstants.HEADER_CONTENT_ID, |
| SessionUtils.generateSessionId()); |
| datahandler = dh; |
| if(dh != null) { |
| setMimeHeader(HTTPConstants.HEADER_CONTENT_TYPE, dh.getContentType()); |
| javax.activation.DataSource ds = dh.getDataSource(); |
| if (ds instanceof ManagedMemoryDataSource) { |
| extractFilename((ManagedMemoryDataSource)ds); //and get the filename if appropriate |
| |
| } |
| } |
| } |
| |
| // fixme: be aware, this may never be called |
| /** |
| * On death, we clean up our file. |
| * |
| * @throws Throwable if anything went wrong during finalization |
| */ |
| protected void finalize() throws Throwable { |
| dispose(); |
| } |
| |
| /** |
| * Get the data handler. |
| * |
| * @return the activation <code>DataHandler</code> |
| */ |
| public javax.activation.DataHandler getActivationDataHandler() { |
| return datahandler; |
| } |
| |
| /** |
| * getContentType |
| * |
| * @return content type |
| */ |
| public String getContentType() { |
| return getFirstMimeHeader(HTTPConstants.HEADER_CONTENT_TYPE); |
| } |
| |
| /** |
| * Add the specified MIME header, as per JAXM. |
| * |
| * @param header |
| * @param value |
| */ |
| public void addMimeHeader(String header, String value) { |
| mimeHeaders.addHeader(header, value); |
| } |
| |
| /** |
| * Get the specified MIME header. |
| * |
| * @param header |
| * |
| * @return |
| */ |
| public String getFirstMimeHeader(String header) { |
| String[] values = mimeHeaders.getHeader(header.toLowerCase()); |
| if ((values != null) && (values.length > 0)) { |
| return values[0]; |
| } |
| return null; |
| } |
| |
| /** |
| * check if this Part's mimeheaders matches the one passed in. |
| * TODO: Am not sure about the logic. |
| * |
| * @param headers the <code>MimeHeaders</code> to check |
| * @return true if all header name, values in <code>headers</code> are |
| * found, false otherwise |
| */ |
| public boolean matches(javax.xml.soap.MimeHeaders headers) { |
| for (Iterator i = headers.getAllHeaders(); i.hasNext();) { |
| javax.xml.soap.MimeHeader hdr = (javax.xml.soap.MimeHeader) i.next(); |
| String values[] = mimeHeaders.getHeader(hdr.getName()); |
| boolean found = false; |
| if (values != null) { |
| for (int j = 0; j < values.length; j++) { |
| if (!hdr.getValue().equalsIgnoreCase(values[j])) { |
| continue; |
| } |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public String getContentLocation() { |
| return getFirstMimeHeader(HTTPConstants.HEADER_CONTENT_LOCATION); |
| } |
| |
| public void setContentLocation(String loc) { |
| setMimeHeader(HTTPConstants.HEADER_CONTENT_LOCATION, loc); |
| } |
| |
| public void setContentId(String newCid) { |
| setMimeHeader(HTTPConstants.HEADER_CONTENT_ID, newCid); |
| } |
| |
| public String getContentId() { |
| return getFirstMimeHeader(HTTPConstants.HEADER_CONTENT_ID); |
| } |
| |
| public java.util.Iterator getMatchingMimeHeaders(final String[] match) { |
| return mimeHeaders.getMatchingHeaders(match); |
| } |
| |
| public java.util.Iterator getNonMatchingMimeHeaders(final String[] match) { |
| return mimeHeaders.getNonMatchingHeaders(match); |
| } |
| |
| public Iterator getAllMimeHeaders() { |
| return mimeHeaders.getAllHeaders(); |
| } |
| |
| /** |
| * Changes the first header entry that matches the given name |
| * to the given value, adding a new header if no existing |
| * header matches. This method also removes all matching |
| * headers but the first. |
| * |
| * <P>Note that RFC822 headers can only contain US-ASCII |
| * characters.</P> |
| * @param name a <CODE>String</CODE> giving the |
| * name of the header for which to search |
| * @param value a <CODE>String</CODE> giving the |
| * value to be set for the header whose name matches the |
| * given name |
| * @throws java.lang.IllegalArgumentException if |
| * there was a problem with the specified mime header name |
| * or value |
| */ |
| public void setMimeHeader(String name, String value) { |
| mimeHeaders.setHeader(name, value); |
| } |
| |
| /** Removes all the MIME header entries. */ |
| public void removeAllMimeHeaders() { |
| mimeHeaders.removeAllHeaders(); |
| } |
| |
| /** |
| * Removes all MIME headers that match the given name. |
| * @param header - the string name of the MIME |
| * header/s to be removed |
| */ |
| public void removeMimeHeader(String header) { |
| mimeHeaders.removeHeader(header); |
| } |
| |
| /** |
| * Gets the <CODE>DataHandler</CODE> object for this <CODE> |
| * AttachmentPart</CODE> object. |
| * @return the <CODE>DataHandler</CODE> object associated with |
| * this <CODE>AttachmentPart</CODE> object |
| * @throws SOAPException if there is |
| * no data in this <CODE>AttachmentPart</CODE> object |
| */ |
| public DataHandler getDataHandler() throws SOAPException { |
| if(datahandler == null) { |
| throw new SOAPException(Messages.getMessage("noContent")); |
| } |
| return datahandler; |
| } |
| |
| /** |
| * Sets the given <CODE>DataHandler</CODE> object as the |
| * data handler for this <CODE>AttachmentPart</CODE> object. |
| * Typically, on an incoming message, the data handler is |
| * automatically set. When a message is being created and |
| * populated with content, the <CODE>setDataHandler</CODE> |
| * method can be used to get data from various data sources into |
| * the message. |
| * @param datahandler <CODE>DataHandler</CODE> object to |
| * be set |
| * @throws java.lang.IllegalArgumentException if |
| * there was a problem with the specified <CODE> |
| * DataHandler</CODE> object |
| */ |
| public void setDataHandler(DataHandler datahandler) { |
| if(datahandler == null) { |
| throw new java.lang.IllegalArgumentException( |
| Messages.getMessage("illegalArgumentException00")); |
| } |
| this.datahandler = datahandler; |
| setMimeHeader(HTTPConstants.HEADER_CONTENT_TYPE, datahandler.getContentType()); |
| //now look at the source of the data |
| javax.activation.DataSource ds = datahandler.getDataSource(); |
| if (ds instanceof ManagedMemoryDataSource) { |
| //and get the filename if appropriate |
| extractFilename((ManagedMemoryDataSource)ds); |
| } |
| |
| |
| } |
| |
| /** |
| * Gets the content of this <CODE>AttachmentPart</CODE> object |
| * as a Java object. The type of the returned Java object |
| * depends on (1) the <CODE>DataContentHandler</CODE> object |
| * that is used to interpret the bytes and (2) the <CODE> |
| * Content-Type</CODE> given in the header. |
| * |
| * <P>For the MIME content types "text/plain", "text/html" and |
| * "text/xml", the <CODE>DataContentHandler</CODE> object does |
| * the conversions to and from the Java types corresponding to |
| * the MIME types. For other MIME types,the <CODE> |
| * DataContentHandler</CODE> object can return an <CODE> |
| * InputStream</CODE> object that contains the content data as |
| * raw bytes.</P> |
| * |
| * <P>A JAXM-compliant implementation must, as a minimum, |
| * return a <CODE>java.lang.String</CODE> object corresponding |
| * to any content stream with a <CODE>Content-Type</CODE> |
| * value of <CODE>text/plain</CODE> and a <CODE> |
| * javax.xml.transform.StreamSource</CODE> object |
| * corresponding to a content stream with a <CODE> |
| * Content-Type</CODE> value of <CODE>text/xml</CODE>. For |
| * those content types that an installed <CODE> |
| * DataContentHandler</CODE> object does not understand, the |
| * <CODE>DataContentHandler</CODE> object is required to |
| * return a <CODE>java.io.InputStream</CODE> object with the |
| * raw bytes.</P> |
| * @return a Java object with the content of this <CODE> |
| * AttachmentPart</CODE> object |
| * @throws SOAPException if there is no content set |
| * into this <CODE>AttachmentPart</CODE> object or if there |
| * was a data transformation error |
| */ |
| public Object getContent() throws SOAPException { |
| if(contentObject != null) { |
| return contentObject; |
| } |
| |
| if(datahandler == null) { |
| throw new SOAPException(Messages.getMessage("noContent")); |
| } |
| |
| javax.activation.DataSource ds = datahandler.getDataSource(); |
| InputStream is = null; |
| try { |
| is = ds.getInputStream();; |
| } catch (java.io.IOException io) { |
| log.error(Messages.getMessage("javaIOException00"), io); |
| throw new SOAPException(io); |
| } |
| if (ds.getContentType().equals("text/plain")) { |
| try { |
| byte[] bytes = new byte[is.available()]; |
| IOUtils.readFully(is, bytes); |
| return new String(bytes); |
| } catch (java.io.IOException io) { |
| log.error(Messages.getMessage("javaIOException00"), io); |
| throw new SOAPException(io); |
| } |
| } else if (ds.getContentType().equals("text/xml")) { |
| return new StreamSource(is); |
| } else if (ds.getContentType().equals("image/gif") || |
| ds.getContentType().equals("image/jpeg")) { |
| try { |
| return ImageIO.read(is); |
| } catch (Exception ex) { |
| log.error(Messages.getMessage("javaIOException00"), ex); |
| throw new SOAPException(ex); |
| } |
| } |
| return is; |
| } |
| |
| |
| /** |
| * Sets the content of this attachment part to that of the |
| * given <CODE>Object</CODE> and sets the value of the <CODE> |
| * Content-Type</CODE> header to the given type. The type of the |
| * <CODE>Object</CODE> should correspond to the value given for |
| * the <CODE>Content-Type</CODE>. This depends on the particular |
| * set of <CODE>DataContentHandler</CODE> objects in use. |
| * @param object the Java object that makes up |
| * the content for this attachment part |
| * @param contentType the MIME string that |
| * specifies the type of the content |
| * @throws java.lang.IllegalArgumentException if |
| * the contentType does not match the type of the content |
| * object, or if there was no <CODE> |
| * DataContentHandler</CODE> object for this content |
| * object |
| * @see #getContent() getContent() |
| */ |
| public void setContent(Object object, String contentType) { |
| ManagedMemoryDataSource source = null; |
| setMimeHeader(HTTPConstants.HEADER_CONTENT_TYPE, contentType); |
| if (object instanceof String) { |
| try { |
| String s = (String) object; |
| java.io.ByteArrayInputStream bais = |
| new java.io.ByteArrayInputStream(s.getBytes()); |
| source = new ManagedMemoryDataSource(bais, |
| ManagedMemoryDataSource.MAX_MEMORY_DISK_CACHED, |
| contentType, true); |
| extractFilename(source); |
| datahandler = new DataHandler(source); |
| contentObject = object; |
| return; |
| } catch (java.io.IOException io) { |
| log.error(Messages.getMessage("javaIOException00"), io); |
| throw new java.lang.IllegalArgumentException( |
| Messages.getMessage("illegalArgumentException00")); |
| } |
| } else if (object instanceof java.io.InputStream) { |
| try { |
| source = new ManagedMemoryDataSource((java.io.InputStream) object, |
| ManagedMemoryDataSource.MAX_MEMORY_DISK_CACHED, |
| contentType, true); |
| extractFilename(source); |
| datahandler = new DataHandler(source); |
| contentObject = null; // the stream has been consumed |
| return; |
| } catch (java.io.IOException io) { |
| log.error(Messages.getMessage("javaIOException00"), io); |
| throw new java.lang.IllegalArgumentException(Messages.getMessage |
| ("illegalArgumentException00")); |
| } |
| } else if (object instanceof StreamSource) { |
| try { |
| source = new ManagedMemoryDataSource(((StreamSource)object).getInputStream(), |
| ManagedMemoryDataSource.MAX_MEMORY_DISK_CACHED, |
| contentType, true); |
| extractFilename(source); |
| datahandler = new DataHandler(source); |
| contentObject = null; // the stream has been consumed |
| return; |
| } catch (java.io.IOException io) { |
| log.error(Messages.getMessage("javaIOException00"), io); |
| throw new java.lang.IllegalArgumentException(Messages.getMessage |
| ("illegalArgumentException00")); |
| } |
| } else { |
| throw new java.lang.IllegalArgumentException( |
| Messages.getMessage("illegalArgumentException00")); |
| } |
| } |
| |
| /** |
| * Clears out the content of this <CODE> |
| * AttachmentPart</CODE> object. The MIME header portion is left |
| * untouched. |
| */ |
| public void clearContent() { |
| datahandler = null; |
| contentObject = null; |
| } |
| |
| /** |
| * Returns the number of bytes in this <CODE> |
| * AttachmentPart</CODE> object. |
| * @return the size of this <CODE>AttachmentPart</CODE> object |
| * in bytes or -1 if the size cannot be determined |
| * @throws SOAPException if the content of this |
| * attachment is corrupted of if there was an exception |
| * while trying to determine the size. |
| */ |
| public int getSize() throws SOAPException { |
| if (datahandler == null) { |
| return 0; |
| } |
| ByteArrayOutputStream bout = new ByteArrayOutputStream(); |
| try { |
| datahandler.writeTo(bout); |
| } catch (java.io.IOException ex) { |
| log.error(Messages.getMessage("javaIOException00"), ex); |
| throw new SOAPException(Messages.getMessage("javaIOException01", ex.getMessage()), ex); |
| } |
| return bout.size(); |
| } |
| |
| /** |
| * Gets all the values of the header identified by the given |
| * <CODE>String</CODE>. |
| * @param name the name of the header; example: |
| * "Content-Type" |
| * @return a <CODE>String</CODE> array giving the value for the |
| * specified header |
| * @see #setMimeHeader(java.lang.String, java.lang.String) setMimeHeader(java.lang.String, java.lang.String) |
| */ |
| public String[] getMimeHeader(String name) { |
| return mimeHeaders.getHeader(name); |
| } |
| |
| /** |
| * Content ID. |
| * |
| * @return the contentId reference value that should be used directly |
| * as an href in a SOAP element to reference this attachment. |
| * <B>Not part of JAX-RPC, JAX-M, SAAJ, etc. </B> |
| */ |
| public String getContentIdRef() { |
| return Attachments.CIDprefix + getContentId(); |
| } |
| |
| /** |
| * Maybe add file name to the attachment. |
| * |
| * @param source the source of the data |
| */ |
| |
| private void extractFilename(ManagedMemoryDataSource source) { |
| //check for there being a file |
| if(source.getDiskCacheFile()!=null) { |
| String path = source.getDiskCacheFile().getAbsolutePath(); |
| setAttachmentFile(path); |
| } |
| } |
| |
| /** |
| * Set the filename of this attachment part. |
| * |
| * @param path the new file path |
| */ |
| protected void setAttachmentFile(String path) { |
| attachmentFile=path; |
| } |
| |
| /** |
| * Detach the attachment file from this class, so it is not cleaned up. |
| * This has the side-effect of making subsequent calls to |
| * getAttachmentFile() return <code>null</code>. |
| */ |
| public void detachAttachmentFile() { |
| attachmentFile=null; |
| } |
| |
| /** |
| * Get the filename of this attachment. |
| * |
| * @return the filename or null for an uncached file |
| */ |
| public String getAttachmentFile() { |
| return attachmentFile; |
| } |
| |
| /** |
| * when an attachment part is disposed, any associated files |
| * are deleted, and the datahandler itself nulled. The object |
| * is no longer completely usable, at this point |
| */ |
| public synchronized void dispose() { |
| if (attachmentFile != null) { |
| javax.activation.DataSource ds = datahandler.getDataSource(); |
| if (ds instanceof ManagedMemoryDataSource) { |
| ((ManagedMemoryDataSource) ds).delete(); //and delete the file |
| } else { |
| File f = new File(attachmentFile); |
| //no need to check for existence here. |
| f.delete(); |
| } |
| //set the filename to null to stop repeated use |
| setAttachmentFile(null); |
| } |
| //clean up the datahandler, as it will have been |
| //invalidated if it was bound to a file; if it wasnt |
| //we get to release memory anyway |
| datahandler = null; |
| } |
| } |