Работа с сообщениями JavaMail

Свободно распространяемая библиотека JavaMail предназначена для подключения к почтовым серверам с целью получения и отправки электронной почты с использованием протоколов SMTP, POP3 и IMAP. Библиотека позволяет отправлять сообщения с вложениями и удалять их на почтовом сервере.

Протоколы JavaMail API

  • SMTP (Simple Mail Transfer Protocol простой протокол передачи почты) - это широко используемый сетевой протокол для передачи электронной почты в сетях TCP/IP. Электронные почтовые серверы используют протокол SMTP для отправки и получения почтовых сообщений. Клиентские почтовые приложения обычно используют SMTP только для отправки сообщений на почтовый сервер и для ретрансляции с использованием порта 25 (465 для SSL). Т.е. протокол SMTP применяют для передачи исходящей почты.
  • IMAP (Internet Message Access Protocol) — протокол прикладного уровня для доступа к электронной почте. Протокол IMAP предоставляет пользователю широкие возможности для работы с почтовыми ящиками, расположенными на удаленном сервере. Почтовая программа, использующая данный протокол, получает доступ к хранилищу корреспонденции на сервере так, как будто эта корреспонденция расположена на компьютере получателя. Электронными письмами можно управлять с компьютера пользователя без пересылки файлов по сети. Протокол IMAP использует транспортный протокол TCP и прослушивает порт 143.
  • POP3 (Post Office Protocol Version 3 протокол почтового отделения 3-ей версии) - это стандартный сетевой протокол прикладного уровня, используемый клиентами электронной почты для получения почты с удаленного сервера по TCP/IP-соединению. Протокол POP3 поддерживает простые требования «загрузи-и-удали» для доступа к удаленным почтовым ящикам. Сервер POP3 прослушивает порт 110.

Отправка сообщения по протоколу SMTP

Создадим класс подготовки и отправки сообщения SendEmail.java. В классе определим поля сервера SMTP, поля авторизации на сервере и дополнительные поля вложения файла FILE_PATH и адресата для ответа REPLY_TO.

Конструктор класса SendEmail.java, настройка SMTP SSL

public class SendEmail
{
    private            Message  message        = null;
    protected  static  String   SMTP_SERVER    = null;
    protected  static  String   SMTP_Port      = null;
    protected  static  String   SMTP_AUTH_USER = null;
    protected  static  String   SMTP_AUTH_PWD  = null;
    protected  static  String   EMAIL_FROM     = null;
    protected  static  String   FILE_PATH      = null;
    protected  static  String   REPLY_TO       = null;
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    public SendEmail(final String emailTo, final String thema)
    {
        // Настройка SMTP SSL
        Properties properties = new Properties();
        properties.put("mail.smtp.host"               , SMTP_SERVER);
        properties.put("mail.smtp.port"               , SMTP_Port  );
        properties.put("mail.smtp.auth"               , "true"     );
        properties.put("mail.smtp.ssl.enable"         , "true"     );
        properties.put("mail.smtp.socketFactory.class", 
                                   "javax.net.ssl.SSLSocketFactory");
        try {
            Authenticator auth = new EmailAuthenticator(SMTP_AUTH_USER,
                                                        SMTP_AUTH_PWD);
            Session session = Session.getDefaultInstance(properties,auth);
            session.setDebug(false);

            InternetAddress email_from = new InternetAddress(EMAIL_FROM);
            InternetAddress email_to   = new InternetAddress(emailTo   );
            InternetAddress reply_to   = (REPLY_TO != null) ? 
                                    new InternetAddress(REPLY_TO) : null;
            message = new MimeMessage(session); 
            message.setFrom(email_from);   
            message.setRecipient(Message.RecipientType.TO, email_to);
            message.setSubject(thema);
            if (reply_to != null)
                message.setReplyTo (new Address[] {reply_to});
        } catch (AddressException e) {
            System.err.println(e.getMessage());
        } catch (MessagingException e) {
            System.err.println(e.getMessage());
        }
    }
}

Конструктор класса SendEmail.java включает два параметра : email получателя и тему сообщения. Первоначально в конструкторе определяются свойства сервера; подключение к серверу будет выполняться по защищенному каналу SSL с авторизацией пользователя. Для этого создается класс авторизации auth типа Authenticator и формируется сессия класса javax.mail.Session, которой в качестве параметров передаются свойства properties и auth.

На следующем шаге формируется сообщение для отправки. Предварительно создаются адресаты (тип InternetAddress) email_from, email_to, reply_to . Адресат сообщения reply_to используется для случая, когда получатель должен отвечать не непосредственно отправителю email_from, который играет роль сервера перессылки. Данную опцию должен поддерживать сервер получателя, чтобы при нажатии на кнопку «Ответить» пользователю открывалось новое письмо с подстановкой в поле адресата «Кому» значения reply_to, а не email_from.

