View Javadoc

    1   package com.ozacc.mail.mock;
    2
    3   import java.io.UnsupportedEncodingException;
    4   import java.util.ArrayList;
    5   import java.util.List;
    6   import java.util.Properties;
    7
    8   import javax.mail.MessagingException;
    9   import javax.mail.Session;
    10  import javax.mail.internet.InternetAddress;
    11  import javax.mail.internet.MimeMessage;
    12
    13  import com.ozacc.mail.Mail;
    14  import com.ozacc.mail.MailBuildException;
    15  import com.ozacc.mail.MailException;
    16  import com.ozacc.mail.SendMail;
    17  import com.ozacc.mail.impl.MimeMessageBuilder;
    18
    19  /***
    20   * SendMailImplクラスのMock。<br>
    21   * 実存するSMTPサーバを設定しても、実際には送信されません。
    22   * デバッグモードを有効にすると、メールを送信するタイミングでコンソールに送信メール内容が出力されます。
    23   * <p>
    24   * Mailインスタンスを addExpectedMail() にセットし verify() メソッドを実行すると、send() されたMailインスタンスと全てのプロパティ(XHeader、添付ファイルを除く)が一致しなければAssertionFailedExceptionがスローされます。
    25   * <p>
    26   * 例えば、send() されたMailインスタンスのFromアドレスと件名だけチェックし、その他のプロパティはチェックしたくない場合は、MockMailインスタンスを使用します。
    27   * <pre>Mail sentMail = new Mail();
    28   *sentMail.setFrom("from@example.com");
    29   *sentMail.setSubject("件名");
    30   *sentMail.addTo("to@example.com");
    31   *sentMail.setText("動的生成される本文");
    32   *
    33   *Mail expectedMail = new Mail();
    34   *expectedMail.setFrom("from@example.com");
    35   *expectedMail.setSubject("件名");
    36   *
    37   *MockMail mockMail = new MockMail();
    38   *mockMail.setFrom("from@example.com");
    39   *mockMail.setSubject("件名");
    40   *
    41   *MockSendMail sendMail = new MockSendMail();
    42   *sendMail.addExpectedMail(expectedMail);
    43   *sendMail.send(sentMail);
    44   *sendMail.verify(); // 失敗 AssertionFailedException
    45   *
    46   *sendMail = new MockSendMail();
    47   *sendMail.addExpectedMail(mockMail);
    48   *sendMail.send(sentMail);
    49   *sendMail.verify(); // 成功</pre>
    50   * <p>
    51   * <strong>注:</strong> 添付ファイルは比較対象になりません。
    52   *
    53   * @since 1.0
    54   * @author Tomohiro Otsuka
    55   * @version $Id: MockSendMail.java,v 1.10.2.1 2004/11/25 08:01:47 otsuka Exp $
    56   */
    57  public class MockSendMail implements SendMail {
    58
    59  	/*** デフォルトのプロトコル。「smtp」 */
    60  	public static final String DEFAULT_PROTOCOL = "smtp";
    61
    62  	/*** デフォルトのポート。「-1」 */
    63  	public static final int DEFAULT_PORT = -1;
    64
    65  	/*** デフォルトのSMTPサーバ。「localhost」 */
    66  	public static final String DEFAULT_HOST = "localhost";
    67
    68  	/*** ISO-2022-JP */
    69  	public static final String JIS_CHARSET = "ISO-2022-JP";
    70
    71  	private static final String RETURN_PATH_KEY = "mail.smtp.from";
    72
    73  	private String protocol = DEFAULT_PROTOCOL;
    74
    75  	private String host = DEFAULT_HOST;
    76
    77  	private int port = DEFAULT_PORT;
    78
    79  	private String username;
    80
    81  	private String password;
    82
    83  	private String charset = JIS_CHARSET;
    84
    85  	private String returnPath;
    86
    87  	private List sentMails;
    88
    89  	private List mimeMessages;
    90
    91  	private List expectedMails;
    92
    93  	private boolean debug;
    94
    95  	/***
    96  	 * コンストラクタ。
    97  	 */
    98  	public MockSendMail() {
    99  		super();
    100 		initialize();
    101 	}
    102
    103 	/***
    104 	 * MockSendMailインスタンスを初期化します。
    105 	 */
    106 	public void initialize() {
    107 		sentMails = new ArrayList();
    108 		expectedMails = new ArrayList();
    109 		mimeMessages = new ArrayList();
    110 	}
    111
    112 	/***
    113 	 * 送信されたメールのMimeMessageインスタンスを返します。
    114 	 * 送信順の配列です。
    115 	 *
    116 	 * @return 送信メールのMimeMessageインスタンス配列
    117 	 */
    118 	public MimeMessage[] getMimeMessages() {
    119 		return (MimeMessage[])mimeMessages.toArray(new MimeMessage[mimeMessages.size()]);
    120 	}
    121
    122 	/***
    123 	 * 送信されたMailインスタンスを返します。送信順の配列です。
    124 	 *
    125 	 * @return 送信メールのMailインスタンス配列
    126 	 */
    127 	public Mail[] getSentMails() {
    128 		return (Mail[])sentMails.toArray(new Mail[sentMails.size()]);
    129 	}
    130
    131 	/***
    132 	 * デバッグモードが有効になっているか判定します。
    133 	 *
    134 	 * @return Returns 有効になっている場合 true
    135 	 */
    136 	public boolean isDebug() {
    137 		return debug;
    138 	}
    139
    140 	/***
    141 	 * デバッグモード(コンソールにログを出力)を有効にします。
    142 	 * デフォルトは無効です。
    143 	 *
    144 	 * @param debug デバッグモードを有効にする場合 true
    145 	 */
    146 	public void setDebug(boolean debug) {
    147 		this.debug = debug;
    148 	}
    149
    150 	/***
    151 	 * デバッグモードが有効のとき、指定されたメッセージをコンソールに出力します。
    152 	 *
    153 	 * @param message コンソール出力するメッセージ
    154 	 */
    155 	private void debug(String message) {
    156 		if (debug) {
    157 			System.out.println("[" + Thread.currentThread().getName() + "] DEBUG "
    158 					+ getClass().getName() + " - " + message);
    159 		}
    160 	}
    161
    162 	/***
    163 	 *
    164 	 * @param expectedMail
    165 	 */
    166 	public void addExpectedMail(Mail expectedMail) {
    167 		expectedMails.add(expectedMail);
    168 	}
    169
    170 	/***
    171 	 *
    172 	 * @param expectedMails
    173 	 */
    174 	public void addExpectedMail(Mail[] expectedMails) {
    175 		for (int i = 0; i < expectedMails.length; i++) {
    176 			addExpectedMail(expectedMails[i]);
    177 		}
    178 	}
    179
    180 	/***
    181 	 *
    182 	 * @throws AssertionFailedException
    183 	 */
    184 	public void verify() throws AssertionFailedException {
    185 		debug("======================================================");
    186 		debug("                      verify()                        ");
    187 		debug("======================================================");
    188
    189 		// メールの数を比較
    190 		int numOfExpectedMails = expectedMails.size();
    191 		int numOfSentMails = sentMails.size();
    192 		if (numOfExpectedMails != numOfSentMails) {
    193 			throw new AssertionFailedException("期待メール数<" + numOfExpectedMails + ">と送信メール数<"
    194 					+ numOfSentMails + ">が一致しませんでした。");
    195 		}
    196
    197 		debug("期待メール数と送信メール数は一致しました。[" + numOfExpectedMails + "通]");
    198
    199 		// メール内容を比較
    200 		for (int i = 0; i < numOfExpectedMails; i++) {
    201 			Mail expected = (Mail)expectedMails.get(i);
    202 			Mail sent = (Mail)sentMails.get(i);
    203 			debug((i + 1) + "通目のチェックを開始します。("
    204 					+ ((expected instanceof MockMail) ? "MockMail" : "Mail") + " - Mail)");
    205 			checkEquality(expected, sent, i + 1);
    206 			debug((i + 1) + "通目の期待メールと送信メール内容は一致しました。");
    207 		}
    208
    209 		debug("verifyプロセスは全て成功しました。");
    210 		debug("======================================================");
    211 	}
    212
    213 	/***
    214 	 * @param expected
    215 	 * @param sent
    216 	 * @throws AssertionFailedException
    217 	 */
    218 	public void checkEquality(Mail expected, Mail sent, int num) throws AssertionFailedException {
    219 		boolean mockMode = (expected instanceof MockMail);
    220
    221 		// マルチパートメールの場合
    222 		if (expected.isMultipartMail()) {
    223
    224 			// HTML
    225 			if (!mockMode) {
    226 				if ((expected.getHtmlText() == null && sent.getHtmlText() != null)
    227 						|| (expected.getHtmlText() != null && sent.getHtmlText() == null)
    228 						|| (!expected.getHtmlText().equals(sent.getHtmlText()))) {
    229 					throwExceptioWithMessage("HTML本文", expected.getHtmlText(), sent.getHtmlText(),
    230 							num);
    231 				}
    232 			} else if (mockMode && expected.getHtmlText() != null) {
    233 				if (!expected.getHtmlText().equals(sent.getHtmlText())) {
    234 					throwExceptioWithMessage("HTML本文", expected.getHtmlText(), sent.getHtmlText(),
    235 							num);
    236 				}
    237 			}
    238 		}
    239
    240 		// Return-Path
    241 		if (!mockMode || (mockMode && expected.getReturnPath() != null)) {
    242 			if (expected.getReturnPath() != null && sent.getReturnPath() != null) {
    243 				if (!expected.getReturnPath().equals(sent.getReturnPath())) {
    244 					throwExceptioWithMessage("Return-Pathアドレス", expected.getReturnPath()
    245 							.toUnicodeString(), sent.getReturnPath().toUnicodeString(), num);
    246 				}
    247 			} else if ((expected.getReturnPath() != null && sent.getReturnPath() == null)
    248 					|| (expected.getReturnPath() == null && sent.getReturnPath() != null)) {
    249 				throw new AssertionFailedException();
    250 			}
    251 		}
    252
    253 		// From
    254 		if (!mockMode || (mockMode && expected.getFrom() != null)) {
    255 			if (expected.getFrom() != null && sent.getFrom() != null) {
    256 				if (!EqualityCheck.equals(expected.getFrom(), sent.getFrom())) {
    257 					throwExceptioWithMessage("Fromアドレス", expected.getFrom().toUnicodeString(), sent
    258 							.getFrom().toUnicodeString(), num);
    259 				}
    260 			} else if ((expected.getFrom() != null && sent.getFrom() == null)
    261 					|| (expected.getFrom() == null && sent.getFrom() != null)) {
    262 				throw new AssertionFailedException();
    263 			}
    264 		}
    265
    266 		// to
    267 		InternetAddress[] expectedAddresses = expected.getTo();
    268 		InternetAddress[] sentAddresses = sent.getTo();
    269 		if (!mockMode || (mockMode && expectedAddresses.length > 0)) {
    270 			if (expectedAddresses.length != sentAddresses.length) {
    271 				throwExceptioWithMessage("Toアドレス数", Integer.toString(expectedAddresses.length),
    272 						Integer.toString(sentAddresses.length), num);
    273 			}
    274 			for (int i = 0; i < expectedAddresses.length; i++) {
    275 				if (!EqualityCheck.equals(expectedAddresses[i], sentAddresses[i])) {
    276 					throwExceptioWithMessage("Toアドレス", expectedAddresses[i].toUnicodeString(),
    277 							sentAddresses[i].toUnicodeString(), num);
    278 				}
    279 			}
    280 		}
    281
    282 		// cc
    283 		expectedAddresses = expected.getCc();
    284 		sentAddresses = sent.getCc();
    285 		if (!mockMode || (mockMode && expectedAddresses.length > 0)) {
    286 			if (expectedAddresses.length != sentAddresses.length) {
    287 				throwExceptioWithMessage("Ccアドレス数", Integer.toString(expectedAddresses.length),
    288 						Integer.toString(sentAddresses.length), num);
    289 			}
    290 			for (int i = 0; i < expectedAddresses.length; i++) {
    291 				if (!EqualityCheck.equals(expectedAddresses[i], sentAddresses[i])) {
    292 					throwExceptioWithMessage("Ccアドレス", expectedAddresses[i].toUnicodeString(),
    293 							sentAddresses[i].toUnicodeString(), num);
    294 				}
    295 			}
    296 		}
    297
    298 		// bcc
    299 		expectedAddresses = expected.getBcc();
    300 		sentAddresses = sent.getBcc();
    301 		if (!mockMode || (mockMode && expectedAddresses.length > 0)) {
    302 			if (expectedAddresses.length != sentAddresses.length) {
    303 				throwExceptioWithMessage("Bccアドレス数", Integer.toString(expectedAddresses.length),
    304 						Integer.toString(sentAddresses.length), num);
    305 			}
    306 			for (int i = 0; i < expectedAddresses.length; i++) {
    307 				if (!EqualityCheck.equals(expectedAddresses[i], sentAddresses[i])) {
    308 					throwExceptioWithMessage("Bccアドレス", expectedAddresses[i].toUnicodeString(),
    309 							sentAddresses[i].toUnicodeString(), num);
    310 				}
    311 			}
    312 		}
    313
    314 		// Reply-To
    315 		if (!mockMode || (mockMode && expected.getReplyTo() != null)) {
    316 			if (expected.getReplyTo() != null && sent.getReplyTo() != null) {
    317 				if (!EqualityCheck.equals(expected.getReplyTo(), sent.getReplyTo())) {
    318 					throwExceptioWithMessage("ReplyToアドレス",
    319 							expected.getReplyTo().toUnicodeString(), sent.getReplyTo()
    320 									.toUnicodeString(), num);
    321 				}
    322 			} else if ((expected.getReplyTo() != null && sent.getReplyTo() == null)
    323 					|| (expected.getReplyTo() == null && sent.getReplyTo() != null)) {
    324 				throw new AssertionFailedException();
    325 			}
    326 		}
    327
    328 		// 件名
    329 		if (!mockMode || (mockMode && expected.getSubject().length() > 0)) {
    330 			if (!expected.getSubject().equals(sent.getSubject())) {
    331 				throwExceptioWithMessage("件名", expected.getSubject(), sent.getSubject(), num);
    332 			}
    333 		}
    334
    335 		// 本文
    336 		if (!mockMode || (mockMode && expected.getText().length() > 0)) {
    337 			if (!expected.getText().equals(sent.getText())) {
    338 				throwExceptioWithMessage("本文", expected.getText(), sent.getText(), num);
    339 			}
    340 		}
    341
    342 	}
    343
    344 	/***
    345 	 * 引数の値を受けてエラーメッセージを生成し、AssertionFailedErrorをスローします。
    346 	 *
    347 	 * @param name 一致しなかった項目名
    348 	 * @param expectedValue 期待値
    349 	 * @param sentValue 実際値
    350 	 * @param num N番目のメール
    351 	 * @throws AssertionFailedException 生成された例外
    352 	 */
    353 	protected void throwExceptioWithMessage(String name, String expectedValue, String sentValue,
    354 											int num) throws AssertionFailedException {
    355 		String message = num + "番目のメッセージで、「" + name + "」が一致しませんでした。期待値='" + expectedValue
    356 				+ "', 送信値='" + sentValue + "'";
    357
    358 		debug(message);
    359 		debug("verifyプロセスは失敗しました。");
    360 		debug("******************************************************");
    361
    362 		throw new AssertionFailedException(message);
    363 	}
    364
    365 	/***
    366 	 * @see com.ozacc.mail.SendMail#send(com.ozacc.mail.Mail)
    367 	 */
    368 	public void send(Mail mail) throws MailException {
    369 		send(new Mail[] { mail });
    370 	}
    371
    372 	/***
    373 	 * @see com.ozacc.mail.SendMail#send(com.ozacc.mail.Mail[])
    374 	 */
    375 	public void send(Mail[] mails) throws MailException {
    376 		debug("SMTPサーバ[" + host + "]に接続するフリ。");
    377 		debug("SMTPサーバ[" + host + "]に接続したフリ。");
    378
    379 		Session session = Session.getInstance(new Properties());
    380 		for (int i = 0; i < mails.length; i++) {
    381
    382 			Mail mail = mails[i];
    383
    384 			// MimeMessageを生成
    385 			MimeMessage message = new MimeMessage(session);
    386 			MimeMessageBuilder builder = new MimeMessageBuilder(message);
    387 			try {
    388 				builder.buildMimeMessage(mail);
    389 			} catch (UnsupportedEncodingException e) {
    390 				throw new MailBuildException("サポートされていない文字コードが指定されました。", e);
    391 			} catch (MessagingException e) {
    392 				throw new MailBuildException("MimeMessageの生成に失敗しました。", e);
    393 			}
    394 			mimeMessages.add(message);
    395
    396 			debug("メールを送信するフリ。");
    397 			sentMails.add(mail);
    398 			debug(mail.toString());
    399 			debug("メールを送信したフリ。");
    400 		}
    401
    402 		debug("SMTPサーバ[" + host + "]との接続を切断するフリ。");
    403 		debug("SMTPサーバ[" + host + "]との接続を切断したフリ。");
    404 	}
    405
    406 	/***
    407 	 * @see com.ozacc.mail.SendMail#send(javax.mail.internet.MimeMessage)
    408 	 */
    409 	public void send(MimeMessage mimeMessage) throws MailException {
    410 		throw new UnsupportedOperationException("申し訳ございません。MockSendMailでは、このメソッドをサポートしていません。");
    411 	}
    412
    413 	/***
    414 	 * @see com.ozacc.mail.SendMail#send(javax.mail.internet.MimeMessage[])
    415 	 */
    416 	public void send(MimeMessage[] mimeMessages) throws MailException {
    417 		throw new UnsupportedOperationException("申し訳ございません。MockSendMailでは、このメソッドをサポートしていません。");
    418 	}
    419
    420 	/***
    421 	 * エンコーディングに使用する文字コードを返します。
    422 	 *
    423 	 * @return エンコーディングに使用する文字コード
    424 	 */
    425 	public String getCharset() {
    426 		return charset;
    427 	}
    428
    429 	/***
    430 	 * メールの件名や本文のエンコーディングに使用する文字コードを指定します。
    431 	 * デフォルトは ISO-2022-JP です。
    432 	 *
    433 	 * @param charset エンコーディングに使用する文字コード
    434 	 */
    435 	public void setCharset(String charset) {
    436 		this.charset = charset;
    437 	}
    438
    439 	/***
    440 	 * セットされたSMTPサーバのホスト名、またはIPアドレスを返します。
    441 	 *
    442 	 * @return SMTPサーバのホスト名、またはIPアドレス
    443 	 */
    444 	public String getHost() {
    445 		return host;
    446 	}
    447
    448 	/***
    449 	 * SMTPサーバのホスト名、またはIPアドレスをセットします。
    450 	 * デフォルトは localhost です。
    451 	 *
    452 	 * @param host SMTPサーバのホスト名、またはIPアドレス
    453 	 */
    454 	public void setHost(String host) {
    455 		this.host = host;
    456 	}
    457
    458 	/***
    459 	 * @return SMTPサーバ認証パスワード
    460 	 */
    461 	public String getPassword() {
    462 		return password;
    463 	}
    464
    465 	/***
    466 	 * SMTPサーバの接続認証が必要な場合にパスワードをセットします。
    467 	 *
    468 	 * @param password SMTPサーバ認証パスワード
    469 	 */
    470 	public void setPassword(String password) {
    471 		this.password = password;
    472 	}
    473
    474 	/***
    475 	 * @return SMTPサーバのポート番号
    476 	 */
    477 	public int getPort() {
    478 		return port;
    479 	}
    480
    481 	/***
    482 	 * SMTPサーバのポート番号をセットします。
    483 	 *
    484 	 * @param port SMTPサーバのポート番号
    485 	 */
    486 	public void setPort(int port) {
    487 		this.port = port;
    488 	}
    489
    490 	/***
    491 	 * @return Returns the protocol.
    492 	 */
    493 	public String getProtocol() {
    494 		return protocol;
    495 	}
    496
    497 	/***
    498 	 * @param protocol The protocol to set.
    499 	 */
    500 	public void setProtocol(String protocol) {
    501 		this.protocol = protocol;
    502 	}
    503
    504 	/***
    505 	 * @return Return-Pathアドレス
    506 	 */
    507 	public String getReturnPath() {
    508 		return returnPath;
    509 	}
    510
    511 	/***
    512 	 * Return-Pathアドレスをセットします。
    513 	 *
    514 	 * @param returnPath Return-Pathアドレス
    515 	 */
    516 	public void setReturnPath(String returnPath) {
    517 		this.returnPath = returnPath;
    518 	}
    519
    520 	/***
    521 	 * @return SMTPサーバ認証ユーザ名
    522 	 */
    523 	public String getUsername() {
    524 		return username;
    525 	}
    526
    527 	/***
    528 	 * SMTPサーバの接続認証が必要な場合にユーザ名をセットします。
    529 	 *
    530 	 * @param username SMTPサーバ認証ユーザ名
    531 	 */
    532 	public void setUsername(String username) {
    533 		this.username = username;
    534 	}
    535
    536 }