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