|
|
(не показана 1 промежуточная версия 1 участника) |
Строка 1: |
Строка 1: |
− | {{Цикл/Java EE}}
| + | #REDIRECT [[LXF97:Java EE]] |
− | | + | |
− | ==Почтовый сервис==
| + | |
− | : '''ЧАСТЬ 9''' Хотите оснастить свою программу возможностью писать «на деревню дедушке»? '''Александр Бабаев''' знает подходящее средство.
| + | |
− | | + | |
− | | + | |
− | Несмотря на засилье браузера в серии, JEE им не ограничивается. Давайте попробуем посылать письма из Java (ведь каждая хорошая программа должна уметь сообщать разработчикам об ошибках), и сделаем это приложение не браузерным, а «обычным».
| + | |
− | | + | |
− | ===Коротко о почте===
| + | |
− | | + | |
− | Чтобы работать с почтой, нужно уметь её отправлять и получать. При попытке разобраться в этом вы наткнетесь на следующие буквосочетания (с разными вариациями): '''SMTP, POP, IMAP'''. Рассмотрим кратко, что это такое и как этим пользоваться (а также где почитать поподробнее).
| + | |
− | | + | |
− | * '''SMTP'''
| + | |
− | При помощи Simple Mail Transfer Protocol (простого протокола передачи почты) почта передается с клиента на сервер. Отправляется то есть. Протокол текстовый, и если есть желание, можно отправлять письма прямо из telnet’а.
| + | |
− | | + | |
− | * '''POP'''
| + | |
− | Тоже текстовый протокол, но уже не для передачи, а для приема сообщений. Может выдать информацию по почтовому ящику (сколько
| + | |
− | сообщений, какой их размер), загрузить сообщение по номеру и так далее.
| + | |
− | | + | |
− | * '''IMAP'''
| + | |
− | '''POP''' предполагает, что почта скачивается на клиент и там уже раскладывается по папкам, обрабатывается, группируется. При этом с сервера сообщения стираются. Это не всегда удобно. Как раз для хранения почты на сервере создан протокол '''IMAP'''. Как и предыдущие два, он текстовый. Но с его помощью можно не только получить сообщения, но и создать на сервере папку, переместить письмо куда-нибудь, подписаться на получение изменений (новых писем) в папке, и так далее.
| + | |
− | | + | |
− | * '''GoogleMail/HotMail/…'''
| + | |
− | Но и это не всё. Протоколы протоколами, но некоторые сервисы работают «по-своему». И если '''GoogleMail''' предоставляет '''POP'''-интерфейс, то '''Hotmail''', например, нет. В таком случае обычно есть какой-то свой, нестандартный протокол.
| + | |
− | | + | |
− | ===Итого===
| + | |
− | | + | |
− | В итоге получается, что разных протоколов много-много (это не считая вариантов и нюансов, комбинаций которых сотни). И чтобы по-человечески все это обрабатывать, пришлось писать бы огромное количество кода. А потом его отлаживать… Поэтому обычно, рассматривая сетевые приложения, отправку/получение писем обходят стороной. Действительно, зачем? Кому нужно, и так разберется.
| + | |
− | | + | |
− | Но в Java, как всегда, уже позаботились о том, чтобы упростить жизнь человеку, которому нужно рассылать письма – позаботились на самом высшем уровне (в Sun Microsystems) и достаточно качественно.
| + | |
− | | + | |
− | ===И где волшебная кнопка?===
| + | |
− | | + | |
− | Есть такая замечательная библиотека, '''JavaMail'''. Она достаточно крупная (224 килобайта только JAR-файл), зато и умеет очень много. А чего не умеет – можно научить, благо архитектура настраиваемая. Давайте посмотрим, как с ней работать.
| + | |
− | | + | |
− | ===Подготовка===
| + | |
− | | + | |
− | Для начала скачаем саму библиотеку. Страничка продукта находится по адресу http://java.sun.com/products/javamail/; скачивать нужно, как водится, последний релиз (1.4). Также понадобится JavaBeans Activation Framework (JAF), которую можно загрузить со странички рядом: http://java.sun.com/products/javabeans/jaf/index.jsp.
| + | |
− | | + | |
− | После загрузки и разархивирования, получаем две библиотеки: '''mail.jar''' и '''activation.jar'''. Первая из них поддерживает все возможные протоколы, поэтому размер имеет достаточно внушительный. Если что-то из этого многообразия вам не нужно, можно воспользоваться урезанными версиями, они также содержатся в '''mail.jar'''.
| + | |
− | | + | |
− | Создадим каталог для проекта ('''QuickMailer'''), в нем заведем подкаталог '''libs''' и положим туда эти два jar-файла. Потом заведем другой подкаталог ('''src'''), для записи исходных текстов.
| + | |
− | | + | |
− | ===Окошки===
| + | |
− | | + | |
− | Сделаем окошко для отправки сообщения. Оно будет простое, как на рис. 1.
| + | |
− | [[Изображение:LXF97_JAVA1.jpg|Рис. 1. Окно создания и отправки сообщений.]] | + | |
− | | + | |
− | Подробно рассказать про то, как создаются формы, не хватит места. Но привести код, создающий такое окошко – запросто.
| + | |
− | | + | |
− | <code>
| + | |
− | import javax.swing.*;
| + | |
− | import javax.swing.border.EmptyBorder;
| + | |
− | import java.awt.*;
| + | |
− | import java.awt.event.ActionListener;
| + | |
− | import java.awt.event.ActionEvent;
| + | |
− | public class QuickMailerForm extends JFrame {
| + | |
− | private JTextField _fieldTo;
| + | |
− | private JTextField _fieldSubject;
| + | |
− | private JEditorPane _message;
| + | |
− | private JButton _buttonSend;
| + | |
− | public QuickMailerForm() throws HeadlessException {
| + | |
− | setTitle(“Быстро Мэйлер”);
| + | |
− | setDefaultCloseOperation(EXIT_ON_CLOSE);
| + | |
− | createLayout();
| + | |
− | createActions();
| + | |
− | pack();
| + | |
− | setSize(700, 560);
| + | |
− | Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
| + | |
− | setLocation((int) (screenSize.getWidth() - 700)/2, (int) ((screenSize.getHeight() - 560)/2));
| + | |
− | }
| + | |
− | }
| + | |
− | </code>
| + | |
− | | + | |
− | Как видно, класс наследуется от '''JFrame''', это окно приложения. Имеется конструктор, где окну присваивается заголовок, устанавливается размер и положение в середине экрана. Также есть две функции: первая создает компоненты ('''createLayout'''), вторая «вешает» на кнопку Отправить обработчик события, который собирает информацию и вызывает метод отправки почты.
| + | |
− | | + | |
− | Вот как создаются компоненты формы:
| + | |
− | | + | |
− | <code>
| + | |
− | private void createLayout() {
| + | |
− | JPanel labelsPanel = new JPanel(new GridLayout(2, 1));
| + | |
− | labelsPanel.add(new JLabel(“EMail получателя:”, JLabel.RIGHT));
| + | |
− | labelsPanel.add(new JLabel(“Тема письма:”, JLabel.RIGHT));
| + | |
− | JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
| + | |
− | _buttonSend = new JButton(“Отправить”);
| + | |
− | buttonsPanel.add(_buttonSend);
| + | |
− | JPanel fieldsPanel = new JPanel(new GridLayout(2, 1));
| + | |
− | _fieldTo = new JTextField();
| + | |
− | _fieldSubject = new JTextField();
| + | |
− | fieldsPanel.add(_fieldTo);
| + | |
− | fieldsPanel.add(_fieldSubject);
| + | |
− | JPanel controlsPanel = new JPanel(new BorderLayout(5, 5));
| + | |
− | controlsPanel.add(labelsPanel, BorderLayout.WEST);
| + | |
− | controlsPanel.add(fieldsPanel, BorderLayout.CENTER);
| + | |
− | JPanel mainPanel = new JPanel(new BorderLayout(5, 5));
| + | |
− | _message = new JEditorPane(“text/rtf”, “”);
| + | |
− | mainPanel.add(controlsPanel, BorderLayout.NORTH);
| + | |
− | mainPanel.add(new JScrollPane(_message), BorderLayout.CENTER);
| + | |
− | mainPanel.add(buttonsPanel, BorderLayout.SOUTH);
| + | |
− | mainPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
| + | |
− | setContentPane(mainPanel);
| + | |
− | }
| + | |
− | </code>
| + | |
− | | + | |
− | Вкратце, здесь создается несколько панелей, вложенных друг в друга. Чтобы выглядело получше, задаются отступы и межкомпонентные расстояния.
| + | |
− | | + | |
− | Последний метод – создание обработчика события нажатия на кнопку:
| + | |
− | | + | |
− | <code>
| + | |
− | private void createActions() {
| + | |
− | _buttonSend.addActionListener(new ActionListener() {
| + | |
− | public void actionPerformed(ActionEvent e) {
| + | |
− | try {
| + | |
− | QuickMailer.sendMessage(“alex@jdnevnik.com”, _fieldTo.getText(), _fieldSubject.getText(),
| + | |
− | _message.getDocument().getText(0, _message.getDocument().getLength()));
| + | |
− | } catch (Exception e1) {
| + | |
− | e1.printStackTrace();
| + | |
− | }
| + | |
− | }
| + | |
− | });
| + | |
− | }
| + | |
− | </code>
| + | |
− | | + | |
− | Тут все достаточно просто. Вытаскиваются параметры письма из полей, после чего вызывается некий метод '''sendMessage''', который мы сейчас и рассмотрим подробнее.
| + | |
− | | + | |
− | ===Собственно отправка сообщения===
| + | |
− | | + | |
− | Предполагается, что у вас на localhost’е настроен smtp-сервер (у меня стоит ''postfix''), либо есть доступ к какому-то другому (который не требует авторизации: с ней разбираться пока не будем).
| + | |
− | | + | |
− | Для начала создадим адреса отправителя и получателя:
| + | |
− | | + | |
− | <code>
| + | |
− | public static void sendMessage(String aFrom, String aTo, String aSubject,
| + | |
− | String aMessageText) throws Exception {
| + | |
− | InternetAddress from = new InternetAddress(aFrom, “From”);
| + | |
− | InternetAddress to = new InternetAddress(aTo, “To”);
| + | |
− | </code>
| + | |
− | | + | |
− | Теперь нужно настроить так называемый транспорт, который будет заниматься отправкой сообщения.
| + | |
− | | + | |
− | <code>
| + | |
− | Properties props = new Properties();
| + | |
− | props.put(“mail.transport.protocol”, “smtp”);
| + | |
− | props.put(“mail.smtp.host”, “localhost”);
| + | |
− | props.put(“mail.smtp.port”, “25”);
| + | |
− | Session session = Session.getDefaultInstance(props);
| + | |
− | Transport transport = session.getTransport();
| + | |
− | </code>
| + | |
− | | + | |
− | Теперь – создадим само сообщение.
| + | |
− | | + | |
− | <code>
| + | |
− | MimeMessage message = new MimeMessage(session);
| + | |
− | message.setFrom(from);
| + | |
− | message.setRecipient(Message.RecipientType.TO, to);
| + | |
− | message.setSubject(aSubject, “utf-8”);
| + | |
− | message.setContent(aMessageText, “text/plain; charset=utf-8”);
| + | |
− | </code>
| + | |
− | | + | |
− | И, наконец, отошлем письмо.
| + | |
− | | + | |
− | <code>
| + | |
− | transport.connect();
| + | |
− | transport.sendMessage(message, new Address[]{to});
| + | |
− | transport.close();
| + | |
− | }
| + | |
− | </code>
| + | |
− | | + | |
− | Как можно заметить, все предельно просто и понятно – но исключительно потому, что сам пример простой. Система достаточно мощная, чтобы справиться и с авторизацией, и с сообщениями на разныхязыках, и с вложениями файлов.
| + | |
− | | + | |
− | ===Собираем все вместе===
| + | |
− | | + | |
− | Осталось только написать метод, который будет все это запускать.
| + | |
− | | + | |
− | <code>
| + | |
− | public static void main(String[] args) {
| + | |
− | QuickMailerForm form = new QuickMailerForm();
| + | |
− | form.setVisible(true);
| + | |
− | }
| + | |
− | </code>
| + | |
− | | + | |
− | У меня после написания кода получилось два файла, '''QuickMailer.java''' и '''QuickMailerForm.java'''. Скомпилируем их (выполнив, находясь в каталоге, в котором находится '''src''' и '''libs'''):
| + | |
− | | + | |
− | <code>
| + | |
− | javac -cp libs/activation.jar:libs/commons-email-1.0.jar:libs/mail.jar -encoding utf-8 -d out src/*.java
| + | |
− | </code>
| + | |
− | | + | |
− | Обратите внимание на часть команды после '''-cp'''. Это указание компилятору, где искать используемые в коде классы, кроме стандартных. Часть команды после '''-d''' определяет каталог, куда будут складываться скомпилированные классы. И, наконец, так как мы создавали файлы в кодировке UTF-8 (в ней представлены исходные тексты на диске), то и в командной строке это нужно указать, иначе будет выбрана кодировка по умолчанию, а это не всегда верно.
| + | |
− | | + | |
− | Запустим:
| + | |
− | | + | |
− | <code>
| + | |
− | cd out
| + | |
− | java -cp .:../libs/activation.jar:../libs/commons-email-1.0.jar:../libs/mail.jar
| + | |
− | QuickMailer
| + | |
− | </code>
| + | |
− | | + | |
− | Можно отправлять письма.
| + | |
− | | + | |
− | ===А получить?===
| + | |
− | | + | |
− | На новое окошко места уже не хватит. Ограничимся просмотром кода, который нужно написать для того, чтобы получить письмо, например, по протоколу '''POP'''.
| + | |
− | | + | |
− | <code>
| + | |
− | Properties props = new Properties();
| + | |
− | Session session = Session.getDefaultInstance(props, null);
| + | |
− | Store store = session.getStore(“pop3”);
| + | |
− | store.connect(aHost, aUserName, aPassword);
| + | |
− | Folder folder = store.getFolder(“INBOX”);
| + | |
− | folder.open(Folder.READ_ONLY);
| + | |
− | Message message[] = folder.getMessages();
| + | |
− | for (int i = 0, n = message.length; i < n; i++) {
| + | |
− | System.out.println(i + “: “ + message[i].getFrom()[0] + “\t” + message[i].
| + | |
− | getSubject());
| + | |
− | }
| + | |
− | folder.close(false);
| + | |
− | store.close();
| + | |
− | </code>
| + | |
− | | + | |
− | Данный кусок кода просто выведет список всех писем на сервере.
| + | |
− | | + | |
− | ===Ну, а если не мудрить…===
| + | |
− | | + | |
− | Есть вариант и попроще. Если программа работает с почтой активно, можно использовать библиотеку наших постоянных друзей из ''apache-commons''. Называется она '''commons-email''', и ее страничка располагается по адресу http://commons.apache.org/email/. Скачав библиотеку, положим её в '''libs''', к '''mail.jar''' и компании. Теперь попробуем отправить письмо с ее помощью:
| + | |
− | | + | |
− | <code>
| + | |
− | public static void sendMessageCommonsEMail(String aFrom, String aTo,
| + | |
− | String aSubject, String aMessageText) throws EmailException {
| + | |
− | SimpleEmail email = new SimpleEmail();
| + | |
− | email.setHostName(“localhost”);
| + | |
− | email.setFrom(aFrom, “From”);
| + | |
− | email.addTo(aTo, “To”);
| + | |
− | email.setSubject(aSubject);
| + | |
− | email.setMsg(aMessageText);
| + | |
− | email.send();
| + | |
− | }
| + | |
− | </code>
| + | |
− | | + | |
− | Насколько все проще и понятнее сразу стало! А если нужно файл приложить? Да пожалуйста:
| + | |
− | | + | |
− | <code>
| + | |
− | public static void sendMessageWithAttachment(String aFrom, String aTo,
| + | |
− | String aSubject, String aMessageText) throws EmailException {
| + | |
− | EmailAttachment attachment = new EmailAttachment();
| + | |
− | attachment.setPath(“attachments/attachment.zip”);
| + | |
− | attachment.setDisposition(EmailAttachment.ATTACHMENT);
| + | |
− | attachment.setDescription(“Файл-приложение к письму”);
| + | |
− | attachment.setName(“attachment.zip”);
| + | |
− | MultiPartEmail email = new MultiPartEmail();
| + | |
− | email.setHostName(“localhost”);
| + | |
− | email.setFrom(aFrom, “From”);
| + | |
− | email.addTo(aTo, “To”);
| + | |
− | email.setSubject(aSubject);
| + | |
− | email.setMsg(aMessageText);
| + | |
− | email.attach(attachment);
| + | |
− | email.send();
| + | |
− | }
| + | |
− | </code>
| + | |
− | | + | |
− | Здесь тоже ничего сложного нет. Создать приложение в письме можно и используя только '''JavaMail''', но там это получается достаточно непросто, и длиннее раза в три-четыре.
| + | |
− | | + | |
− | ===Про спам===
| + | |
− | | + | |
− | Конечно, работа с почтой не так проста, как это отражено в статье. Есть и проблема спама (а для программно отправляемых сообщений – проблема того, что оно с большой вероятностью посчитается именно спамом), и проблема корректности. SMTP-протокол, в частности, достаточно старый, и там много неточностей, неявных правил и так далее. В общем, то, что есть '''JavaMail''' – это отлично, и она очень сильно помогает при работе с почтовыми сообщениями, но панацеей тем не менее не является. Все равно нужно представлять себе, как работает протокол, какие заголовки нужно ставить, как обрабатывают сообщения разные почтовые клиенты (чтобы письмо нормально там показывалось, а не крякозябрами) и много чего еще.
| + | |
− | | + | |
− | Но все же, надеюсь, теперь можно не бояться страшных буквенных сочетаний, связанных с почтой, и спокойно встраивать в прорамму еще одно удобнейшее средство коммуникации: электронные письма. '''LXF'''
| + | |