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.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 		// グローバルReturn-Pathの設定
105 		putOnReturnPath(this.returnPath);
106 
107 		try {
108 			// SMTPサーバに接続
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 		//	mail.smtp.authプロパティの設定
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 				// SMTPサーバとの接続を切断
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 				// グローバルReturn-Pathの解除
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 		// MimeMessageの生成
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 }