Asghabard
Asghabard

Reputation: 301

Sending mail with attachment with JavaMail - Exception while writing Multipart

I want to send mails with attachments using JavaMail, but I am encountering an Exception difficult to understand.

My code is divided in two parts. The first is the class EmailSender, deputied to manage the mail account used to send mail and the system configurations. The second is the class Mail, that manages the single email. (Code at the end of the post)

When creating an EmailSender, the constructor automatically searches the SMTP settings from a list of known settings with the method setServerHost().

When the EmailSender is asked to send a Email, the EmailSender transforms the information contained in the fields of the Email into a MimeMessage by calling the build() method of the Email; this is then sent using the Transport class of JavaMail.

The testing method I use is rather simple:

public static void sendMail(String subject, String body, String to, String urlAttachment) {
        System.out.printf("Username:\t");
        String username = readString();
        System.out.printf("Password:\t");
        String password = readString();

        EmailSender account = new EmailSender(username, password);
        Email mail = new Email(username, to, subject, body);
        mail.addAttachment(urlAttachment);
        account.sendMail(to, subject, body);
    }

The error I get in turn is less so.

javax.mail.MessagingException: IOException while sending message;
  nested exception is:
    java.io.IOException: Exception writing Multipart
    at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1365)
    at mail.EmailSender.sendMail(EmailSender.java:104)
    at mail.EmailSender.sendMail(EmailSender.java:122)
    at Test.TestLibraries.sendMail(TestLibraries.java:134)
    at Test.TestLibraries.main(TestLibraries.java:51)
Caused by: java.io.IOException: Exception writing Multipart
    at com.sun.mail.handlers.multipart_mixed.writeTo(multipart_mixed.java:86)
    at javax.activation.ObjectDataContentHandler.writeTo(Unknown Source)
    at javax.activation.DataHandler.writeTo(Unknown Source)
    at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:1694)
    at javax.mail.internet.MimeMessage.writeTo(MimeMessage.java:1913)
    at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1315)
    ... 4 more
Caused by: javax.mail.MessagingException: Empty multipart: multipart/mixed; 
    boundary="----=_Part_0_2129789493.1581503162040"
    at javax.mail.internet.MimeMultipart.writeTo(MimeMultipart.java:556)
    at com.sun.mail.handlers.multipart_mixed.writeTo(multipart_mixed.java:84)
    ... 9 more

What is the problem (and how can I solve it)?


Here is the code:

public class EmailSender {
    private String from;
    private String password;


    private String emailHost;
    private Properties properties = System.getProperties();
    private Session session;


    public static final int serverName = 0;
    public static final int serverPort = 1;
    public static final int serverAutentication = 2;
    public static final String[][] knownServerHostData = new String[][]
            {
                {   "smtp.mail.yahoo.com",  "587",  "SLL"       }           ,
                {   "smtp.mail.com",        "587",  "StartTLS"  }           ,
                {   "smtp.gmail.com",       "587",  ""          }           ,
                {   "out.virgilio.it",      "587",  ""          }
    };

    public EmailSender(String username, String password) {
        this.from = username;
        this.password = password;

        this.session = Session.getDefaultInstance(properties);

        this.setServerHost(password);
    }

    public boolean sendMail(String to, String subject, String body) {
        return sendMail(new Email(from, to, subject, body));
    }
    public boolean sendMail(Email email) {
        MimeMessage message = email.build(session);

        Transport transport = null;
        try {
            transport = session.getTransport("smtp");
            } catch (NoSuchProviderException e) {       e.printStackTrace();        closeTransport(transport);      }
        try {   
            transport.connect(emailHost, from, password);
            } catch (MessagingException e) {            e.printStackTrace();        closeTransport(transport);      }
        try {
            transport.sendMessage(message, message.getAllRecipients()); // <== THIS LINE RETURN EXCEPTION
            } catch (MessagingException e) {            e.printStackTrace();        closeTransport(transport);      }
        closeTransport(transport);

        return true;
    }

