JavaMail Вопрос или ошибка?

У меня есть вопрос о Java Mail и о том, как он работает с потоками. В Java Mail 1.4.1 есть конструктор MimeMessage, принимающий поток. Насколько я понимаю, я мог бы передать поток этому конструктору, и он разобрал бы его для меня в MimeMessage. Я написал 2 теста, чтобы доказать это. Первый тест отправляет поток, который содержит только часть сообщения MIME, состоящего из нескольких частей. Второй тест отправляет поток, содержащий 2 полных сообщения MIME, состоящих из нескольких частей. Ни одна из них не работает так, как я ожидал. Первый не генерирует исключение, а второй каким-то образом считывает весь поток в одно сообщение. Это ошибка в Java Mail или я использую неправильные потоки? Или я упускаю что-то большее?

Это немного долго, но вот тестовый код:

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;

import junit.framework.TestCase;


public class mimeTest extends TestCase {

    public void testPartialMulitpartMessage() throws MessagingException, IOException 
    {
        Properties props = new Properties();
        Session session = Session.getInstance(props, null);
        String testMsg1 = "test";
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        // Step 1 - Create first MIME message
        MimeMessage mesg = new MimeMessage(session);
        Multipart mp = new MimeMultipart("mixed");
        //create a child part
        BodyPart bodyPart = new MimeBodyPart();
        bodyPart.setContent(testMsg1, "application/x-special");
        bodyPart.setHeader("Content-Length", String.valueOf(testMsg1.length()));
        DataSource ds = new ByteArrayDataSource(testMsg1, "application/x-special");
        bodyPart.setDataHandler(new DataHandler(ds));
        bodyPart.setHeader("Content-Transfer-Encoding", "8bit");
        // Add the child part to the multipart
        mp.addBodyPart(bodyPart);
        // Put the MultiPart into the Message
        mesg.setContent(mp);

        // Step 2 - write to a stream
        mesg.writeTo(byteArrayOutputStream);

        byte bytes[] = byteArrayOutputStream.toByteArray();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes, 0, 10);
        BufferedInputStream bufferedInputStream = new BufferedInputStream(byteArrayInputStream);
        boolean thrown = false;
        try
        {
            //Why does this not throw a messageexception.
            MimeMessage mesg2 = new MimeMessage(session, bufferedInputStream);
        }
        catch(MessagingException me){
            thrown = true;
        }

        if(!thrown) {
            assertTrue("Expected exception not thrown.", false);
        }
    }

    public void testMulitpleMulitpartMessages() throws MessagingException, IOException {
        Properties props = new Properties();
        Session session = Session.getInstance(props, null);
        String testMsg1 = "test";
        String testMsg2 = "test1";
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        // Step 1 - Create first MIME message
        MimeMessage mesg = new MimeMessage(session);
        Multipart mp = new MimeMultipart("mixed");
        //create a child part
        BodyPart bodyPart = new MimeBodyPart();
        bodyPart.setContent(testMsg1, "application/x-special");
        bodyPart.setHeader("Content-Length", String.valueOf(testMsg1.length()));
        DataSource ds = new ByteArrayDataSource(testMsg1, "application/x-special");
        bodyPart.setDataHandler(new DataHandler(ds));
        bodyPart.setHeader("Content-Transfer-Encoding", "8bit");
        // Add the child part to the multipart
        mp.addBodyPart(bodyPart);
        // Put the MultiPart into the Message
        mesg.setContent(mp);

        // Step 2 - write to a stream
        mesg.writeTo(byteArrayOutputStream);

        // Step 3 - Create second MIME message
        MimeMessage mesg2 = new MimeMessage(session);
        mp = new MimeMultipart("mixed");
        //create a child part
        bodyPart = new MimeBodyPart();
        bodyPart.setContent(testMsg2, "application/x-special");
        bodyPart.setHeader("Content-Length", String.valueOf(testMsg2.length()));
        ds = new ByteArrayDataSource(testMsg2, "application/x-special");
        bodyPart.setDataHandler(new DataHandler(ds));
        bodyPart.setHeader("Content-Transfer-Encoding", "8bit");
        // Add the child part to the multipart 
        mp.addBodyPart(bodyPart);
        // Put the MultiPart into the Message
        mesg2.setContent(mp);

        // Step 4 - write to the same stream
        mesg2.writeTo(byteArrayOutputStream);

        // Step 6 - read the two messages back
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        BufferedInputStream bufferedInputStream = new BufferedInputStream(byteArrayInputStream);
        List<MimeMessage> listMessages = new ArrayList<MimeMessage>();
        while (bufferedInputStream.available() > 0) {
            //http://java.sun.com/products/javamail/javadocs/javax/mail/internet/MimeMessage.html#MimeMessage(javax.mail.Session,%20java.io.InputStream)
            //The InputStream will be left positioned at the end of the data for the message.
            //WHY does this not work?  It reads the whole stream.
            mesg = new MimeMessage(session, bufferedInputStream);
            //output the message
            listMessages.add(mesg);
        }

        assertEquals(2, listMessages.size());

        assertTrue(listMessages.get(0).equals(mesg));
        assertTrue(listMessages.get(1).equals(mesg2));
    }
}

person bnantz    schedule 01.03.2009    source источник


Ответы (2)


API JavaMail — это свинья, и использовать его в сыром виде — неблагодарная задача, в основном потому, что он ведет себя не так, как вы ожидаете.

Я бы порекомендовал использовать API-слой Spring вокруг него, это гораздо менее напряжно. Он не скрывает JavaMail полностью, он просто делает его более предсказуемым и удобным для тестирования.

person skaffman    schedule 01.03.2009

JavaMail будет читать весь поток, возможно, буферизуя его. Я согласен с @skaffman, однако я считаю, что он просто сильно оптимизирован без комментариев о том, почему, и это затрудняет его использование.

MimeMessage.parse(InputStream) - это то, с чем вы имеете дело. Он читает весь поток, не закрывая его, если только вы не используете SharedInputStream. Как вы видите, он оставит InputStream в конце.

MimeMessage мало что делает для интерпретации данных в потоке, поэтому я думаю, что вы «упускаете что-то большее», чтобы использовать свои слова. Почему вы ожидаете, что он будет интерпретировать данные? Можете ли вы просто отправлять составные сообщения, которые вы используете для создания своих потоков?

person Jason Thrasher    schedule 18.06.2009