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