/* $Id: MailMessageAdapter.java,v 1.3 2007/11/28 07:09:06 nito Exp $
 *
 * Copyright (c)ARGO 21, Corporation. 2005, 2006.  All rights reserved.
 * 
 * This file is part of Nautica Workflow Core.
 * 
 *  Nautica Workflow Core is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2.1 of the License, or
 *  (at your option) any later version.
 * 
 *  Nautica Workflow Core is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with Nautica Workflow Core; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *  
 */
package jp.co.argo21.nautica.workflow.ta;

import java.io.File;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;

import jp.co.argo21.nautica.workflow.dataaccess.AppExecutionBean;
import jp.co.argo21.nautica.workflow.engine.DataAccessManager;
import jp.co.argo21.nautica.workflow.jms.WorkflowMessage;
import jp.co.argo21.nautica.workflow.util.StringManager;

/**
 * メール送信実行用クラス
 * 
 * @author kiyoda(Argo 21, Corp.)
 * @version $Revision: 1.3 $
 * @since Nautica Workflow 1.0
 */
public class MailMessageAdapter extends AbstractToolAgentMessageAdapter
{
	/* メールヘッダのX-Mailerに設定する値 */
	private static final String MAILER_NAME = "nautica 1.0/MailAgent";

	/**
	 * デフォルトコンストラクタ
	 */
	MailMessageAdapter()
	{
		super();
	}

	@Override
	protected WorkflowMessage getMessage(HashMap<String, Serializable> map)
	{
		return new MailExecutionInfo(map);
	}

	/**
	 * メールを送信する。
	 * 
	 * 設定情報とアプリケーション実行情報に従ってメールを送信する。 実行状況に合わせて、随時、アプリケーション実行テーブル(APPEXEC)を
	 * 更新する。
	 * 
	 * @param mid
	 *            メッセージID
	 * @param message
	 *            メッセージ
	 * @param isRedelivered
	 *            再配信フラグ
	 * @see jp.co.argo21.nautica.workflow.jms.WorkflowMessageAdapter#onMessage(java.lang.String,
	 *      jp.co.argo21.nautica.workflow.jms.WorkflowMessage, boolean)
	 */
	public void onMessage(String mid, WorkflowMessage message,
			boolean isRedelivered)
	{
		if (!MailExecutionInfo.class.isAssignableFrom(message.getClass())) {
			return;
		}
		MailExecutionInfo info = (MailExecutionInfo)message;

		if (isRedelivered) {
			//再配信されたメッセージの場合
			ExecutionCounter.update(mid);
		} else {
			//初めて受信したメッセージの場合
			ExecutionCounter.set(mid, info.getRetryCount());
		}
		
		AppExecutionBean bean = null;
		AbstractToolAgent agent = null;
		ApplicationState astate = ApplicationState.NOT_STARTED;
		try {
			// メール送信の設定情報を取得する
			MailConfig config = MailConfigFactory.getConfig();
			agent = (AbstractToolAgent)(new MailToolAgentFactory())
			        .getToolAgent();

			DataAccessManager.begin(false);
			bean = getApplicationState(info);
			astate = ApplicationState.RUNNING;
			updateState(info, bean.getAppState(), astate);

			log.info(StringManager.get("I2009"));
			// メールを送信する
			sendMail(config, info);
			// ログ出力
			log.info(StringManager.get("I2010"));

			// アプリケーションの実行結果に応じて、
			// アプリケーション実行テーブルと作業項目を更新する。
			finalizeApplication(info, astate, 0, agent);

			ExecutionCounter.clear(mid);
			
			DataAccessManager.commit();
		} catch (Exception ex) {
			// メール送信に失敗した場合
			int count = ExecutionCounter.get(mid);

			// ログ出力
			String errMsg = StringManager.get("E2011")
				+ ": (Retry Count = " + (count - 1);
			log.error(errMsg, ex);

			if (count > 1) {
				// まだリトライ回数が残っている場合
				try {
					DataAccessManager.rollback();
				} catch (Exception ex2) {
					/* Ignore */
				}
				throw new RuntimeException(ex);
			} else {
				try {
					// アプリケーション実行テーブル(APPEXEC)テーブルの
					// アプリケーションの状態を変更する
					finalizeApplication(info, astate, -1, agent);

					// コミット
					DataAccessManager.commit();
				} catch (Exception ex2) {
					// ログ出力
					String errMsg2 = StringManager.get("E2012");
					log.error(errMsg2, ex);
				}	
			}
		} finally {
			try {
				DataAccessManager.close();
			} catch (Exception ex2) {
				/* Ignore */
			}
		}
	}