    private void closeTransport(Transport transport) {
        try {   transport.close();
            } catch (MessagingException e) {            e.printStackTrace();        }
    }

}

public class Email {

    private String sender;
    private Vector<String> recipients = new Vector<String>();
    private Vector<String> cc = new Vector<String>();
    private Vector<String> bcc = new Vector<String>();
    private String subject;
    private String body;
    private Vector<String> attachments = new Vector<String>();

    public Email(String from, String to, String subject, String body) {
        this.sender = from;
        this.recipients.add(to);
        this.subject = subject;
        this.body = body;
    }

    /** Returns a {@link MimeMessage} ready to be sent by an {@link EmailSender} with all the fields of {@code this} {@link Email}.
     * 
     * @return
     */
    public MimeMessage build(Session session) {
        MimeMessage message = new MimeMessage(session);

        // STEP 1 - Header

        // Sets the sender
        try {           message.setFrom(new InternetAddress(sender));
            } catch (AddressException e) {              e.printStackTrace();        }
              catch (MessagingException e) {            e.printStackTrace();        }

        // Sets the subject
        try {           message.setSubject(subject);
            } catch (MessagingException e) {            e.printStackTrace();        }

        // Adds the recipients one by one
        int i = 0;
        try {           
            for(i=0 ; i<recipients.size() ; i++)
                message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipients.get(i)));
            } catch (MessagingException e) {            e.printStackTrace();    System.err.println("The " + i + "-th recipient gave error.");       }
        try {           
            for(i=0 ; i<cc.size() ; i++)
                message.addRecipient(Message.RecipientType.CC, new InternetAddress(cc.get(i)));
            } catch (MessagingException e) {            e.printStackTrace();    System.err.println("The " + i + "-th cc gave error.");      }
        try {
            for(i=0 ; i<bcc.size() ; i++)
                message.addRecipient(Message.RecipientType.BCC, new InternetAddress(bcc.get(i)));
            } catch (MessagingException e) {            e.printStackTrace();    System.err.println("The " + i + "-th bcc gave error.");     }



        // STEP 2 - Body

        // Adds the body
        MimeBodyPart messageBodyPart = new MimeBodyPart();
        Multipart multipart = new MimeMultipart();
        try {
            messageBodyPart.setContent(message, "text/plain; charset=" +
                    MimeUtility.quote("us-ascii", HeaderTokenizer.MIME));
            } catch (MessagingException e) {            e.printStackTrace();        }
        try {           message.setText(body);
            } catch (MessagingException e) {            e.printStackTrace();        }

        // Adds the attachments
        try {   
            for(i=0 ; i<attachments.size() ; i++)           // Preps the attachments
                attachFileToMessageMultipart(multipart, attachments.get(i));
            } catch (MessagingException e) {            e.printStackTrace();        System.err.println("The " + i + "-th attachment gave error.");      }
              catch (IOException e) {                   e.printStackTrace();        System.err.println("The " + i + "-th attachment gave error.");      }



        // STEP 3 - Appends the MimeMessage's body
        try {
            message.setContent(multipart);
            } catch (MessagingException e1) {           e1.printStackTrace();       }
        return message;
    }

    /** This method avoids compatibility problems between JavaMail 1.3 and JavaMail 1.4.
     * @throws MessagingException 
     * @throws IOException 
     * 
     */
    private static void attachFileToMessageMultipart(Multipart multipart, String fileUrl) throws MessagingException, IOException {
        File file = new File(fileUrl);
        if( ! file.isFile() )       
            throw new IOException("The specified url does not identify a file.");

        // JavaMail 1.3

        MimeBodyPart attachPart = new MimeBodyPart();

        DataSource source = new FileDataSource(fileUrl);
        attachPart.setDataHandler(new DataHandler(source));
        attachPart.setFileName(file.getName());

        multipart.addBodyPart(attachPart);
    }



