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