Java tutorial
/* * 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. */ /* @author Rob Jellinghaus (robj@unrealities.com) */ /* @author Rick Rineholt */ package org.apache.axis.attachments; import org.apache.axis.AxisFault; import org.apache.axis.Part; import org.apache.axis.SOAPPart; import org.apache.axis.components.logger.LogFactory; import org.apache.axis.utils.Messages; import org.apache.commons.logging.Log; import javax.activation.DataHandler; import javax.activation.DataSource; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; /** * Implements the Attachment interface, via an actual Hashmap of actual * AttachmentParts. */ public class AttachmentsImpl implements Attachments { protected static Log log = LogFactory.getLog(AttachmentsImpl.class.getName()); /** Field attachments. */ private HashMap attachments = new java.util.HashMap(); /** Field orderedAttachments. */ private LinkedList orderedAttachments = new LinkedList(); /** Field soapPart. */ protected SOAPPart soapPart = null; /** * The actual stream to manage the multi-related input stream. */ protected MultiPartInputStream mpartStream = null; /** * The form of the attachments, whether MIME or DIME. */ protected int sendtype = Attachments.SEND_TYPE_NOTSET; /** * This is the content location as specified in SOAP with Attachments. * This maybe null if the message had no Content-Location specifed. */ protected String contentLocation = null; /** * The HashMap for DataHandler Managements. */ private HashMap stackDataHandler = new HashMap(); /** * Used to distribute attachment streams without caching them. */ private IncomingAttachmentStreams _streams = null; private boolean _askedForAttachments = false; private boolean _askedForStreams = false; /** * Construct one of these on a parent Message. * Should only ever be called by Message constructor! * * @param intialContents should be anything but today only a stream is * supported. * @param contentType The mime content type of the stream for transports * that provide it. * @param contentLocation * * @throws org.apache.axis.AxisFault */ public AttachmentsImpl(Object intialContents, String contentType, String contentLocation) throws org.apache.axis.AxisFault { if (contentLocation != null) { contentLocation = contentLocation.trim(); if (contentLocation.length() == 0) { contentLocation = null; } } this.contentLocation = contentLocation; if (contentType != null) { if (contentType.equals(org.apache.axis.Message.MIME_UNKNOWN)) { } else { java.util.StringTokenizer st = new java.util.StringTokenizer(contentType, " \t;"); if (st.hasMoreTokens()) { String token = st.nextToken(); if (token.equalsIgnoreCase(org.apache.axis.Message.MIME_MULTIPART_RELATED)) { sendtype = SEND_TYPE_MIME; mpartStream = new org.apache.axis.attachments.MultiPartRelatedInputStream(contentType, (java.io.InputStream) intialContents); if (null == contentLocation) { // If the content location is not specified as // of the main message use the SOAP content location. contentLocation = mpartStream.getContentLocation(); if (contentLocation != null) { contentLocation = contentLocation.trim(); if (contentLocation.length() == 0) { contentLocation = null; } } } soapPart = new org.apache.axis.SOAPPart(null, mpartStream, false); MultiPartRelatedInputStream specificType = (MultiPartRelatedInputStream) mpartStream; _streams = new MultipartAttachmentStreams(specificType.boundaryDelimitedStream, specificType.orderedParts); } else if (token.equalsIgnoreCase(org.apache.axis.Message.MIME_APPLICATION_DIME)) { try { mpartStream = new MultiPartDimeInputStream((java.io.InputStream) intialContents); soapPart = new org.apache.axis.SOAPPart(null, mpartStream, false); } catch (Exception e) { throw org.apache.axis.AxisFault.makeFault(e); } sendtype = SEND_TYPE_DIME; MultiPartDimeInputStream specificType = (MultiPartDimeInputStream) mpartStream; _streams = new DimeAttachmentStreams(specificType.dimeDelimitedStream); } else if (token.indexOf(org.apache.axis.Message.CONTENT_TYPE_MTOM) != -1) { sendtype = SEND_TYPE_MTOM; } } } } } /** * Copies attachment references from the multipartStream to local list. * Done only once per object creation. * * @throws AxisFault */ private void mergeinAttachments() throws AxisFault { if (mpartStream != null) { Collection atts = mpartStream.getAttachments(); if (contentLocation == null) contentLocation = mpartStream.getContentLocation(); mpartStream = null; setAttachmentParts(atts); } } /** * This method uses getAttacmentByReference() to look for attachment. * If attachment has been found, it will be removed from the list, and * returned to the user. * @param reference The reference that referers to an attachment. * * @return The part associated with the removed attachment, or null. * * @throws org.apache.axis.AxisFault */ public Part removeAttachmentPart(String reference) throws org.apache.axis.AxisFault { if (_askedForStreams) { throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream")); } multipart = null; dimemultipart = null; mergeinAttachments(); Part removedPart = getAttachmentByReference(reference); if (removedPart != null) { attachments.remove(removedPart.getContentId()); attachments.remove(removedPart.getContentLocation()); orderedAttachments.remove(removedPart); } return removedPart; } /** * Adds an existing attachment to this list. * Note: Passed part will be bound to this message. * @param newPart new part to add * @return Part old attachment with the same Content-ID, or null. * * @throws org.apache.axis.AxisFault */ public Part addAttachmentPart(Part newPart) throws org.apache.axis.AxisFault { if (_askedForStreams) { throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream")); } multipart = null; dimemultipart = null; mergeinAttachments(); Part oldPart = (Part) attachments.put(newPart.getContentId(), newPart); if (oldPart != null) { orderedAttachments.remove(oldPart); attachments.remove(oldPart.getContentLocation()); } orderedAttachments.add(newPart); if (newPart.getContentLocation() != null) { attachments.put(newPart.getContentLocation(), newPart); } return oldPart; } public Part createAttachmentPart(Object datahandler) throws org.apache.axis.AxisFault { // Searching for the same attachements Integer key = new Integer(datahandler.hashCode()); if (stackDataHandler.containsKey(key)) { return (Part) stackDataHandler.get(key); } multipart = null; dimemultipart = null; mergeinAttachments(); if (!(datahandler instanceof javax.activation.DataHandler)) { throw new org.apache.axis.AxisFault(Messages.getMessage("unsupportedAttach", datahandler.getClass().getName(), javax.activation.DataHandler.class.getName())); } Part ret = new AttachmentPart((javax.activation.DataHandler) datahandler); addAttachmentPart(ret); // Store the current DataHandler with its key stackDataHandler.put(key, ret); return ret; } /** * Add the collection of parts. * * @param parts * * @throws org.apache.axis.AxisFault */ public void setAttachmentParts(java.util.Collection parts) throws org.apache.axis.AxisFault { if (_askedForStreams) { throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream")); } removeAllAttachments(); if ((parts != null) && !parts.isEmpty()) { for (java.util.Iterator i = parts.iterator(); i.hasNext();) { Object part = i.next(); if (null != part) { if (part instanceof Part) addAttachmentPart((Part) part); else createAttachmentPart(part); } } } } /** * This method should look at a refernce and determine if it is a CID: or * url to look for attachment. * <br> * Note: if Content-Id or Content-Location headers have changed by outside * code, lookup will not return proper values. In order to change these * values attachment should be removed, then added again. * @param reference The reference in the xml that referers to an attachment. * * @return The part associated with the attachment. * * @throws org.apache.axis.AxisFault */ public Part getAttachmentByReference(String reference) throws org.apache.axis.AxisFault { if (_askedForStreams) { throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream")); } if (null == reference) { return null; } reference = reference.trim(); if (0 == reference.length()) { return null; } mergeinAttachments(); //This search will pickit up if its fully qualified location or if it's a content-id // that is not prefixed by the cid. Part ret = (Part) attachments.get(reference); if (null != ret) return ret; if (!reference.startsWith(Attachments.CIDprefix) && (null != contentLocation)) { //Not a content-id check to see if its a relative location id. String fqreference = contentLocation; if (!fqreference.endsWith("/")) { fqreference += "/"; } if (reference.startsWith("/")) { fqreference += reference.substring(1); } else { fqreference += reference; } // lets see if we can get it as Content-Location ret = (AttachmentPart) attachments.get(fqreference); } if (null == ret && reference.startsWith(Attachments.CIDprefix)) { //This is a content-id lets see if we have it. ret = (Part) attachments.get(reference.substring(4)); } return ret; } /** * This method will return all attachments as a collection. * * @return A collection of attachments. * * @throws org.apache.axis.AxisFault */ public java.util.Collection getAttachments() throws org.apache.axis.AxisFault { if (_askedForStreams) { throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream")); } mergeinAttachments(); return new LinkedList(orderedAttachments); } /** * From the complex stream return the root part. * Today this is SOAP. * * @return the root <code>Part</code> */ public Part getRootPart() { return soapPart; } public void setRootPart(Part newRoot) { try { this.soapPart = (SOAPPart) newRoot; multipart = null; dimemultipart = null; } catch (ClassCastException e) { throw new ClassCastException(Messages.getMessage("onlySOAPParts")); } } /** multipart , cached entries for the stream of attachment that are going to be sent. */ javax.mail.internet.MimeMultipart multipart = null; DimeMultiPart dimemultipart = null; /** * Get the content length of the stream. * * @return the content length of the stream * * @throws org.apache.axis.AxisFault */ public long getContentLength() throws org.apache.axis.AxisFault { if (_askedForStreams) { throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream")); } mergeinAttachments(); int sendtype = this.sendtype == SEND_TYPE_NOTSET ? SEND_TYPE_DEFAULT : this.sendtype; try { if (sendtype == SEND_TYPE_MIME || sendtype == SEND_TYPE_MTOM) return org.apache.axis.attachments.MimeUtils.getContentLength(multipart != null ? multipart : (multipart = org.apache.axis.attachments.MimeUtils.createMP(soapPart.getAsString(), orderedAttachments, getSendType()))); else if (sendtype == SEND_TYPE_DIME) return createDimeMessage().getTransmissionSize(); } catch (Exception e) { throw AxisFault.makeFault(e); } return 0; } /** * Creates the DIME message * * @return a DIME part * * @throws org.apache.axis.AxisFault if the part could not be built */ protected DimeMultiPart createDimeMessage() throws org.apache.axis.AxisFault { int sendtype = this.sendtype == SEND_TYPE_NOTSET ? SEND_TYPE_DEFAULT : this.sendtype; if (sendtype == SEND_TYPE_DIME) { if (dimemultipart == null) { dimemultipart = new DimeMultiPart(); dimemultipart.addBodyPart(new DimeBodyPart(soapPart.getAsBytes(), DimeTypeNameFormat.URI, "http://schemas.xmlsoap.org/soap/envelope/", "uuid:714C6C40-4531-442E-A498-3AC614200295")); for (java.util.Iterator i = orderedAttachments.iterator(); i.hasNext();) { AttachmentPart part = (AttachmentPart) i.next(); DataHandler dh = AttachmentUtils.getActivationDataHandler(part); dimemultipart.addBodyPart(new DimeBodyPart(dh, part.getContentId())); } } } return dimemultipart; } /** * Write the content to the stream. * * @param os * * @throws org.apache.axis.AxisFault */ public void writeContentToStream(java.io.OutputStream os) throws org.apache.axis.AxisFault { int sendtype = this.sendtype == SEND_TYPE_NOTSET ? SEND_TYPE_DEFAULT : this.sendtype; try { mergeinAttachments(); if (sendtype == SEND_TYPE_MIME || sendtype == SEND_TYPE_MTOM) { org.apache.axis.attachments.MimeUtils.writeToMultiPartStream(os, (multipart != null) ? multipart : (multipart = org.apache.axis.attachments.MimeUtils .createMP(soapPart.getAsString(), orderedAttachments, getSendType()))); for (java.util.Iterator i = orderedAttachments.iterator(); i.hasNext();) { AttachmentPart part = (AttachmentPart) i.next(); DataHandler dh = AttachmentUtils.getActivationDataHandler(part); DataSource ds = dh.getDataSource(); if ((ds != null) && (ds instanceof ManagedMemoryDataSource)) { ((ManagedMemoryDataSource) ds).delete(); } } } else if (sendtype == SEND_TYPE_DIME) createDimeMessage().write(os); } catch (Exception e) { throw org.apache.axis.AxisFault.makeFault(e); } } /** * Gets the content type for the whole stream. * * @return the content type for the whole stream * * @throws org.apache.axis.AxisFault */ public String getContentType() throws org.apache.axis.AxisFault { mergeinAttachments(); int sendtype = this.sendtype == SEND_TYPE_NOTSET ? SEND_TYPE_DEFAULT : this.sendtype; if (sendtype == SEND_TYPE_MIME || sendtype == SEND_TYPE_MTOM) return org.apache.axis.attachments.MimeUtils.getContentType((multipart != null) ? multipart : (multipart = org.apache.axis.attachments.MimeUtils.createMP(soapPart.getAsString(), orderedAttachments, getSendType()))); else return org.apache.axis.Message.MIME_APPLICATION_DIME; } /** * This is the number of attachments. * * @return the number of attachments */ public int getAttachmentCount() { if (_askedForStreams) { throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream")); } try { mergeinAttachments(); // force a serialization of the message so that // any attachments will be added soapPart.saveChanges(); return orderedAttachments.size(); } catch (AxisFault e) { log.warn(Messages.getMessage("exception00"), e); } return 0; } /** * Determine if an object is to be treated as an attchment. * * @param value the value that is to be determined if * its an attachment. * * @return True if value should be treated as an attchment. */ public boolean isAttachment(Object value) { return AttachmentUtils.isAttachment(value); } /** * Removes all <CODE>AttachmentPart</CODE> objects that have * been added to this <CODE>SOAPMessage</CODE> object. * * <P>This method does not touch the SOAP part.</P> */ public void removeAllAttachments() { if (_askedForStreams) { throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream")); } try { multipart = null; dimemultipart = null; mergeinAttachments(); attachments.clear(); orderedAttachments.clear(); stackDataHandler.clear(); } catch (AxisFault af) { log.warn(Messages.getMessage("exception00"), af); } } /** * Retrieves all the <CODE>AttachmentPart</CODE> objects * that have header entries that match the specified headers. * Note that a returned attachment could have headers in * addition to those specified. * @param headers a <CODE>MimeHeaders</CODE> * object containing the MIME headers for which to * search * @return an iterator over all attachments that have a header * that matches one of the given headers */ public java.util.Iterator getAttachments(javax.xml.soap.MimeHeaders headers) { if (_askedForStreams) { throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream")); } java.util.Vector vecParts = new java.util.Vector(); java.util.Iterator iterator = GetAttachmentsIterator(); while (iterator.hasNext()) { Part part = (Part) iterator.next(); if (part instanceof AttachmentPart) { if (((AttachmentPart) part).matches(headers)) { vecParts.add(part); } } } return vecParts.iterator(); } /** * get an iterator over all attachments. This * @return iterator of Part Objects; some of which may be * AttachmentPart instances * @see org.apache.axis.Part * @see AttachmentPart */ private java.util.Iterator GetAttachmentsIterator() { java.util.Iterator iterator = attachments.values().iterator(); return iterator; } /** * Create a new attachment Part in this Message. * Will actually, and always, return an AttachmentPart. * * @return a new attachment Part * * @throws org.apache.axis.AxisFault */ public Part createAttachmentPart() throws org.apache.axis.AxisFault { return new AttachmentPart(); } public void setSendType(int sendtype) { if (sendtype < 1) throw new IllegalArgumentException(""); if (sendtype > SEND_TYPE_MAX) throw new IllegalArgumentException(""); this.sendtype = sendtype; } public int getSendType() { return sendtype; } /** * dispose of the attachments and their files; do not use the object * after making this call. */ public void dispose() { java.util.Iterator iterator = GetAttachmentsIterator(); while (iterator.hasNext()) { Part part = (Part) iterator.next(); if (part instanceof AttachmentPart) { AttachmentPart apart = (AttachmentPart) part; apart.dispose(); } } } // consider type-safe e-num here? /** * Determine how an object typically sent as attachments are to * be represented. Currently, MIME DIME and NONE are reccognised. * * @param value a String representing a sending type, treated in a * case-insensetive manner * @return an <code>int</code> send type code */ public static int getSendType(String value) { if (value.equalsIgnoreCase("MTOM")) return SEND_TYPE_MTOM; if (value.equalsIgnoreCase("MIME")) return SEND_TYPE_MIME; if (value.equalsIgnoreCase("DIME")) return SEND_TYPE_DIME; if (value.equalsIgnoreCase("NONE")) return SEND_TYPE_NONE; return SEND_TYPE_NOTSET; } /** * For a given sendType value, return a string representation. * * @param value a type code integer * @return a <code>String</code> representation of <code>value</code> */ public static String getSendTypeString(int value) { if (value == SEND_TYPE_MTOM) { return "MTOM"; } if (value == SEND_TYPE_MIME) { return "MIME"; } if (value == SEND_TYPE_DIME) { return "DIME"; } if (value == SEND_TYPE_NONE) { return "NONE"; } return null; } /** * Once this method is called, attachments can only be accessed via the InputStreams. * Any other access to the attachments collection (e.g. via getAttachments()) is * prohibited and will cause a IllegalStateException to be thrown. * * @return All of the attachment streams. */ public IncomingAttachmentStreams getIncomingAttachmentStreams() { if (_askedForAttachments) { throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream")); } _askedForStreams = true; mpartStream = null; // todo: comment return _streams; } }