View Javadoc

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 			// MimeMessageを生成
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 			// Return-Pathを取得
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 		// mail.smtp.authプロパティの設定
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 			// SMTPサーバに接続
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 				// Return-Pathをセット
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 				// Return-Pathを解除
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 					// SMTPサーバとの接続を切断
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 }