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