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
020package org.apache.james.mime4j.samples.transform;
021
022import java.io.ByteArrayInputStream;
023import java.io.IOException;
024import java.util.Date;
025import java.util.Random;
026
027import org.apache.james.mime4j.dom.Body;
028import org.apache.james.mime4j.dom.Entity;
029import org.apache.james.mime4j.dom.Message;
030import org.apache.james.mime4j.dom.MessageBuilder;
031import org.apache.james.mime4j.dom.MessageWriter;
032import org.apache.james.mime4j.dom.Multipart;
033import org.apache.james.mime4j.dom.TextBody;
034import org.apache.james.mime4j.dom.field.ParseException;
035import org.apache.james.mime4j.field.address.AddressBuilder;
036import org.apache.james.mime4j.message.BodyPart;
037import org.apache.james.mime4j.message.MessageImpl;
038import org.apache.james.mime4j.message.DefaultMessageBuilder;
039import org.apache.james.mime4j.message.DefaultMessageWriter;
040import org.apache.james.mime4j.message.MultipartImpl;
041import org.apache.james.mime4j.storage.DefaultStorageProvider;
042import org.apache.james.mime4j.storage.StorageBodyFactory;
043import org.apache.james.mime4j.storage.StorageProvider;
044import org.apache.james.mime4j.storage.TempFileStorageProvider;
045
046/**
047 * This code should illustrate how to transform a message into another message
048 * without modifying the original.
049 */
050public class TransformMessage {
051
052    // Host name used in message identifiers.
053    private static final String HOSTNAME = "localhost";
054
055    public static void main(String[] args) throws Exception {
056        // Explicitly set a strategy for storing body parts. Usually not
057        // necessary; for most applications the default setting is appropriate.
058        StorageProvider storageProvider = new TempFileStorageProvider();
059        DefaultStorageProvider.setInstance(storageProvider);
060
061        // Create a template message. It would be possible to load a message
062        // from an input stream but for this example a message object is created
063        // from scratch for demonstration purposes.
064        Message template = createTemplate();
065
066        // Create a new message by transforming the template.
067        Message transformed = transform(template);
068
069        MessageWriter writer = new DefaultMessageWriter();
070
071        // Print transformed message.
072        System.out.println("\n\nTransformed message:\n--------------------\n");
073        writer.writeMessage(transformed, System.out);
074
075        // Messages should be disposed of when they are no longer needed.
076        // Disposing of a message also disposes of all child elements (e.g. body
077        // parts) of the message.
078        transformed.dispose();
079
080        // Print original message to illustrate that it was not affected by the
081        // transformation.
082        System.out.println("\n\nOriginal template:\n------------------\n");
083        writer.writeMessage(template, System.out);
084
085        // Original message is no longer needed.
086        template.dispose();
087
088        // At this point all temporary files have been deleted because all
089        // messages and body parts have been disposed of properly.
090    }
091
092    /**
093     * Copies the given message and makes some arbitrary changes to the copy.
094     * @throws ParseException on bad arguments
095     */
096    private static Message transform(Message original) throws IOException, ParseException {
097        // Create a copy of the template. The copy can be modified without
098        // affecting the original.
099        MessageBuilder builder = new DefaultMessageBuilder();
100        Message message = builder.newMessage(original);
101
102        // In this example we know we have a multipart message. Use
103        // Message#isMultipart() if uncertain.
104        Multipart multipart = (Multipart) message.getBody();
105
106        // Insert a new text/plain body part after every body part of the
107        // template.
108        final int count = multipart.getCount();
109        for (int i = 0; i < count; i++) {
110            String text = "Text inserted after part " + (i + 1);
111            BodyPart bodyPart = createTextPart(text);
112            multipart.addBodyPart(bodyPart, 2 * i + 1);
113        }
114
115        // For no particular reason remove the second binary body part (now
116        // at index four).
117        Entity removed = multipart.removeBodyPart(4);
118
119        // The removed body part no longer has a parent entity it belongs to so
120        // it should be disposed of.
121        removed.dispose();
122
123        // Set some headers on the transformed message
124        message.createMessageId(HOSTNAME);
125        message.setSubject("Transformed message");
126        message.setDate(new Date());
127        message.setFrom(AddressBuilder.DEFAULT.parseMailbox("John Doe <jdoe@machine.example>"));
128
129        return message;
130    }
131
132    /**
133     * Creates a multipart/mixed message that consists of three parts (one text,
134     * two binary).
135     */
136    private static Message createTemplate() throws IOException {
137        Multipart multipart = new MultipartImpl("mixed");
138
139        BodyPart part1 = createTextPart("This is the first part of the template..");
140        multipart.addBodyPart(part1);
141
142        BodyPart part2 = createRandomBinaryPart(200);
143        multipart.addBodyPart(part2);
144
145        BodyPart part3 = createRandomBinaryPart(300);
146        multipart.addBodyPart(part3);
147
148        MessageImpl message = new MessageImpl();
149        message.setMultipart(multipart);
150
151        message.setSubject("Template message");
152
153        return message;
154    }
155
156    /**
157     * Creates a text part from the specified string.
158     */
159    private static BodyPart createTextPart(String text) {
160        TextBody body = new StorageBodyFactory().textBody(text, "UTF-8");
161
162        BodyPart bodyPart = new BodyPart();
163        bodyPart.setText(body);
164        bodyPart.setContentTransferEncoding("quoted-printable");
165
166        return bodyPart;
167    }
168
169    /**
170     * Creates a binary part with random content.
171     */
172    private static BodyPart createRandomBinaryPart(int numberOfBytes)
173            throws IOException {
174        byte[] data = new byte[numberOfBytes];
175        new Random().nextBytes(data);
176
177        Body body = new StorageBodyFactory()
178                .binaryBody(new ByteArrayInputStream(data));
179
180        BodyPart bodyPart = new BodyPart();
181        bodyPart.setBody(body, "application/octet-stream");
182        bodyPart.setContentTransferEncoding("base64");
183
184        return bodyPart;
185    }
186
187}