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