Edit: After reading the answer of Bill Shannon, I edited my method build(). The current version is:

        MimeMessage message = new MimeMessage(session);

        System.out.println("\t Building mail.");


        // STEP 1 - Header

        // Sets the sender
        try {           message.setFrom(new InternetAddress(sender));
            } catch (AddressException e) {              e.printStackTrace();        }
              catch (MessagingException e) {            e.printStackTrace();        }

        // Sets the subject
        try {           message.setSubject(subject);
            } catch (MessagingException e) {            e.printStackTrace();        }

        System.out.println("\t\t Sender and subject set mail.");

        // Adds the recipients one by one
        int i = 0;
        try {           
            for(i=0 ; i<recipients.size() ; i++)
                message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipients.get(i)));
            } catch (MessagingException e) {            e.printStackTrace();    System.err.println("The " + i + "-th recipient gave error.");       }
        try {           
            for(i=0 ; i<cc.size() ; i++)
                message.addRecipient(Message.RecipientType.CC, new InternetAddress(cc.get(i)));
            } catch (MessagingException e) {            e.printStackTrace();    System.err.println("The " + i + "-th cc gave error.");      }
        try {
            for(i=0 ; i<bcc.size() ; i++)
                message.addRecipient(Message.RecipientType.BCC, new InternetAddress(bcc.get(i)));
            } catch (MessagingException e) {            e.printStackTrace();    System.err.println("The " + i + "-th bcc gave error.");     }

        System.out.println("\t\t TO, CC, BCC fields setted.");


        // STEP 2 - Body

        // Adds the body
        MimeBodyPart messageBodyPart = new MimeBodyPart();
        Multipart multipart = new MimeMultipart();
        try {           
            messageBodyPart.setText(body);
            } catch (MessagingException e) {            e.printStackTrace();        }
        try {
            multipart.addBodyPart(messageBodyPart);
            } catch (IllegalWriteException e) {         e.printStackTrace();        }
              catch (MessagingException e) {            e.printStackTrace();        }

        //  try {
        //      messageBodyPart.setContent(message, "text/plain; charset=" +
        //              MimeUtility.quote("us-ascii", HeaderTokenizer.MIME));
        //      } catch (MessagingException e) {            e.printStackTrace();        }

        System.out.println("\t\t Body attached.");

        // Adds the attachments
        for(i=0 ; i<attachments.size() ; i++)           
            {
            // Creates a BodyPart representing the attachment
            try {   
                messageBodyPart.attachFile(attachments.get(i));
                } catch (MessagingException e) {            e.printStackTrace();        System.err.println("The " + i + "-th attachment gave error.");      }
                  catch (IOException e) {                   e.printStackTrace();        System.err.println("The " + i + "-th attachment gave error.");      }

            // Appends the BodyPart to the MultiPart
            try {
                multipart.addBodyPart(messageBodyPart);
                } catch (IllegalWriteException e) {         e.printStackTrace();        }
                  catch (MessagingException e) {            e.printStackTrace();        }
            }
        System.out.println("\t\t Files attached.");


        // STEP 3 - Appends the MimeMessage's body
        try {
            message.setContent(multipart);
            } catch (MessagingException e1) {           e1.printStackTrace();       }
        System.out.println("\t\t MimeMessage created.");
        return message;
    }

This version does not give Exceptions and the mail is sent... but without the attachment.

Upvotes: 0

Views: 2268

Answers (1)

Bill Shannon
Bill Shannon

Reputation: 29961

I don't understand what you're trying to do here:

messageBodyPart.setContent(message, "text/plain; charset=" +
                MimeUtility.quote("us-ascii", HeaderTokenizer.MIME));

I'm sure you don't mean to add the MimeMessage object itself as the content of a message body part. And you should never need to use the MimeUtility.quote method in this way.

This statement:

message.setText(body);

sets the entire content of the MimeMessage object to be a plain text message with the body string as its content. I don't think that's what you want.

What you want is to use the setText method to set the content of messageBodyPart object to the string body. Then add the messageBodyPart object to the multipart object.

After that, you can add all the attachments to the multipart object. Note that you might want to use the MimeBodyPart.atttachFile method to simplify your code.

Upvotes: 2

Related Questions