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. */ 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.IOUtils; import org.apache.commons.logging.Log; import javax.activation.DataHandler; import javax.mail.internet.MimeUtility; import java.io.IOException; import java.io.BufferedInputStream; /** * This simulates the multipart stream. * * @author Rick Rineholt */ public class MultiPartRelatedInputStream extends MultiPartInputStream { /** Field log */ protected static Log log = LogFactory.getLog(MultiPartRelatedInputStream.class.getName()); /** Field MIME_MULTIPART_RELATED */ public static final String MIME_MULTIPART_RELATED = "multipart/related"; /** Field parts */ protected java.util.HashMap parts = new java.util.HashMap(); /** Field orderedParts */ protected java.util.LinkedList orderedParts = new java.util.LinkedList(); /** Field rootPartLength */ protected int rootPartLength = 0; /** Field closed */ protected boolean closed = false; // If true the stream has been closed. /** Field eos */ protected boolean eos = false; // This is set once the SOAP packet has reached the end of stream. // protected java.io.InputStream is = null; //The orginal multipart/related stream. // This stream controls and manages the boundary. /** Field boundaryDelimitedStream */ protected org.apache.axis.attachments.BoundaryDelimitedStream boundaryDelimitedStream = null; /** Field soapStream */ protected java.io.InputStream soapStream = null; // Set the soap stream once found. /** Field soapStreamBDS */ protected java.io.InputStream soapStreamBDS = null; // Set to the boundary delimited stream assoc. with soap stream once found. /** Field boundary */ protected byte[] boundary = null; /** Field cachedSOAPEnvelope */ protected java.io.ByteArrayInputStream cachedSOAPEnvelope = null; // Caches the soap stream if it is // Still open and a reference to read data in a later attachment occurs. /** Field contentLocation */ protected String contentLocation = null; /** Field contentId */ protected String contentId = null; /** Field MAX_CACHED */ private static final int MAX_CACHED = 16 * 1024; /** * Create a new Multipart stream. * @param contentType the string that holds the contentType * @param stream the true input stream from where the source * * @throws org.apache.axis.AxisFault if the stream could not be created */ public MultiPartRelatedInputStream(String contentType, java.io.InputStream stream) throws org.apache.axis.AxisFault { super(null); // don't cache this stream. if (!(stream instanceof BufferedInputStream)) { stream = new BufferedInputStream(stream); } try { // First find the start and boundary parameters. There are real weird rules regard what // can be in real headers what needs to be escaped etc let mail parse it. javax.mail.internet.ContentType ct = new javax.mail.internet.ContentType(contentType); String rootPartContentId = ct.getParameter("start"); // Get the root part content. if (rootPartContentId != null) { rootPartContentId = rootPartContentId.trim(); if (rootPartContentId.startsWith("<")) { rootPartContentId = rootPartContentId.substring(1); } if (rootPartContentId.endsWith(">")) { rootPartContentId = rootPartContentId.substring(0, rootPartContentId.length() - 1); } } if (ct.getParameter("boundary") != null) { String boundaryStr = "--" + ct.getParameter("boundary"); // The boundary with -- add as always the case. // if start is null then the first attachment is the rootpart // First read the start boundary -- this is done with brute force since the servlet may swallow the crlf between headers. // after this we use the more efficient boundarydelimeted stream. There should never be any data here anyway. byte[][] boundaryMarker = new byte[2][boundaryStr.length() + 2]; IOUtils.readFully(stream, boundaryMarker[0]); boundary = (boundaryStr + "\r\n").getBytes("US-ASCII"); int current = 0; // This just goes brute force one byte at a time to find the first boundary. // in most cases this just a crlf. for (boolean found = false; !found; ++current) { if (!(found = java.util.Arrays.equals(boundaryMarker[current & 0x1], boundary))) { System.arraycopy(boundaryMarker[current & 0x1], 1, boundaryMarker[(current + 1) & 0x1], 0, boundaryMarker[0].length - 1); if (stream.read(boundaryMarker[(current + 1) & 0x1], boundaryMarker[0].length - 1, 1) < 1) { throw new org.apache.axis.AxisFault( Messages.getMessage("mimeErrorNoBoundary", new String(boundary))); } } } // after the first boundary each boundary will have a cr lf at the beginning since after the data in any part there // is a cr lf added to put the boundary at the begining of a line. boundaryStr = "\r\n" + boundaryStr; boundary = boundaryStr.getBytes("US-ASCII"); } else { // Since boundary is not specified, we try to find one. for (boolean found = false; !found;) { boundary = readLine(stream); if (boundary == null) throw new org.apache.axis.AxisFault(Messages.getMessage("mimeErrorNoBoundary", "--")); found = boundary.length > 4 && boundary[2] == '-' && boundary[3] == '-'; } } // create the boundary delmited stream. boundaryDelimitedStream = new org.apache.axis.attachments.BoundaryDelimitedStream(stream, boundary, 1024); // Now read through all potential streams until we have found the root part. String contentTransferEncoding = null; do { contentId = null; contentLocation = null; contentTransferEncoding = null; // Read this attachments headers from the stream. javax.mail.internet.InternetHeaders headers = new javax.mail.internet.InternetHeaders( boundaryDelimitedStream); // Use java mail utility to read through the headers. contentId = headers.getHeader(HTTPConstants.HEADER_CONTENT_ID, null); // Clean up the headers and remove any < > if (contentId != null) { contentId = contentId.trim(); if (contentId.startsWith("<")) { contentId = contentId.substring(1); } if (contentId.endsWith(">")) { contentId = contentId.substring(0, contentId.length() - 1); } contentId = contentId.trim(); // if (!contentId.startsWith("cid:")) { // contentId = // "cid:" // + contentId; // make sure its identified as cid // } } contentLocation = headers.getHeader(HTTPConstants.HEADER_CONTENT_LOCATION, null); if (contentLocation != null) { contentLocation = contentLocation.trim(); if (contentLocation.startsWith("<")) { contentLocation = contentLocation.substring(1); } if (contentLocation.endsWith(">")) { contentLocation = contentLocation.substring(0, contentLocation.length() - 1); } contentLocation = contentLocation.trim(); } contentType = headers.getHeader(HTTPConstants.HEADER_CONTENT_TYPE, null); if (contentType != null) { contentType = contentType.trim(); } contentTransferEncoding = headers.getHeader(HTTPConstants.HEADER_CONTENT_TRANSFER_ENCODING, null); if (contentTransferEncoding != null) { contentTransferEncoding = contentTransferEncoding.trim(); } java.io.InputStream decodedStream = boundaryDelimitedStream; if ((contentTransferEncoding != null) && (0 != contentTransferEncoding.length())) { decodedStream = MimeUtility.decode(decodedStream, contentTransferEncoding); } if ((rootPartContentId != null) && !rootPartContentId.equals(contentId)) { // This is a part that has come in prior to the root part. Need to buffer it up. javax.activation.DataHandler dh = new javax.activation.DataHandler( new org.apache.axis.attachments.ManagedMemoryDataSource(decodedStream, MAX_CACHED, contentType, true)); AttachmentPart ap = new AttachmentPart(dh); if (contentId != null) { ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_ID, contentId); } if (contentLocation != null) { ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_LOCATION, contentLocation); } for (java.util.Enumeration en = headers.getNonMatchingHeaders( new String[] { HTTPConstants.HEADER_CONTENT_ID, HTTPConstants.HEADER_CONTENT_LOCATION, HTTPConstants.HEADER_CONTENT_TYPE }); en.hasMoreElements();) { javax.mail.Header header = (javax.mail.Header) en.nextElement(); String name = header.getName(); String value = header.getValue(); if ((name != null) && (value != null)) { name = name.trim(); if (name.length() != 0) { ap.addMimeHeader(name, value); } } } addPart(contentId, contentLocation, ap); boundaryDelimitedStream = boundaryDelimitedStream.getNextStream(); // Gets the next stream. } } while ((null != boundaryDelimitedStream) && (rootPartContentId != null) && !rootPartContentId.equals(contentId)); if (boundaryDelimitedStream == null) { throw new org.apache.axis.AxisFault(Messages.getMessage("noRoot", rootPartContentId)); } soapStreamBDS = boundaryDelimitedStream; if ((contentTransferEncoding != null) && (0 != contentTransferEncoding.length())) { soapStream = MimeUtility.decode(boundaryDelimitedStream, contentTransferEncoding); } else { soapStream = boundaryDelimitedStream; // This should be the SOAP part } // Read from the input stream all attachments prior to the root part. } catch (javax.mail.internet.ParseException e) { throw new org.apache.axis.AxisFault(Messages.getMessage("mimeErrorParsing", e.getMessage())); } catch (java.io.IOException e) { throw new org.apache.axis.AxisFault(Messages.getMessage("readError", e.getMessage())); } catch (javax.mail.MessagingException e) { throw new org.apache.axis.AxisFault(Messages.getMessage("readError", e.getMessage())); } } //when searching for a MIME boundary it MUST be terminated with CR LF. LF alone is NOT sufficient. private final byte[] readLine(java.io.InputStream is) throws IOException { java.io.ByteArrayOutputStream input = new java.io.ByteArrayOutputStream(1024); int c = 0; input.write('\r'); input.write('\n'); int next = -1; for (; c != -1;) { c = -1 != next ? next : is.read(); next = -1; switch (c) { case -1: break; case '\r': next = is.read(); if (next == '\n') //found a line. return input.toByteArray(); if (next == -1) return null; //fall through default: input.write((byte) c); break; } } //even if there is stuff in buffer if EOF then this can't be a boundary. return null; } public Part getAttachmentByReference(final String[] id) throws org.apache.axis.AxisFault { // First see if we have read it in yet. Part ret = null; for (int i = id.length - 1; (ret == null) && (i > -1); --i) { ret = (AttachmentPart) parts.get(id[i]); } if (null == ret) { ret = readTillFound(id); } log.debug(Messages.getMessage("return02", "getAttachmentByReference(\"" + id + "\"", ((ret == null) ? "null" : ret.toString()))); return ret; } /** * Add an <code>AttachmentPart</code> together with its content and location * IDs. * * @param contentId the content ID * @param locationId the location ID * @param ap the <code>AttachmentPart</code> */ protected void addPart(String contentId, String locationId, AttachmentPart ap) { if ((contentId != null) && (contentId.trim().length() != 0)) { parts.put(contentId, ap); } if ((locationId != null) && (locationId.trim().length() != 0)) { parts.put(locationId, ap); } orderedParts.add(ap); } /** Field READ_ALL */ protected static final String[] READ_ALL = { " * \0 ".intern() }; // Shouldn't never match /** * Read all data. * * @throws org.apache.axis.AxisFault if there was a problem reading all the * data */ protected void readAll() throws org.apache.axis.AxisFault { readTillFound(READ_ALL); } public java.util.Collection getAttachments() throws org.apache.axis.AxisFault { readAll(); return orderedParts; } /** * This will read streams in till the one that is needed is found. * * @param id id is the stream being sought. * * @return the part for the id * * @throws org.apache.axis.AxisFault */ protected Part readTillFound(final String[] id) throws org.apache.axis.AxisFault { if (boundaryDelimitedStream == null) { return null; // The whole stream has been consumed already } Part ret = null; try { if (soapStreamBDS == boundaryDelimitedStream) { // Still on the SOAP stream. if (!eos) { // The SOAP packet has not been fully read yet. Need to store it away. java.io.ByteArrayOutputStream soapdata = new java.io.ByteArrayOutputStream(1024 * 8); byte[] buf = new byte[1024 * 16]; int byteread = 0; do { byteread = soapStream.read(buf); if (byteread > 0) { soapdata.write(buf, 0, byteread); } } while (byteread > -1); soapdata.close(); soapStream = new java.io.ByteArrayInputStream(soapdata.toByteArray()); } boundaryDelimitedStream = boundaryDelimitedStream.getNextStream(); } // Now start searching for the data. if (null != boundaryDelimitedStream) { do { String contentType = null; String contentId = null; String contentTransferEncoding = null; String contentLocation = null; // Read this attachments headers from the stream. javax.mail.internet.InternetHeaders headers = new javax.mail.internet.InternetHeaders( boundaryDelimitedStream); contentId = headers.getHeader("Content-Id", null); if (contentId != null) { contentId = contentId.trim(); if (contentId.startsWith("<")) { contentId = contentId.substring(1); } if (contentId.endsWith(">")) { contentId = contentId.substring(0, contentId.length() - 1); } // if (!contentId.startsWith("cid:")) { // contentId = "cid:" + contentId; // } contentId = contentId.trim(); } contentType = headers.getHeader(HTTPConstants.HEADER_CONTENT_TYPE, null); if (contentType != null) { contentType = contentType.trim(); } contentLocation = headers.getHeader(HTTPConstants.HEADER_CONTENT_LOCATION, null); if (contentLocation != null) { contentLocation = contentLocation.trim(); } contentTransferEncoding = headers.getHeader(HTTPConstants.HEADER_CONTENT_TRANSFER_ENCODING, null); if (contentTransferEncoding != null) { contentTransferEncoding = contentTransferEncoding.trim(); } java.io.InputStream decodedStream = boundaryDelimitedStream; if ((contentTransferEncoding != null) && (0 != contentTransferEncoding.length())) { decodedStream = MimeUtility.decode(decodedStream, contentTransferEncoding); } ManagedMemoryDataSource source = new ManagedMemoryDataSource(decodedStream, ManagedMemoryDataSource.MAX_MEMORY_DISK_CACHED, contentType, true); DataHandler dh = new DataHandler(source); AttachmentPart ap = new AttachmentPart(dh); if (contentId != null) { ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_ID, contentId); } if (contentLocation != null) { ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_LOCATION, contentLocation); } for (java.util.Enumeration en = headers.getNonMatchingHeaders( new String[] { HTTPConstants.HEADER_CONTENT_ID, HTTPConstants.HEADER_CONTENT_LOCATION, HTTPConstants.HEADER_CONTENT_TYPE }); en.hasMoreElements();) { javax.mail.Header header = (javax.mail.Header) en.nextElement(); String name = header.getName(); String value = header.getValue(); if ((name != null) && (value != null)) { name = name.trim(); if (name.length() != 0) { ap.addMimeHeader(name, value); } } } addPart(contentId, contentLocation, ap); for (int i = id.length - 1; (ret == null) && (i > -1); --i) { if ((contentId != null) && id[i].equals(contentId)) { // This is the part being sought ret = ap; } else if ((contentLocation != null) && id[i].equals(contentLocation)) { ret = ap; } } boundaryDelimitedStream = boundaryDelimitedStream.getNextStream(); } while ((null == ret) && (null != boundaryDelimitedStream)); } } catch (Exception e) { throw org.apache.axis.AxisFault.makeFault(e); } return ret; } public String getContentLocation() { return contentLocation; } public String getContentId() { return contentId; } public int read(byte[] b, int off, int len) throws java.io.IOException { if (closed) { throw new java.io.IOException(Messages.getMessage("streamClosed")); } if (eos) { return -1; } int read = soapStream.read(b, off, len); if (read < 0) { eos = true; } return read; } public int read(byte[] b) throws java.io.IOException { return read(b, 0, b.length); } public int read() throws java.io.IOException { if (closed) { throw new java.io.IOException(Messages.getMessage("streamClosed")); } if (eos) { return -1; } int ret = soapStream.read(); if (ret < 0) { eos = true; } return ret; } public void close() throws java.io.IOException { closed = true; soapStream.close(); } public int available() throws java.io.IOException { return (closed || eos) ? 0 : soapStream.available(); } }