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.8 2004/12/20 03:42:52 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
157 if (username != null && !"".equals(username) && password != null &&!"".equals(password)) {
158 prop.put("mail.smtp.auth", "true");
159 }
160 Session session = Session.getInstance(prop);
161
162 Transport transport = null;
163 try {
164
165 log.debug("SMTPサーバ[" + host + "]に接続します。");
166 transport = session.getTransport(protocol);
167 transport.connect(host, port, username, password);
168 log.debug("SMTPサーバ[" + host + "]に接続しました。");
169
170 for (int i = 0; i < mmws.length; i++) {
171 MimeMessage mimeMessage = mmws[i].getMimeMessage();
172 String returnPath = mmws[i].getReturnPath();
173
174
175 if (returnPath != null) {
176 session.getProperties().put(RETURN_PATH_KEY, returnPath);
177 log.debug("Return-Path[" + returnPath + "]を設定しました。");
178 }
179
180
181 mimeMessage.setSentDate(new Date());
182
183 mimeMessage.saveChanges();
184
185
186 log.debug("メールを送信します。");
187 transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
188 log.debug("メールを送信しました。");
189
190
191 if (returnPath != null) {
192 session.getProperties().remove(RETURN_PATH_KEY);
193 log.debug("Return-Path設定をクリアしました。");
194 }
195 }
196 } catch (AuthenticationFailedException ex) {
197 log.error("SMTPサーバ[" + host + "]への接続認証に失敗しました。", ex);
198 throw new MailAuthenticationException(ex);
199 } catch (MessagingException ex) {
200 log.error("メールの送信に失敗しました。", ex);
201 throw new MailSendException("メールの送信に失敗しました。", ex);
202 } finally {
203 if (transport != null && transport.isConnected()) {
204 log.debug("SMTPサーバ[" + host + "]との接続を切断します。");
205 try {
206
207 transport.close();
208 } catch (MessagingException e) {
209 log.error("SMTPサーバ[" + host + "]との接続切断に失敗しました。", e);
210 throw new MailException("SMTPサーバ[" + host + "]との接続切断に失敗しました。");
211 }
212 log.debug("SMTPサーバ[" + host + "]との接続を切断しました。");
213 }
214 }
215 }
216
217 /***
218 * 新しいMimeMessageオブジェクトを生成します。<br>
219 * messageIdプロパティがセットされている場合、OMLMimeMessageのインスタンスを生成します。
220 *
221 * @return 新しいMimeMessageオブジェクト
222 */
223 private MimeMessage createMimeMessage(Session session) {
224 if (messageId == null) {
225 return new MimeMessage(session);
226 }
227 return new OMLMimeMessage(session, messageId);
228 }
229
230 /***
231 * エンコーディングに使用する文字コードを返します。
232 *
233 * @return エンコーディングに使用する文字コード
234 */
235 public String getCharset() {
236 return charset;
237 }
238
239 /***
240 * メールの件名や本文のエンコーディングに使用する文字コードを指定します。
241 * デフォルトは<code>ISO-2022-JP</code>です。
242 * <p>
243 * 日本語環境で利用する場合は通常変更する必要はありません。
244 *
245 * @param charset エンコーディングに使用する文字コード
246 */
247 public void setCharset(String charset) {
248 this.charset = charset;
249 }
250
251 /***
252 * セットされたSMTPサーバのホスト名、またはIPアドレスを返します。
253 *
254 * @return SMTPサーバのホスト名、またはIPアドレス
255 */
256 public String getHost() {
257 return host;
258 }
259
260 /***
261 * SMTPサーバのホスト名、またはIPアドレスをセットします。
262 * デフォルトは localhost です。
263 *
264 * @param host SMTPサーバのホスト名、またはIPアドレス
265 */
266 public void setHost(String host) {
267 this.host = host;
268 }
269
270 /***
271 * @return SMTPサーバ認証パスワード
272 */
273 public String getPassword() {
274 return password;
275 }
276
277 /***
278 * SMTPサーバの接続認証が必要な場合にパスワードをセットします。
279 *
280 * @param password SMTPサーバ認証パスワード
281 */
282 public void setPassword(String password) {
283 this.password = password;
284 }
285
286 /***
287 * @return SMTPサーバのポート番号
288 */
289 public int getPort() {
290 return port;
291 }
292
293 /***
294 * SMTPサーバのポート番号をセットします。
295 *
296 * @param port SMTPサーバのポート番号
297 */
298 public void setPort(int port) {
299 this.port = port;
300 }
301
302 /***
303 * @return Returns the protocol.
304 */
305 public String getProtocol() {
306 return protocol;
307 }
308
309 /***
310 * @param protocol The protocol to set.
311 */
312 public void setProtocol(String protocol) {
313 this.protocol = protocol;
314 }
315
316 /***
317 * @return Return-Pathアドレス
318 */
319 public String getReturnPath() {
320 return returnPath;
321 }
322
323 /***
324 * Return-Pathアドレスをセットします。
325 * <p>
326 * 送信するMailインスタンスに指定されたFromアドレス以外のアドレスをReturn-Pathとしたい場合に使用します。
327 * ここでセットされたReturn-Pathより、MailインスタンスにセットされたReturn-Pathが優先されます。
328 *
329 * @param returnPath Return-Pathアドレス
330 */
331 public void setReturnPath(String returnPath) {
332 this.returnPath = returnPath;
333 }
334
335 /***
336 * @return SMTPサーバ認証ユーザ名
337 */
338 public String getUsername() {
339 return username;
340 }
341
342 /***
343 * SMTPサーバの接続認証が必要な場合にユーザ名をセットします。
344 *
345 * @param username SMTPサーバ認証ユーザ名
346 */
347 public void setUsername(String username) {
348 this.username = username;
349 }
350
351 /***
352 * SMTPサーバとの接続タイムアウトをセットします。
353 * 単位はミリ秒。デフォルトは5,000ミリ秒(5秒)です。
354 * <p>
355 * -1を指定すると無限大になりますが、お薦めしません。
356 *
357 * TODO: タイムアウトになると・・・
358 *
359 * @since 1.1.4
360 * @param connectionTimeout SMTPサーバとの接続タイムアウト
361 */
362 public void setConnectionTimeout(int connectionTimeout) {
363 this.connectionTimeout = connectionTimeout;
364 }
365
366 /***
367 * SMTPサーバへの送受信時のタイムアウトをセットします。
368 * 単位はミリ秒。デフォルトは5,000ミリ秒(5秒)です。
369 * <p>
370 * -1を指定すると無限大になりますが、お薦めしません。
371 *
372 * @since 1.1.4
373 * @param readTimeout SMTPサーバへの送受信時のタイムアウト
374 */
375 public void setReadTimeout(int readTimeout) {
376 this.readTimeout = readTimeout;
377 }
378
379 /***
380 * 生成されるMimeMessageに付けられるMessage-Idヘッダのドメイン部分を指定します。<br>
381 * 指定されない場合(nullや空文字列の場合)は、JavaMailがMessage-Idヘッダを生成します。
382 * JavaMailが生成する「JavaMail.実行ユーザ名@ホスト名」のMessage-Idを避けたい場合に、このメソッドを使用します。
383 * <p>
384 * messageIdプロパティがセットされている場合、Mailから生成されるMimeMessageのMessage-Idには
385 * <code>タイムスタンプ + ランダムに生成される16桁の数値 + ここでセットされた値</code>
386 * が使用されます。
387 * <p>
388 * 生成されるMessage-Idの例。 (実際の数値部分は送信メール毎に変わります)<ul>
389 * <li>messageIdに'example.com'を指定した場合・・・1095714924963.5619528074501343@example.com</li>
390 * <li>messageIdに'@example.com'を指定した場合・・・1095714924963.5619528074501343@example.com (上と同じ)</li>
391 * <li>messageIdに'OML@example.com'を指定した場合・・・1095714924963.5619528074501343.OML@example.com</li>
392 * <li>messageIdに'.OML@example.com'を指定した場合・・・1095714924963.5619528074501343.OML@example.com (上と同じ)</li>
393 * </ul>
394 * <p>
395 * <strong>注:</strong> このMessage-Idは<code>send(Mail)</code>か<code>send(Mail[])</code>メソッドが呼びだれた時にのみ有効です。MimeMessageを直接送信する場合には適用されません。
396 *
397 * @param messageId メールに付けられるMessage-Idヘッダのドメイン部分
398 * @throws IllegalArgumentException @を複数含んだ文字列を指定した場合
399 */
400 public void setMessageId(String messageId) {
401 if (messageId == null || messageId.length() < 1) {
402 return;
403 }
404
405 String[] parts = messageId.split("@");
406 if (parts.length > 2) {
407 throw new IllegalArgumentException("messageIdプロパティに'@'を複数含むことはできません。[" + messageId
408 + "]");
409 }
410
411 this.messageId = messageId;
412 }
413
414 /***
415 * MimeMessageインスタンスと、そのメールに対応するReturn-Pathをラップするクラス。
416 *
417 * @author Tomohiro Otsuka
418 * @version $Id: SendMailImpl.java,v 1.8 2004/12/20 03:42:52 otsuka Exp $
419 */
420 private static class MimeMessageWrapper {
421
422 private MimeMessage mimeMessage;
423
424 private String returnPath;
425
426 public MimeMessageWrapper(MimeMessage mimeMessage, String returnPath) {
427 this.mimeMessage = mimeMessage;
428 this.returnPath = returnPath;
429 }
430
431 public MimeMessage getMimeMessage() {
432 return mimeMessage;
433 }
434
435 public String getReturnPath() {
436 return returnPath;
437 }
438
439 }
440
441 }