Java tutorial
/* * Copyright 2015 Adaptris Ltd. * * 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 com.adaptris.util.text.mime; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Vector; import javax.activation.DataSource; import javax.mail.MessagingException; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMultipart; import org.apache.commons.io.output.ByteArrayOutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.adaptris.annotation.Removal; import com.adaptris.util.GuidGenerator; import com.adaptris.util.IdGenerator; import com.adaptris.util.stream.StreamUtil; /** * Reading a mime multipart input stream. * <p> * This offers, by default, a simplified model for processing all the body parts iteratively, however access to the underlying * MimeBodyPart is available depending on the constructor that is used. * </p> * <p> * A multi-part mime payload would look something similar to * * <pre> * {@code * Message-ID: db03b6ef-ffff-ffc0-019b-04e2b47a4d8e * Mime-Version: 1.0 * Content-Type: multipart/mixed; * boundary="----=_Part_1_33189144.1047351507632" * Content-Length: 383 * * ------=_Part_1_33189144.1047351507632 * Content-Id: AdaptrisMessage/payload * * This is the message 03/11/2003 01:57 PM * * ------=_Part_1_33189144.1047351507632 * Content-Id: AdaptrisMessage/metadata * * workflowId=loopback * previousGuid=db03b6ef-ffff-ffc0-019b-04e2b47a4d8e * emailmessageid=<200303110257.h2B2v9sC030299@localhost.localdomain> * * ------=_Part_1_33189144.1047351507632-- * } * </pre> * * <p> * The Content-Length header is ignored for the purposes of parsing the multi-part mime message, the multipart is considered * finished, when the final mime boundary occurs. If Content-Length needs to be taken into account then a specific DataSource should * be used as the parameter to the constructor. * </p> * * @deprecated since 3.7.2 use one of {@link MultipartIterator} concrete sub-classes instead. */ @Deprecated @Removal(version = "3.10.0") public class MultiPartInput implements Enumeration, Iterator { private List<PartHolder> bodyParts; private MimeMultipart multipart; private Iterator bodyPartIterator; private transient Logger logR = LoggerFactory.getLogger(this.getClass()); private DataSource dataSource; private boolean simpleIterator; private static IdGenerator idGenerator; static { idGenerator = new GuidGenerator(); } private MultiPartInput() { bodyParts = new Vector<PartHolder>(); simpleIterator = true; } /** * Constructor. * * @param in the Inputstream from which to parse the mime multi-part * @throws MessagingException if the bytes did not contain a valid * MimeMultiPart * @throws IOException if there was an IOException * @throws MessagingException if an underlying javax.mail exception occurred * @see MultiPartInput#MultiPartInput(InputStream, boolean) */ public MultiPartInput(InputStream in) throws IOException, MessagingException { this(in, true); } /** * Constructor. * * @param in the Inputstream from which to parse the mime multi-part * @param simplified whether the iterator / enumeration should simply return * the content body as a byte array rather than a MimeBodyPart * @throws MessagingException if the bytes did not contain a valid * MimeMultiPart * @throws IOException if there was an IOException * @throws MessagingException if the bytes did not contain a valid * MimeMultiPart * @see MultiPartInput#MultiPartInput(DataSource, boolean) */ public MultiPartInput(InputStream in, boolean simplified) throws IOException, MessagingException { this(new InputStreamDataSource(in), simplified); } /** * Constructor. * * @param bytes the byte array where the mime multi-part is. * @throws MessagingException if the bytes did not contain a valid * MimeMultiPart * @throws IOException if there was an IOException * @throws MessagingException if the bytes did not contain a valid * MimeMultiPart * @see MultiPartInput#MultiPartInput(byte[], boolean) */ public MultiPartInput(byte[] bytes) throws IOException, MessagingException { this(bytes, true); } /** * Constructor. * * @param bytes the bytes from which to parse the mime multipart. * @param simplified whether the iterator / enumeration should simply return * the content body as a byte array rather than a MimeBodyPart * @see InputStreamDataSource * @throws MessagingException if the bytes did not contain a valid * MimeMultiPart * @throws IOException if there was an IOException */ public MultiPartInput(byte[] bytes, boolean simplified) throws IOException, MessagingException { this(new ByteArrayInputStream(bytes), simplified); } /** * Constructor. * * @param ds the Datasource from which to parse the mime multipart. * @see InputStreamDataSource * @throws MessagingException if the bytes did not contain a valid * MimeMultiPart * @throws IOException if there was an IOException * @throws MessagingException if the bytes did not contain a valid * MimeMultiPart * @see MultiPartInput#MultiPartInput(DataSource, boolean) */ public MultiPartInput(DataSource ds) throws IOException, MessagingException { this(ds, true); } /** * Constructor. * * @param ds the Datasource from which to parse the mime multipart. * @param simplified whether the iterator / enumeration should simply return * the content body as a byte array rather than a MimeBodyPart * @see InputStreamDataSource * @throws MessagingException if the bytes did not contain a valid * MimeMultiPart * @throws IOException if there was an IOException */ public MultiPartInput(DataSource ds, boolean simplified) throws IOException, MessagingException { this(); dataSource = ds; simpleIterator = simplified; initialise(); } /** * Get a BodyPart based on the id. * <p> * Although it is unlikely that the Content-Id will re-occur across a mime * multi-part, this is possible, so use of this method may not return the * expected body part. * </p> * * @param id the defining content-id. * @return the underlying MimeBodyPart specified by the contentid or null if * the contentId is not present. */ public MimeBodyPart getBodyPart(String id) { MimeBodyPart result = null; PartHolder tmp = wrap(id); if (bodyParts.contains(tmp)) { result = ((PartHolder) bodyParts.get(bodyParts.indexOf(tmp))).getPart(); } return result; } /** * Get a BodyPart based on the it's position within the multipart. * * @param partNumber the part position (starts from 0). * @return the underlying MimeBodyPart specified by the partNumber. */ public MimeBodyPart getBodyPart(int partNumber) { if (bodyParts.size() < partNumber + 1) { return null; } return ((PartHolder) bodyParts.get(partNumber)).getPart(); } /** * Get a part by the contentId. * <p> * Although it is unlikely that the Content-Id will re-occur across a mime * multi-part, this is possible, so use of this method may not return the * expected body part. * </p> * * @param id the defining content-id. * @return the contents of the body part specified by the contentId or null if * the contentId is not present. */ public byte[] getPart(String id) { byte[] result = null; PartHolder tmp = wrap(id); if (bodyParts.contains(tmp)) { result = ((BytePartHolder) bodyParts.get(bodyParts.indexOf(tmp))).getBytes(); } return result; } /** * Get a BodyPart based on the it's position within the multipart. * * @param partNumber the part position (starts from 0). * @return the underlying bytes specified by the partNumber. */ public byte[] getPart(int partNumber) { if (bodyParts.size() < partNumber + 1) { return null; } return ((BytePartHolder) bodyParts.get(partNumber)).getBytes(); } /** @see Enumeration#hasMoreElements */ @Override public boolean hasMoreElements() { return hasNext(); } /** * @see Enumeration#nextElement * @see #next() */ @Override public Object nextElement() { return next(); } /** * @see Iterator#hasNext() */ @Override public boolean hasNext() { return bodyPartIterator.hasNext(); } /** * @see Iterator#next() * @return the next body part in the list, either a byte array or MimeBodyPart */ @Override public Object next() { Object result = null; if (simpleIterator) { result = ((BytePartHolder) bodyPartIterator.next()).getBytes(); } else { result = ((PartHolder) bodyPartIterator.next()).getPart(); } return result; } /** * @see Iterator#remove() */ @Override public void remove() { throw new UnsupportedOperationException("Remove is not supported"); } /** * Return the number of body parts in this mime multipart. * * @return the number of body parts */ public int size() { return bodyParts.size(); } /** * Return the underlying data source used to parse this mime multipart. * * @return the datasource * @see javax.activation.DataSource * @see InputStreamDataSource */ public DataSource getDataSource() { return dataSource; } /** * Convenience Method to get the content-Type * * @return the contenttype * @see javax.activation.DataSource#getContentType() */ public String getContentType() { return dataSource.getContentType(); } /** * Convenience Method to get the name from the underlying datasource. * * @return the name * @see javax.activation.DataSource#getName() */ public String getName() { return dataSource.getName(); } private void initialise() throws MessagingException, IOException { multipart = new MimeMultipart(dataSource); for (int i = 0; i < multipart.getCount(); i++) { MimeBodyPart part = (MimeBodyPart) multipart.getBodyPart(i); PartHolder ph = wrap(part); if (bodyParts.contains(ph)) { logR.warn(ph.contentId + " already exists as a part"); } bodyParts.add(ph); } bodyPartIterator = bodyParts.iterator(); } private PartHolder wrap(MimeBodyPart part) throws MessagingException, IOException { if (simpleIterator) { return new BytePartHolder(part); } return new MimePartHolder(part); } private PartHolder wrap(String id) { if (simpleIterator) { return new BytePartHolder(id); } return new MimePartHolder(id); } abstract class PartHolder { private MimeBodyPart bodyPart; private String contentId; PartHolder(String id) { contentId = id; bodyPart = null; } PartHolder(MimeBodyPart p) throws IOException, MessagingException { bodyPart = p; contentId = bodyPart.getContentID(); if (contentId == null) { logR.warn( "No Content Id Found as part of body part, assigning a unique id for referential integrity"); contentId = idGenerator.create(p); } } MimeBodyPart getPart() { return bodyPart; } @Override public boolean equals(Object o) { boolean rc = false; if (o instanceof PartHolder) { rc = contentId.equals(((PartHolder) o).contentId); } return rc; } @Override public int hashCode() { return contentId.hashCode(); } } private class BytePartHolder extends PartHolder { private byte[] bytes; BytePartHolder(String id) { super(id); } BytePartHolder(MimeBodyPart p) throws IOException, MessagingException { super(p); ByteArrayOutputStream out = new ByteArrayOutputStream(); StreamUtil.copyAndClose(p.getInputStream(), out); bytes = out.toByteArray(); } byte[] getBytes() { return bytes; } } private class MimePartHolder extends PartHolder { MimePartHolder(String id) { super(id); } MimePartHolder(MimeBodyPart p) throws IOException, MessagingException { super(p); } } }