OSDN Git Service

aef932a11719000dd35d1de423ddaea8e099c00c
[spring-ext/ozacc-mail.git] / src / main / java / com / ozacc / mail / impl / SendMailImpl.java
1 package com.ozacc.mail.impl;
2
3 import java.io.UnsupportedEncodingException;
4 import java.util.Date;
5 import java.util.Properties;
6
7 import javax.mail.AuthenticationFailedException;
8 import javax.mail.MessagingException;
9 import javax.mail.Session;
10 import javax.mail.Transport;
11 import javax.mail.internet.InternetAddress;
12 import javax.mail.internet.MimeMessage;
13
14 import org.apache.commons.logging.Log;
15 import org.apache.commons.logging.LogFactory;
16
17 import com.ozacc.mail.Mail;
18 import com.ozacc.mail.MailAuthenticationException;
19 import com.ozacc.mail.MailBuildException;
20 import com.ozacc.mail.MailException;
21 import com.ozacc.mail.MailSendException;
22 import com.ozacc.mail.SendMail;
23
24 /**
25  * SendMailインターフェースの実装クラス。
26  * 
27  * @since 1.0
28  * @author Tomohiro Otsuka
29  * @version $Id: SendMailImpl.java,v 1.7.2.6 2007/03/30 13:03:44 otsuka Exp $
30  */
31 public class SendMailImpl implements SendMail {
32
33         private static Log log = LogFactory.getLog(SendMailImpl.class);
34
35         /** デフォルトのプロトコル。「smtp」 */
36         public static final String DEFAULT_PROTOCOL = "smtp";
37
38         /**
39          * デフォルトのポート。「-1」<br>
40          * -1はプロトコルに応じた適切なポートを設定する特別な値。
41          * */
42         public static final int DEFAULT_PORT = -1;
43
44         /** デフォルトのSMTPサーバ。「localhost」 */
45         public static final String DEFAULT_HOST = "localhost";
46
47         /** ISO-2022-JP */
48         public static final String JIS_CHARSET = "ISO-2022-JP";
49
50         private static final String RETURN_PATH_KEY = "mail.smtp.from";
51
52         /** 接続タイムアウト */
53         private static final int DEFAULT_CONNECTION_TIMEOUT = 5000;
54
55         /** 読込タイムアウト */
56         private static final int DEFAULT_READ_TIMEOUT = 5000;
57
58         private String protocol = DEFAULT_PROTOCOL;
59
60         private String host = DEFAULT_HOST;
61
62         private int port = DEFAULT_PORT;
63
64         private String username;
65
66         private String password;
67
68         private String charset = JIS_CHARSET;
69
70         private String returnPath;
71
72         private String messageId;
73
74         private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
75
76         private int readTimeout = DEFAULT_READ_TIMEOUT;
77
78         /**
79          * コンストラクタ。
80          */
81         public SendMailImpl() {}
82
83         /**
84          * コンストラクタ。使用するSMTPサーバを指定します。
85          * 
86          * @param host SMTPサーバのホスト名、またはIPアドレス
87          */
88         public SendMailImpl(String host) {
89                 this();
90                 setHost(host);
91         }
92
93         /**
94          * @see com.ozacc.mail.SendMail#send(com.ozacc.mail.Mail)
95          */
96         public void send(Mail mail) throws MailException {
97                 send(new Mail[] { mail });
98         }
99
100         /**
101          * @see com.ozacc.mail.SendMail#send(com.ozacc.mail.Mail[])
102          */
103         public void send(Mail[] mails) throws MailException {
104                 MimeMessageWrapper[] mmws = new MimeMessageWrapper[mails.length];
105                 Session session = Session.getInstance(new Properties());
106                 for (int i = 0; i < mails.length; i++) {
107                         Mail mail = mails[i];
108
109                         // MimeMessageを生成
110                         MimeMessage message = createMimeMessage(session);
111                         if (isMessageIdCustomized()) {
112                                 mail.addHeader("Message-ID", ((OMLMimeMessage)message).getMessageId());
113                         }
114                         MimeMessageBuilder builder = new MimeMessageBuilder(message, charset);
115                         try {
116                                 builder.buildMimeMessage(mail);
117                         } catch (UnsupportedEncodingException e) {
118                                 throw new MailBuildException("サポートされていない文字コードが指定されました。", e);
119                         } catch (MessagingException e) {
120                                 throw new MailBuildException("MimeMessageの生成に失敗しました。", e);
121                         }
122
123                         // Return-Pathを取得
124                         String returnPath;
125                         if (mail.getReturnPath() != null) {
126                                 returnPath = mail.getReturnPath().getAddress();
127                         } else {
128                                 returnPath = this.returnPath;
129                         }
130
131                         mmws[i] = new MimeMessageWrapper(message, returnPath, mail.getEnvelopeTo());
132                 }
133                 processSend(mmws);
134         }
135
136         /**
137          * @see com.ozacc.mail.SendMail#send(javax.mail.internet.MimeMessage)
138          */
139         public void send(MimeMessage message) throws MailException {
140                 send(new MimeMessage[] { message });
141         }
142
143         /**
144          * @see com.ozacc.mail.SendMail#send(javax.mail.internet.MimeMessage[])
145          */
146         public void send(MimeMessage[] messages) throws MailException {
147                 MimeMessageWrapper[] mmws = new MimeMessageWrapper[messages.length];
148                 for (int i = 0; i < messages.length; i++) {
149                         mmws[i] = new MimeMessageWrapper(messages[i], returnPath);
150                 }
151                 processSend(mmws);
152         }
153
154         private void processSend(MimeMessageWrapper[] mmws) throws MailException {
155
156                 Properties prop = new Properties();
157                 // タイムアウトの設定
158                 prop.put("mail.smtp.connectiontimeout", String.valueOf(connectionTimeout));
159                 prop.put("mail.smtp.timeout", String.valueOf(readTimeout));
160                 //      mail.smtp.authプロパティの設定
161                 if (username != null && !"".equals(username) && password != null && !"".equals(password)) {
162                         prop.put("mail.smtp.auth", "true");
163                 }
164                 Session session = Session.getInstance(prop);
165
166                 Transport transport = null;
167                 try {
168                         // SMTPサーバに接続
169                         log.debug("SMTPサーバ[" + host + "]に接続します。");
170                         transport = session.getTransport(protocol);
171                         transport.connect(host, port, username, password);
172                         log.debug("SMTPサーバ[" + host + "]に接続しました。");
173
174                         for (int i = 0; i < mmws.length; i++) {
175                                 MimeMessage mimeMessage = mmws[i].getMimeMessage();
176                                 //      Return-Pathをセット
177                                 String returnPath = mmws[i].getReturnPath();
178                                 if (returnPath != null) {
179                                         session.getProperties().put(RETURN_PATH_KEY, returnPath);
180                                         log.debug("Return-Path[" + returnPath + "]を設定しました。");
181                                 }
182                                 // 送信日時をセット
183                                 mimeMessage.setSentDate(new Date());
184                                 mimeMessage.saveChanges();
185
186                                 // 送信
187                                 log.debug("メールを送信します。");
188                                 if (mmws[i].hasEnvelopeTo()) {
189                                         log.debug("メールはenvelope-toアドレスに送信されます。");
190                                         transport.sendMessage(mimeMessage, mmws[i].getEnvelopeTo());
191                                 } else {
192                                         transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
193                                 }
194                                 log.debug("メールを送信しました。");
195
196                                 // Return-Pathを解除
197                                 if (returnPath != null) {
198                                         session.getProperties().remove(RETURN_PATH_KEY);
199                                         log.debug("Return-Path設定をクリアしました。");
200                                 }
201                         }
202                 } catch (AuthenticationFailedException ex) {
203                         log.error("SMTPサーバ[" + host + "]への接続認証に失敗しました。", ex);
204                         throw new MailAuthenticationException(ex);
205                 } catch (MessagingException ex) {
206                         log.error("メールの送信に失敗しました。", ex);
207                         throw new MailSendException("メールの送信に失敗しました。", ex);
208                 } finally {
209                         if (transport != null && transport.isConnected()) {
210                                 log.debug("SMTPサーバ[" + host + "]との接続を切断します。");
211                                 try {
212                                         // SMTPサーバとの接続を切断
213                                         transport.close();
214                                 } catch (MessagingException e) {
215                                         log.error("SMTPサーバ[" + host + "]との接続切断に失敗しました。", e);
216                                         throw new MailException("SMTPサーバ[" + host + "]との接続切断に失敗しました。");
217                                 }
218                                 log.debug("SMTPサーバ[" + host + "]との接続を切断しました。");
219                         }
220                 }
221         }
222
223         /**
224          * 新しいMimeMessageオブジェクトを生成します。<br>
225          * messageIdプロパティがセットされている場合、OMLMimeMessageのインスタンスを生成します。
226          * 
227          * @return 新しいMimeMessageオブジェクト
228          */
229         private MimeMessage createMimeMessage(Session session) {
230                 if (isMessageIdCustomized()) {
231                         return new OMLMimeMessage(session, messageId);
232                 }
233                 return new MimeMessage(session);
234         }
235
236         /**
237          * Message-Idヘッダのドメイン部分を独自にセットしているかどうか判定します。
238          * 
239          * @return Message-Idヘッダのドメイン部分を独自にセットしている場合 true
240          */
241         private boolean isMessageIdCustomized() {
242                 return messageId != null;
243         }
244
245         /**
246          * エンコーディングに使用する文字コードを返します。
247          * 
248          * @return エンコーディングに使用する文字コード
249          */
250         public String getCharset() {
251                 return charset;
252         }
253
254         /**
255          * メールの件名や本文のエンコーディングに使用する文字コードを指定します。
256          * デフォルトは<code>ISO-2022-JP</code>です。
257          * <p>
258          * 日本語環境で利用する場合は通常変更する必要はありません。
259          * 
260          * @param charset エンコーディングに使用する文字コード
261          */
262         public void setCharset(String charset) {
263                 this.charset = charset;
264         }
265
266         /**
267          * セットされたSMTPサーバのホスト名、またはIPアドレスを返します。
268          * 
269          * @return SMTPサーバのホスト名、またはIPアドレス
270          */
271         public String getHost() {
272                 return host;
273         }
274
275         /**
276          * SMTPサーバのホスト名、またはIPアドレスをセットします。
277          * デフォルトは localhost です。
278          * 
279          * @param host SMTPサーバのホスト名、またはIPアドレス
280          */
281         public void setHost(String host) {
282                 this.host = host;
283         }
284
285         /**
286          * @return SMTPサーバ認証パスワード
287          */
288         public String getPassword() {
289                 return password;
290         }
291
292         /**
293          * SMTPサーバの接続認証が必要な場合にパスワードをセットします。
294          * 
295          * @param password SMTPサーバ認証パスワード
296          */
297         public void setPassword(String password) {
298                 this.password = password;
299         }
300
301         /**
302          * @return SMTPサーバのポート番号
303          */
304         public int getPort() {
305                 return port;
306         }
307
308         /**
309          * SMTPサーバのポート番号をセットします。
310          * 
311          * @param port SMTPサーバのポート番号
312          */
313         public void setPort(int port) {
314                 this.port = port;
315         }
316
317         /**
318          * プロトコルを返します。
319          * 
320          * @return プロトコル
321          */
322         public String getProtocol() {
323                 return protocol;
324         }
325
326         /**
327          * プロトコルをセットします。デフォルトは「smtp」。
328          * 
329          * @param protocol プロトコル
330          */
331         public void setProtocol(String protocol) {
332                 this.protocol = protocol;
333         }
334
335         /**
336          * @return Return-Pathアドレス
337          */
338         public String getReturnPath() {
339                 return returnPath;
340         }
341
342         /**
343          * Return-Pathアドレスをセットします。
344          * <p>
345          * 送信するMailインスタンスに指定されたFromアドレス以外のアドレスをReturn-Pathとしたい場合に使用します。
346          * ここでセットされたReturn-Pathより、MailインスタンスにセットされたReturn-Pathが優先されます。
347          * 
348          * @param returnPath Return-Pathアドレス
349          */
350         public void setReturnPath(String returnPath) {
351                 this.returnPath = returnPath;
352         }
353
354         /**
355          * @return SMTPサーバ認証ユーザ名
356          */
357         public String getUsername() {
358                 return username;
359         }
360
361         /**
362          * SMTPサーバの接続認証が必要な場合にユーザ名をセットします。
363          * 
364          * @param username SMTPサーバ認証ユーザ名
365          */
366         public void setUsername(String username) {
367                 this.username = username;
368         }
369
370         /**
371          * SMTPサーバとの接続タイムアウトをセットします。
372          * 単位はミリ秒。デフォルトは5,000ミリ秒(5秒)です。
373          * <p>
374          * -1を指定すると無限大になりますが、お薦めしません。
375          * 
376          * @since 1.1.4
377          * @param connectionTimeout SMTPサーバとの接続タイムアウト
378          */
379         public void setConnectionTimeout(int connectionTimeout) {
380                 this.connectionTimeout = connectionTimeout;
381         }
382
383         /**
384          * SMTPサーバへの送信時のタイムアウトをセットします。
385          * 単位はミリ秒。デフォルトは5,000ミリ秒(5秒)です。<br>
386          * 送信時にタイムアウトすると、<code>com.ozacc.mail.MailSendException</code>がスローされます。
387          * <p>
388          * -1を指定すると無限大になりますが、お薦めしません。
389          * 
390          * @since 1.1.4
391          * @param readTimeout SMTPサーバへの送受信時のタイムアウト
392          */
393         public void setReadTimeout(int readTimeout) {
394                 this.readTimeout = readTimeout;
395         }
396
397         /**
398          * 生成されるMimeMessageに付けられるMessage-Idヘッダのドメイン部分を指定します。<br>
399          * 指定されない場合(nullや空文字列の場合)は、JavaMailがMessage-Idヘッダを生成します。
400          * JavaMailが生成する「JavaMail.実行ユーザ名@ホスト名」のMessage-Idを避けたい場合に、このメソッドを使用します。
401          * <p>
402          * messageIdプロパティがセットされている場合、Mailから生成されるMimeMessageのMessage-Idには
403          * <code>タイムスタンプ + ランダムに生成される16桁の数値 + ここでセットされた値</code>
404          * が使用されます。
405          * <p>
406          * 生成されるMessage-Idの例。 (実際の数値部分は送信メール毎に変わります)<ul>
407          * <li>messageIdに'example.com'を指定した場合・・・1095714924963.5619528074501343@example.com</li>
408          * <li>messageIdに'@example.com'を指定した場合・・・1095714924963.5619528074501343@example.com (上と同じ)</li>
409          * <li>messageIdに'OML@example.com'を指定した場合・・・1095714924963.5619528074501343.OML@example.com</li>
410          * <li>messageIdに'.OML@example.com'を指定した場合・・・1095714924963.5619528074501343.OML@example.com (上と同じ)</li>
411          * </ul>
412          * <p>
413          * <strong>注:</strong> このMessage-Idは<code>send(Mail)</code>か<code>send(Mail[])</code>メソッドが呼びだれた時にのみ有効です。MimeMessageを直接送信する場合には適用されません。
414          * 
415          * @param messageId メールに付けられるMessage-Idヘッダのドメイン部分
416          * @throws IllegalArgumentException @を複数含んだ文字列を指定した場合
417          */
418         public void setMessageId(String messageId) {
419                 if (messageId == null || messageId.length() < 1) {
420                         return;
421                 }
422
423                 String[] parts = messageId.split("@");
424                 if (parts.length > 2) {
425                         throw new IllegalArgumentException("messageIdプロパティに'@'を複数含むことはできません。[" + messageId
426                                         + "]");
427                 }
428
429                 this.messageId = messageId;
430         }
431
432         /**
433          * MimeMessageインスタンスと、そのメールに対応するReturn-Path、envelope-toアドレスをラップするクラス。
434          * 
435          * @author Tomohiro Otsuka
436          * @version $Id: SendMailImpl.java,v 1.7.2.6 2007/03/30 13:03:44 otsuka Exp $
437          */
438         private static class MimeMessageWrapper {
439
440                 private MimeMessage mimeMessage;
441
442                 private String returnPath;
443
444                 private InternetAddress[] envelopeTo;
445
446                 public MimeMessageWrapper(MimeMessage mimeMessage, String returnPath) {
447                         this.mimeMessage = mimeMessage;
448                         this.returnPath = returnPath;
449                 }
450
451                 public MimeMessageWrapper(MimeMessage mimeMessage, String returnPath,
452                                 InternetAddress[] envelopeTo) {
453                         this.mimeMessage = mimeMessage;
454                         this.returnPath = returnPath;
455                         this.envelopeTo = envelopeTo;
456                 }
457
458                 public MimeMessage getMimeMessage() {
459                         return mimeMessage;
460                 }
461
462                 public String getReturnPath() {
463                         return returnPath;
464                 }
465
466                 public boolean hasEnvelopeTo() {
467                         return envelopeTo.length > 0;
468                 }
469
470                 public InternetAddress[] getEnvelopeTo() {
471                         return envelopeTo;
472                 }
473         }
474
475 }