Конструктор объекта сообщения message типа MimeMessage в качестве параметра получает значение созданной защищенной с сервером SMTP сессии session.

Листинг аутентификатора EmailAuthenticator.java

Класс Authenticator обеспечивает доступ к защищенным почтовым ресурсам на сервере. В примерах этот класс используется для установления сессии с сервером. Когда потребуется авторизация, библиотека JavaMail будет вызывать метод getPasswordAuthentication.

public class EmailAuthenticator extends javax.mail.Authenticator
{
    private String login   ;
    private String password;
    public EmailAuthenticator (final String login, final String password)
    {
        this.login    = login;
        this.password = password;
    }
    public PasswordAuthentication getPasswordAuthentication()
    {
        return new PasswordAuthentication(login, password);
    }
}

Вложение файла в сообщение, MimeBodyPart

Для вложения файла в сообщение необходимо создать объект типа MimeBodyPart, в котором в качестве параметров указать путь к файлу и наименование файла. Следующий листинг представляет функцию формирования объекта MimeBodyPart с содержимом файла, который можно вкладывать в сообщение.

private MimeBodyPart createFileAttachment(String filepath) 
                                          throws MessagingException
{
    // Создание MimeBodyPart
    MimeBodyPart mbp = new MimeBodyPart();

    // Определение файла в качестве контента
    FileDataSource fds = new FileDataSource(filepath);
    mbp.setDataHandler(new DataHandler(fds));
    mbp.setFileName(fds.getName());
    return mbp;
}

Отправка сообщения, Multipart

Функция sendMessage завершает оформление сообщения. Для этого создается объект mmp типа MimeMultipart, в который можно вложить другие объекты типа MimeBodyPart (текст, файл). Метод сообщения setContent, которому в качестве параметра передается объект MimeMultipart, определяет содержимое сообщения.

public boolean sendMessage (final String text)
{
    boolean result = false;
    try {
        // Содержимое сообщения
        Multipart mmp = new MimeMultipart();
        // Текст сообщения	
        MimeBodyPart bodyPart = new MimeBodyPart();
        bodyPart.setContent(text, "text/plain; charset=utf-8");
        mmp.addBodyPart(bodyPart);
        // Вложение файла в сообщение
        if (FILE_PATH != null) {
            MimeBodyPart mbr = createFileAttachment(FILE_PATH);
            mmp.addBodyPart(mbr);
        }
        // Определение контента сообщения
        message.setContent(mmp);
        // Отправка сообщения 
        Transport.send(message);
        result = true;
    } catch (MessagingException e){
        // Ошибка отправки сообщения
        System.err.println(e.getMessage());
    }
    return result;
}

Для отправки сообщения используется метод send класса Transport, который имеет следующие перегруженные методы send :

static void send(Message message)
                 throws MessagingException
static void send(Message message, Address[] addresses) 
                 throws MessagingException

Первый метод send отправит сообщение получателям, указанным в сообщении. Второй метод отправит сообщение всем получателям, определенным в массиве addresses за исключением тех, которые определены в сообщении.

Пример отправки сообщения

Для отправки сообщения создадим файл свойств email.properties, в котором определим все необходимые параметры сервера SMTP и параметры подключения. В примере используем почтовый сервер Yandex, хотя настройки позволяют использовать и другие известные почтовые сервера.

#
# Параметры сервера SMTP
#
server=smtp.yandex.ru
port=465
#
# email отправителя
#
from=...@yandex.ru
user=...
pass=...
#
# email получателя 
#
to=...@yandex.ru
#
# email для ответа  
#
replyto=java-online@mail.ru
#
# Сообщение : тема и текст
#
thema=Пересылка сообщения
text=Тестовое сообщение для перессылки

В файле свойств необходимо заполнить 4 поля, значения которых определены в виде многоточий. Если, к примеру, почтовый адрес в Yandex'e имеет вид qwerty@yandex.ru, то пользователь user будет иметь значение qwerty.

Следующий класс EmailTest используется для отправки сообщения. Сначала из файла свойств читаются параметры почтового сервера и параметры подключения. Для чтения файла используется класс InputStream, более подробно о котором сказано на странице Потоки ввода. После этого создается объект SendEmail и выполняется отправка сообщения.

public class EmailTest
{
    private  final  static  String  PROPS_FILE = "email.properties";

