9

I have been trying to serialize a MimeMessage instance, but as I read on web it is not possible. What I want to achieve with serializing a MimeMessage instance is that I want to hash that instance and send it along mail itself. What I coded so far is this:

MimeMessage message = new MimeMessage(session);
//...setting up content of MimeMessage
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("object.ser")));
oos.writeObject(message);
oos.close();

It compiles on GlassFish server, but I get a runtime error when I try to use service. It says:

exception

java.io.NotSerializableException: javax.mail.internet.MimeMessage

I tried it to do in this way; yet it didn't work, either:

Object obj = new Object();
obj = (Object)message;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("object.ser")));
oos.writeObject(obj);
oos.close();

Is there any way to achieve serializing a MimeMessage instance or to go around and hack it in some other way?

Ekrem Doğan
  • 674
  • 6
  • 13

5 Answers5

11

Actually, MimeMessage does not implement Serializable by design, you can extend MimeMessage to do so but you do not need to as MimeMessage has facilities using writeTo(OutputStream) to allow you to save the content as n RFC-822 mime message.

try (OutputStream str = Files.newOutputStream(Paths.get("message.eml"))) {
    msg.writeTo(str);
}

You can then read this message in for later processing using the MimeMessage(Session,InputStream) constructor with the session object.

Session session = Session.getInstance(props);
try (InputStream str = Files.newInputStream(Paths.get("message.eml"))) {
    MimeMessage msg = new MimeMessage(session, str);
    // Do something with the message, maybe send it.
    Transport.send(msg);
}

If you happen to be using spring's JavaMailSender then you can also construct new mime messages through the configured session by using createMimeMessage(InputStream) which uses the configured session.

Brett Ryan
  • 26,937
  • 30
  • 128
  • 163
  • I get a NPE for the recipients when trying to send the createMimeMessage(InputStream) created mail. Do you have to specify the recipients explicitly again after deserializing? – lilalinux Jul 26 '18 at 17:17
1

NotSerializableException is thrown when the Object being serialized does not implement java.io.Serializable interface. Since, javax.mail.internet.MimeMessage does not implement this interface it cannot be serialized.

public class MimeMessage extends Message implements MimePart

Consider serializing its content instead; by wrapping it up in a custom domain object (with the message text and recipients) that implements Serializable. De-serialize this domain object when required and then go on to construct a new MimeMessage from its contents.

Ravi K Thapliyal
  • 51,095
  • 9
  • 76
  • 89
  • Is there anyway making MimeMessage to implement `java.io.Serializable` interface, like overriding or something? Is it totally impossible to convert MimeMessage object into a ByteArray? (I am assuming serialization and converting into byte array are same things). – Ekrem Doğan Jul 12 '13 at 07:31
  • 2
    When you try to serialize an object in Java, the complete object graph from the root has to be serialized. The reason MimeMessage isn't Serializable is because it's a container for objects that aren't Serializable like Session which in turn would have members that aren't Serializable and so on. – Ravi K Thapliyal Jul 12 '13 at 07:40
  • And, the reason the byte array approach would fail is that upon de-serialization the mail session object received would be invalid (not alive). This same concept prevent resources like database connections or data streams from getting serialized. – Ravi K Thapliyal Jul 12 '13 at 07:42
  • Thanks for your answers, sir. I intended to hash byte array of mimemessage instance. I was going to send it over network for some reason. That's why I was trying to convert mimemessage instance into byte array. Now I learned that is impossible. – Ekrem Doğan Jul 12 '13 at 08:08
1

As stated in other answers: if you don't need the session information in the MimeMessage you can make use of the MimeMessage.writeTo method to store it in a serializable wrapper object. As a template see the following code (Beware, it's not Null-safe).

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;

import com.sun.mail.smtp.SMTPOutputStream;

public class SerializableMimeMessage implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 3763328805281033284L;

    private transient MimeMessage mimeMessage;

    public SerializableMimeMessage(MimeMessage mimeMessage) {
        this.mimeMessage = mimeMessage;
    }

    private void writeObject(ObjectOutputStream oos) throws IOException {
        // convert
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        SMTPOutputStream os = new SMTPOutputStream(baos);
        try {
            mimeMessage.writeTo(os);
        } catch (MessagingException e) {
            throw new IOException("MimeMessage could not be serialized.", e);
        }
        os.flush();
        byte[] serializedEmail = baos.toByteArray();

        // default serialization 
        oos.defaultWriteObject();

        // write the object
        oos.writeInt(serializedEmail.length);
        oos.write(serializedEmail);
    }

    private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
        // default deserialization
        ois.defaultReadObject();

        // read the object
        int len = ois.readInt();
        byte[] serializedEmail = new byte[len];
        ois.readFully(serializedEmail);

        // convert
        ByteArrayInputStream bais = new ByteArrayInputStream(serializedEmail);

        try {
            mimeMessage = new MimeMessage((Session) null, bais);
        } catch (MessagingException e) {
            throw new IOException("MimeMessage could not be deserialized.", e);
        }
    }

    public MimeMessage getMimeMessage() {
        return mimeMessage;
    }
Fabian Braun
  • 3,612
  • 1
  • 27
  • 44
0

This has worked for me

Serialisation

            var mailMessage = new MimeMessage();
            mailMessage.From.Add(new MailboxAddress("from", "email@example.com"));
            mailMessage.To.Add(new MailboxAddress("to", "email2@example.com"));
            mailMessage.ReplyTo.Add(new MailboxAddress("reply", "email3@example.com"));
            mailMessage.Subject = "Test subject";
            var bodyBuilder = new BodyBuilder();
            bodyBuilder.TextBody = "GenericEmail";
            bodyBuilder.HtmlBody = JsonConvert.SerializeObject(new Settings() { Exchange = "x" });
            mailMessage.Body = bodyBuilder.ToMessageBody();
            using var memoryStream = new MemoryStream();
            mailMessage.WriteTo(memoryStream);
            return memoryStream.ToArray();

Deserialisation

            using var stream = new MemoryStream(data);
            return MimeMessage.Load(stream);

Note that my use case is to send the email to AMQP queue, so that the email sender sends it when available or ready.. My AMQP receiver processes the email template so I send the template in textbody and variables in htmlbody

Scholtz
  • 2,878
  • 2
  • 23
  • 23
0

We can create MimeMessage and write it to a ByteArrayOutputStream as below:

JavaMailSenderImpl sender = new JavaMailSenderImpl();
MimeMessage message = sender.createMimeMessage();
MimeMailMessage mimeMailMessage = new MimeMailMessage(message); 
mimeMailMessage.setTo("To@address.com");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
mimeMailMessage.getMimeMessage().writeTo(bos);
byte[] byteArrayMessage = bos.toByteArray());
bos.close();
Nandan
  • 497
  • 2
  • 7
  • 18