	/**
	 * メール送信を行う
	 * 
	 * 
	 * @param config
	 * @param info
	 * @throws AddressException
	 * @throws UnsupportedEncodingException
	 * @throws MessagingException
	 */
	private void sendMail(MailConfig config, MailExecutionInfo info)
	        throws AddressException, UnsupportedEncodingException,
	        MessagingException
	{

		String appName = info.getAppName();
		String encode = config.getSenderEncode(appName);

		// アプリケーション実行情報のパラメータからメール送信の情報を解釈する
		info.parse(encode);

		Properties props = System.getProperties();
		// 送信サーバのアドレスを指定
		props.put("mail." + config.getSenderProtocol(appName) + ".host", config
		        .getSenderHostAddress(appName));
		props.put("mail.host", config.getSenderHostAddress(appName));

		// セッションを取得する
		Session session = null;
		if (config.getSenderHostUser(appName) != null) {
			// SMTP認証を行う場合
			Authenticator auth = new MailSendAuthenticator(config
			        .getSenderHostUser(appName), config
			        .getSenderHostPassword(appName));
			session = Session.getInstance(props, auth);
		} else if (config.getAuthType(appName).equals("pop-over-smtp")) {
			// POP over SMTP で認証を行う場合
			session = Session.getInstance(props, null);
			Store store = session.getStore(config.getReceiverProtocol(appName));
			store.connect(config.getReceiverHostAddress(appName), config
			        .getReceiverHostPort(appName), config
			        .getReceiverHostUser(appName), config
			        .getReceiverHostPassword(appName));
		} else {
			session = Session.getInstance(props, null);
		}
		MimeMessage mimeMessage = new MimeMessage(session);

		// メールヘッダー部の設定
		// X-Mailerを設定
		mimeMessage.setHeader("X-Mailer", MAILER_NAME);
		// 送信元メールアドレスと送信者名を指定
		mimeMessage.setFrom(info.getFrom());
		// 送信先メールアドレスを指定
		mimeMessage.setRecipients(Message.RecipientType.TO, info.getTo());
		// CCを指定
		mimeMessage.setRecipients(Message.RecipientType.CC, info.getCC());
		// BCCを指定
		mimeMessage.setRecipients(Message.RecipientType.BCC, info.getBCC());
		// メールのタイトルを指定
		if (info.getSubject() == null) {
			mimeMessage.setSubject("No Subject", encode);
		} else {
			mimeMessage.setSubject(info.getSubject(), encode);
		}
		// メールの形式を指定
		mimeMessage
		        .setHeader("Content-Type", config.getSenderMimeType(appName));
		// 送信日付を指定
		mimeMessage.setSentDate(new Date(System.currentTimeMillis()));

		// 添付ファイルが存在する場合は、自動的にマルチパートメールに変更する
		if (info.getAttachmentFiles().length > 0) {
			// マルチパートメッセージ作成
			MimeMultipart content = new MimeMultipart();
			MimeBodyPart text = new MimeBodyPart();
			text.setText(info.getBody(), encode);
			content.addBodyPart(text);
			// 添付ファイル部作成
			String[] attachmentFileNames = info.getAttachmentFiles();
			for (int i = 0; i < attachmentFileNames.length; i++) {
				File attachmentFile = new File(attachmentFileNames[i]);
				MimeBodyPart attachment = new MimeBodyPart();
				attachment.setDataHandler(new DataHandler(new FileDataSource(
				        attachmentFile)));
				attachment.setFileName(MimeUtility.encodeWord(attachmentFile
				        .getName(), encode, null));
				content.addBodyPart(attachment);
			}
			// メールにマルチパートメッセージをセット
			mimeMessage.setContent(content);
		} else {
			// メールの内容を指定
			mimeMessage.setText(info.getBody(), encode);
		}

		// 送信します
		Transport.send(mimeMessage);
	}

	/**
	 * ネットワーク接続に必要な認証を取得するためのオブジェクト
	 * 
	 * @author kiyoda(Argo 21, Corp.)
	 * @version $Revision: 1.3 $
	 * @since Nautica Workflow 1.0
	 */
	class MailSendAuthenticator extends Authenticator
	{

		/* ユーザ名 */
		String user = null;

		/* パスワード */
		String password = null;

		/**
		 * コンストラクタ
		 * 
		 * @param user
		 *            ユーザ名
		 * @param password
		 *            パスワード
		 */
		MailSendAuthenticator(String user, String password)
		{
			this.user = user;
			this.password = password;
		}

		/**
		 * パスワード認証が必要な場合に呼び出されます。
		 * 
		 * 
		 * @return PasswordAuthentication。
		 * @see javax.mail.Authenticator#getPasswordAuthentication()
		 */
		protected PasswordAuthentication getPasswordAuthentication()
		{

			return new PasswordAuthentication(this.user, this.password);
		}
	}
}