    public static void main(String[] args)
    {
        try {
            InputStream is = new FileInputStream(PROPS_FILE);
            if (is != null) {
                Reader reader = new InputStreamReader(is, "UTF-8");
                Properties pr = new Properties();
                pr.load(reader);
                SendEmail.SMTP_SERVER    = pr.getProperty ("server" );
                SendEmail.SMTP_Port      = pr.getProperty ("port"   );
                SendEmail.EMAIL_FROM     = pr.getProperty ("from"   );
                SendEmail.SMTP_AUTH_USER = pr.getProperty ("user"   );
                SendEmail.SMTP_AUTH_PWD  = pr.getProperty ("pass"   );
                SendEmail.REPLY_TO       = pr.getProperty ("replyto");
                SendEmail.FILE_PATH      = PROPS_FILE;

                String emailTo = pr.getProperty ("to"   );
                String thema   = pr.getProperty ("thema");
                String text    = pr.getProperty ("text" );

                is.close();

                SendEmail se = new SendEmail(emailTo, thema);
                se.sendMessage(text);
                System.out.println ("Сообщение отправлено");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Представленный пример с небольшими изменениями можно использовать для рассылки сообщений.

Чтение сообщений по протоколу IMAP

Как было отмечено выше протокол IMAP следует использовать для чтения почтовых сообщений. При использовании протокола IMAP программа должна подключиться к серверу и получить доступ к папке INBOX. Для настройки сессии подключения по протоколу IMAP необходимо указать следующие данные :

  • адрес почтового сервера (в следующем примере мы используем imap.yandex.ru);
  • порт подключения — 993;
  • защита соединения — SSL.

Для доступа к почтовому серверу также необходимо указать логин и пароль пользователя. Если для получение почты (подключения к почтовому серверу) используется ящик вида «login@yandex.ru», то логином является часть адреса до знака «@».

Листинг примера чтения почтовых сообщений

В следующем примере сначала настраивается сессия с почтовым сервером для работы по «закрытому» каналу связи. После этого выполняется чтение последнего сообщения папки «INBOX», которое было отправлено в предыдущем примере. В консоль выводится информация этого последнего сообщения.

Чтобы пример сработал необходимо вместо многоточий подставить реальные логин и пароль.

public class ReadEmail
{
    String   IMAP_AUTH_EMAIL = "...@yandex.ru" ;
    String   IMAP_AUTH_PWD   = "..."           ;
    String   IMAP_Server     = "imap.yandex.ru";
    String   IMAP_Port       = "993"           ;
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    public ReadEmail()
    {
        Properties properties = new Properties();
        properties.put("mail.debug"          , "false"  );
        properties.put("mail.store.protocol" , "imaps"  );
        properties.put("mail.imap.ssl.enable", "true"   );
        properties.put("mail.imap.port"      , IMAP_Port);

        Authenticator auth = new EmailAuthenticator(IMAP_AUTH_EMAIL,
                                                    IMAP_AUTH_PWD);
        Session session = Session.getDefaultInstance(properties, auth);
        session.setDebug(false);
        try {
            Store store = session.getStore();

            // Подключение к почтовому серверу
            store.connect(IMAP_Server, IMAP_AUTH_EMAIL, IMAP_AUTH_PWD);

            // Папка входящих сообщений
            Folder inbox = store.getFolder("INBOX");

            // Открываем папку в режиме только для чтения
            inbox.open(Folder.READ_ONLY);

            System.out.println("Количество сообщений : " + 
                                String.valueOf(inbox.getMessageCount()));
            if (inbox.getMessageCount() == 0)
                return;
            // Последнее сообщение; первое сообщение под номером 1
            Message message = inbox.getMessage(inbox.getMessageCount());
            Multipart mp = (Multipart) message.getContent();
            // Вывод содержимого в консоль
            for (int i = 0; i < mp.getCount(); i++){
                BodyPart  bp = mp.getBodyPart(i);
                if (bp.getFileName() == null)
                    System.out.println("    " + i + ". сообщение : '" + 
                                                       bp.getContent() + "'");
                else
                    System.out.println("    " + i + ". файл : '" +
                                                       bp.getFileName() + "'");
            }
        } catch (NoSuchProviderException e) {
            System.err.println(e.getMessage());
        } catch (MessagingException e) {
            System.err.println(e.getMessage());
        } catch (IOException e) {
            System.err.println(e.getMessage());
        }
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    public static void main(String[] args)
    {
        new ReadEmail();
        System.exit(0);
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}

В примере используются абстрактные классы

  • Store - представляет собой хранилище сообщений, поддерживаемых почтовым сервером и сгруппированных по владельцу,
  • Folder - предоставляет возможность иерархически организовывать сообщения. Папки могут содержать сообщения и поддиректории.

В консоль будет выведено следующее сообщение. Конечно, количество сообщений в почтовом ящике «INBOX» будет другим.


Количество сообщений : 30
    0. сообщение : 'Тестовое сообщение для перессылки'
    1. файл : 'email.properties'
 

Скачать примеры использования JavaMail

Исходные коды рассмотренных примеров использования библиотеки JavaMail в виде проекта Eclipse можно скачать здесь (930 Кб).

  Рейтинг@Mail.ru