package aipo.webservice.soap.axis2;

/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * Copyright (C) 2004-2011 Aimluck,Inc.
 * http://www.aipo.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 */

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.cayenne.access.DataContext;
import org.apache.cayenne.access.Transaction;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.commons.lang.StringUtils;
import org.apache.jetspeed.om.security.JetspeedUser;
import org.apache.jetspeed.om.security.UserNamePrincipal;
import org.apache.jetspeed.services.JetspeedSecurity;
import org.apache.jetspeed.services.JetspeedUserManagement;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.services.security.JetspeedSecurityException;
import org.apache.jetspeed.services.security.UnknownUserException;

import aipo.batch.exec.MailAccountListExportBatchExecuter;
import aipo.batch.exec.UserInfoImportBatchExecuter;
import aipo.webservice.soap.axis2.bean.OutlookAddressBookBean;
import aipo.webservice.soap.axis2.bean.OutlookScheduleBean;
import aipo.webservice.soap.axis2.bean.ReceiveMailViewBean;
import aipo.webservice.soap.axis2.bean.ScheduleViewBean;
import aipo.webservice.soap.axis2.bean.WebServiceResultBean;
import aipo.webservice.util.OutlookTransferUtils;
import aipo.webservice.util.WsScheduleUtils;
import aipo.webservice.util.WsUtils;

import com.aimluck.eip.addressbookuser.util.AddressBookUserUtils;
import com.aimluck.eip.category.util.CommonCategoryUtils;
import com.aimluck.eip.cayenne.om.portlet.AvzTMailSendRecvAcl;
import com.aimluck.eip.cayenne.om.portlet.EipMAddressGroup;
import com.aimluck.eip.cayenne.om.portlet.EipMAddressbook;
import com.aimluck.eip.cayenne.om.portlet.EipMMailAccount;
import com.aimluck.eip.cayenne.om.portlet.EipTAddressbookGroupMap;
import com.aimluck.eip.cayenne.om.portlet.EipTCommonCategory;
import com.aimluck.eip.cayenne.om.portlet.EipTMail;
import com.aimluck.eip.cayenne.om.portlet.EipTSchedule;
import com.aimluck.eip.cayenne.om.portlet.EipTScheduleMap;
import com.aimluck.eip.cayenne.om.security.TurbineUser;
import com.aimluck.eip.mail.ALPop3MailReceiveThread;
import com.aimluck.eip.orm.Database;
import com.aimluck.eip.orm.query.SelectQuery;

/**
 * <HR>
 * リマインダーWebサービスクラス
 * <p>
 * 
 * リマインダー、Outlookデータ移行からの要求に応じてDBアクセス、メールサーバーアクセスを伴う処理を行う。
 * <P>
 * <HR>
 * <P>
 * 
 */
public class ReminderService {

  /** ロガー */
  private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(ReminderService.class.getName());

  /** スケジュール繰り返しパターン：通常 */
  private static final String EIP_T_SCHEDULE_REPEAT_PATTERN_NORMAL = "N";

  /** スケジュール繰り返しパターン：期間 */
  private static final String EIP_T_SCHEDULE_REPEAT_PATTERN_SPAN = "S";

  /** スケジュール繰り返しパターン：日またぎ */
  private static final String EIP_T_SCHEDULE_REPEAT_PATTERN_ACROSS = "Z";

  /** スケジュール編集フラグ：編集可 */
  private static final String EIP_T_SCHEDULE_EDIT_FLAG_EDIT = "T";

  /** 代理送受信制御適用タイプ：ユーザー */
  private static final String AVZ_T_MAIL_SEND_RECV_ACL_TARGET_TYPE_USER = "U";

  /** 代理送受信制御送信受信種別：代理受信 */
  private static final String AVZ_T_MAIL_SEND_RECV_ACL_ACL_TYPE_RECV = "R";

  /** WEB受信メール既読フラグ：未読 */
  private static final String EIP_T_MAIL_READ_FLG_UNREAD = "F";

  /** ユーザー情報スケジュール移行済みフラグ */
  private static final String TUBINE_USER_SCHEDULE_MIGRATE = "S";

  /** ユーザー情報アドレス移行済みフラグ */
  private static final String TUBINE_USER_ADDRESS_MIGRATE = "A";

  /** Outlookスケジュール会議状態：会議でない */
  private static final String OUTLOOK_SCHEDULE_MEETING_STATUS_NON_MEETING = "0";

  /** スケジュールマップ状態：所有者 */
  private static final String EIP_T_SCHEDULE_MAP_STATUS_OWNER = "O";

  /** スケジュールマップ状態：ダミー */
  private static final String EIP_T_SCHEDULE_MAP_STATUS_DUMMY = "D";

  /** スケジュールマップ状態：辞退 */
  private static final String EIP_T_SCHEDULE_MAP_STATUS_VETO = "V";

  /** スケジュールマップ状態：削除 */
  private static final String EIP_T_SCHEDULE_MAP_STATUS_REMOVE = "R";

  /** スケジュールマップ種別：ユーザー */
  private static final String EIP_T_SCHEDULE_MAP_TYPE_USER = "U";

  /** スケジュールマップ必須フラグ：必須 */
  private static final String EIP_T_SCHEDULE_MAP_REQUIERED_REQUIERED = "R";

  /** スケジュールマップ重要フラグ：通常 */
  private static final String EIP_T_SCHEDULE_MAP_PRIORITY_FALSE = "F";

  /** ユーザー情報移行済みフラグ：済 */
  private static final String MIGRATE_FLAG_COMPLETED = "1";

  /** 個人アドレス帳公開フラグ：非公開 */
  private static final String EIP_M_ADDRESSBOOK_PUBLIC_FLAG_CLOSED = "F";

  /** アドレス帳社外グループ公開フラグ：非公開 */
  private static final String EIP_M_ADDRESS_GROUP_PUBLIC_FLAG_CLOSED = "F";

  private static DateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

  /**
   * DB接続
   * 
   * @return int 接続に成功した場合0、失敗した場合-1
   */
  private int openDbConecction() {
    logger.debug("openDbConecction() start.");
    int result = 0;
    DataContext dataContext = null;
    try {
      dataContext = Database.createDataContext(Database.DEFAULT_ORG);
      DataContext.bindThreadDataContext(dataContext);
    } catch (Exception e) {
      logger.error("DB接続に失敗しました。", e);
      result = -1;
    }
    logger.debug("openDbConecction() end.");
    return result;
  }

  /**
   * DB切断
   * 
   * @return int 切断に成功した場合0、失敗した場合-1
   */
  private int closeDbConecction() {
    logger.debug("closeDbConecction() start.");
    int result = 0;
    try {
      Database.tearDown();
    } catch (Exception e) {
      logger.error("DB切断で予期せぬ例外が発生しました。", e);
      result = -1;
    }
    logger.debug("closeDbConecction() end.");
    return result;
  }

  /**
   * 認証サービス
   * <p>
   * ユーザーIDとパスワードでユーザー情報テーブルを検索し、結果を返す。<br>
   * 
   * @param userName
   *            ユーザー名（アカウント）
   * @param password
   *            パスワード
   * @return boolean 認証に成功した場合true、失敗した場合false
   */
  public boolean checkAccount(String userName, String password) {
    logger.debug("checkAccount() start.");
    // ユーザーIDとパスワードより認証を実行する
    JetspeedUser user = null;
    String encryptPassword = null;
    String warnMsg = "ユーザー認証に失敗しました。ユーザー名：[" + userName + "] パスワード：[" + password + "]";
    try {

      // Aipoリマインダーのバージョンが"1.2.0"以上の場合はパスワードを復号する
      password = decryptPassword(userName, password);

      openDbConecction();
      user = JetspeedUserManagement.getUser(new UserNamePrincipal(userName));
      logger.debug("get Jetspeed User.");
      encryptPassword = JetspeedSecurity.encryptPassword(password);
      logger.debug("get password.");
      if (!user.getPassword().equals(encryptPassword)) {
        logger.warn(warnMsg);
        return false;
      }
    } catch (UnknownUserException e) {
      logger.warn(warnMsg, e);
      return false;
    } catch (JetspeedSecurityException e) {
      logger.warn(warnMsg, e);
      return false;
    } catch (Exception e) {
      logger.error("ユーザー認証で予期せぬ例外が発生しました。ユーザー名：[" + userName + "]", e);
      return false;
    } finally {
      closeDbConecction();
    }
    logger.debug("checkAccount() end.");
    return true;
  }

  /**
   * スケジュール取得サービス
   * <p>
   * 指定した時間内で最新のスケジュール情報を取得して返す。<br>
   * 対象期間は「開始時刻-通知時間（分前）≦処理時刻≦開始時刻」とする。<br>
   * 
   * @param demandDateTime
   *            処理日時 形式：yyyy/MM/dd HH:mm:ss（24時間表記）
   * @param noticeTiming
   *            スケジュール通知間隔 形式：分（開始n分前以降が通知対象）
   * @param userName
   *            ユーザー名
   * @param password
   *            パスワード
   * @return ScheduleViewBean[] 開始時刻で降順ソートされたスケジュール一覧（配列）
   */
  public ScheduleViewBean[] getTargetNewSheduleList(String demandDateTime, String noticeTiming, String userName, String password) {
    logger.info("【開始】スケジュール取得サービス ユーザー:" + userName);
    ScheduleViewBean[] result = null;
    JetspeedUser user = null;

    try {
      // DB接続
      openDbConecction();

      // ユーザー情報を取得
      // 認証成功の場合、ユーザー情報を取得
      user = JetspeedUserManagement.getUser(new UserNamePrincipal(userName));
      if (user == null) {
        // 認証失敗の場合、0件のスケジュールリストを返す
        return new ScheduleViewBean[0];
      }
      // 処理日時を秒なしに変換
      Date demandDateTimeNonSecond = WsUtils.getSecondDeletedDate(WsUtils.getDateFromYMDHHMISE(demandDateTime));

      // --------------------------------
      // DBより該当スケジュール取得
      // --------------------------------
      SelectQuery<EipTScheduleMap> query = Database.query(EipTScheduleMap.class);
      // 検索条件：ユーザーIDと一致
      Expression exp1 = ExpressionFactory.matchExp(EipTScheduleMap.USER_ID_PROPERTY, Integer.valueOf(user.getUserId()));
      // 検索条件：繰り返し条件≠期間
      Expression exp2 =
        ExpressionFactory.noMatchExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.REPEAT_PATTERN_PROPERTY, EIP_T_SCHEDULE_REPEAT_PATTERN_SPAN);
      // 検索条件：状態≠辞退
      Expression exp3 = ExpressionFactory.noMatchExp(EipTScheduleMap.STATUS_PROPERTY, EIP_T_SCHEDULE_MAP_STATUS_VETO);
      // 検索条件：状態≠削除
      Expression exp4 = ExpressionFactory.noMatchExp(EipTScheduleMap.STATUS_PROPERTY, EIP_T_SCHEDULE_MAP_STATUS_REMOVE);

      query.setQualifier(exp1.andExp(exp2).andExp(exp3).andExp(exp4));

      // 検索条件A：（通常または日またぎ）かつ開始日時-スケジュール通知時間≦処理日時≦開始日時

      // 便宜上、式を変換して処理日時+スケジュール通知時間と比較
      Calendar demandDateAndNoticeTiming = Calendar.getInstance();
      demandDateAndNoticeTiming.setTime(demandDateTimeNonSecond);
      demandDateAndNoticeTiming.add(Calendar.MINUTE, Integer.parseInt(noticeTiming));

      // 検索条件A1：開始時間≦処理日時+スケジュール通知時間
      Expression expA1 =
        ExpressionFactory.lessOrEqualExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.START_DATE_PROPERTY, demandDateAndNoticeTiming.getTime());

      // 検索条件A2：開始日時≧処理日時
      Expression expA2 =
        ExpressionFactory.greaterOrEqualExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.START_DATE_PROPERTY, demandDateTimeNonSecond);

      // 検索条件A3：繰り返しパターン=通常
      Expression expA3 =
        ExpressionFactory.matchExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.REPEAT_PATTERN_PROPERTY, EIP_T_SCHEDULE_REPEAT_PATTERN_NORMAL);

      // 検索条件A3：繰り返しパターン=日またぎ
      Expression expA4 =
        ExpressionFactory.matchExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.REPEAT_PATTERN_PROPERTY, EIP_T_SCHEDULE_REPEAT_PATTERN_ACROSS);

      Expression expA = (expA3.orExp(expA4)).andExp(expA1.andExp(expA2));

      // 検索条件B：繰り返し条件≠通常かつ繰り返し条件≠日またぎ かつ 終了時間＜処理日時

      // 検索条件B1：繰り返し条件≠通常
      Expression expB1 =
        ExpressionFactory.noMatchExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.REPEAT_PATTERN_PROPERTY, EIP_T_SCHEDULE_REPEAT_PATTERN_NORMAL);

      // 検索条件B2：繰り返し条件≠日またぎ
      Expression expB2 =
        ExpressionFactory.noMatchExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.REPEAT_PATTERN_PROPERTY, EIP_T_SCHEDULE_REPEAT_PATTERN_ACROSS);

      // 検索条件B3：終了時間＞処理日時
      Expression expB3 = ExpressionFactory.greaterExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.END_DATE_PROPERTY, demandDateTimeNonSecond);

      Expression expB = expB1.andExp(expB2).andExp(expB3);

      // 検索条件C：ダミースケジュール かつ 処理日時の日≦開始日≦（処理日時+スケジュール通知時間）の日
      // 処理日時の日(便宜上00:00にする)
      Calendar checkStartDate = Calendar.getInstance();
      checkStartDate.setTime(demandDateTimeNonSecond);
      checkStartDate.set(Calendar.HOUR_OF_DAY, 0);
      checkStartDate.set(Calendar.MINUTE, 0);
      checkStartDate.set(Calendar.SECOND, 0);
      checkStartDate.set(Calendar.MILLISECOND, 0);

      // （処理日時+スケジュール通知時間）の日(便宜上23:59にする)
      Calendar checkEndDate = Calendar.getInstance();
      checkEndDate.setTime(demandDateTimeNonSecond);
      checkEndDate.set(Calendar.HOUR_OF_DAY, 23);
      checkEndDate.set(Calendar.MINUTE, 59);
      checkEndDate.set(Calendar.SECOND, 59);
      checkEndDate.set(Calendar.MILLISECOND, 999);

      // 検索条件C1：ダミースケジュール
      Expression expC1 = ExpressionFactory.matchExp(EipTScheduleMap.STATUS_PROPERTY, EIP_T_SCHEDULE_MAP_STATUS_DUMMY);

      // 検索条件C2：開始日≧処理日時の日
      Expression expC2 =
        ExpressionFactory.greaterOrEqualExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.START_DATE_PROPERTY, checkStartDate.getTime());

      // 検索条件C3：開始日≦（処理日時+スケジュール通知時間）の日
      Expression expC3 =
        ExpressionFactory.lessOrEqualExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.START_DATE_PROPERTY, checkEndDate.getTime());

      Expression expC = expC1.andExp(expC2).andExp(expC3);

      // 検索条件：検索条件A or 検索条件B or 検索条件C
      query.andQualifier(expA.orExp(expB).orExp(expC));

      logger.debug("search schedule start.");

      // 検索実行
      List<EipTScheduleMap> resultList = query.fetchList();

      logger.debug("search schedule end.");

      // --------------------------------
      // 繰り返しスケジュールを整理
      // --------------------------------
      logger.debug("check schedule start.");

      // 削除対象リスト
      List<EipTScheduleMap> delList = new ArrayList<EipTScheduleMap>(0);

      // 検索結果より「子にダミースケジュールをもつ」繰り返しスケジュールを削除
      for (EipTScheduleMap record : resultList) {
        EipTSchedule schedule = record.getEipTSchedule();
        for (EipTScheduleMap record2 : resultList) {
          EipTSchedule schedule2 = (record2.getEipTSchedule());
          if (!schedule.getRepeatPattern().equals(EIP_T_SCHEDULE_REPEAT_PATTERN_NORMAL) // 単発スケジュール
            && !schedule.getRepeatPattern().equals(EIP_T_SCHEDULE_REPEAT_PATTERN_ACROSS) // 日またぎスケジュール
            && record2.getStatus().equals(EIP_T_SCHEDULE_MAP_STATUS_DUMMY) // 状態がD(ダミー)
            && schedule.getScheduleId().equals(schedule2.getParentId())) { // 親IDが一致
            delList.add(record);
            delList.add(record2);
          }
        }
      }
      resultList.removeAll(delList);

      // 期間外の繰り返しスケジュールを削除
      delList.clear();
      for (EipTScheduleMap record : resultList) {
        EipTSchedule schedule = record.getEipTSchedule();
        if (!WsScheduleUtils.isReminderRange(
          demandDateTimeNonSecond,
          schedule.getRepeatPattern(),
          Integer.parseInt(noticeTiming),
          schedule.getStartDate(),
          schedule.getEndDate())) {
          delList.add(record);
        }
      }
      resultList.removeAll(delList);

      // add start 運用フェーズ課題・障害台帳No.１４２
      // ダミーレコードを除外する。
      delList.clear();
      for (EipTScheduleMap record : resultList) {
        if (record.getStatus().equals(EIP_T_SCHEDULE_MAP_STATUS_DUMMY)) {
          delList.add(record);
        }
      }
      resultList.removeAll(delList);
      // add end

      logger.debug("check schedule end.");

      // --------------------------------
      // スケジュールをソート
      // --------------------------------
      logger.debug("sort schedule start.");
      // 条件：開始時刻の昇順
      WsScheduleUtils.sortByTime(resultList, true);
      logger.debug("sort schedule end.");

      // --------------------------------
      // ScheduleViewBean配列に変換
      // --------------------------------
      logger.debug("create schedule view start.");

      result = new ScheduleViewBean[resultList.size()];
      int index = 0;
      for (EipTScheduleMap record : resultList) {
        result[index] = new ScheduleViewBean();
        EipTSchedule schedule = record.getEipTSchedule();
        // スケジュールID
        result[index].setScheduleId(schedule.getScheduleId());
        // 予定名
        result[index].setName(schedule.getName());
        // 場所
        result[index].setPlace(schedule.getPlace());
        // 日付
        Date scheduleDate = null;
        if (schedule.getRepeatPattern().equals(EIP_T_SCHEDULE_REPEAT_PATTERN_NORMAL)
          || schedule.getRepeatPattern().equals(EIP_T_SCHEDULE_REPEAT_PATTERN_ACROSS)) {
          // 単発、日またぎスケジュールは開始日時を日付とする。
          scheduleDate = schedule.getStartDate();
        } else {
          // 繰り返しスケジュールは処理日時を日付とする。
          scheduleDate = demandDateTimeNonSecond;
        }
        result[index].setDate(WsUtils.getYMDFromDate(scheduleDate));
        // 終了日付
        if (schedule.getRepeatPattern().equals(EIP_T_SCHEDULE_REPEAT_PATTERN_ACROSS)) {
          result[index].setEndDate(WsUtils.getYMDFromDate(schedule.getEndDate()));
        }
        // 開始時刻
        result[index].setStartTime(WsUtils.getHHMIFromDate(schedule.getStartDate()));
        // 終了時刻
        result[index].setEndTime(WsUtils.getHHMIFromDate(schedule.getEndDate()));
        index++;
      }

      logger.debug("create schedule view end.");

    } catch (Exception e) {
      // 予期せぬエラーの場合、0件のスケジュールリストを返す
      logger.error("最新スケジュール取得で予期せぬ例外が発生しました。処理日時：["
        + demandDateTime
        + "] 通知時間：["
        + noticeTiming
        + "]分前 ユーザー名：["
        + userName
        + "] IPアドレス： ["
        + getRequest().getRemoteAddr()
        + "]", e);
      return new ScheduleViewBean[0];
    } finally {
      closeDbConecction();
    }
    logger.info("【終了】スケジュール取得サービス ユーザー:" + userName);
    return result;
  }

  /**
   * メール取得サービス
   * <p>
   * 未受信の新着メール情報を取得して返す。<br>
   * 
   * @param demandDateTime
   *            前回受信日時 [年4桁/月2桁/日2桁 時2桁:分2桁:秒2桁]
   * @param userName
   *            ユーザー名
   * @param password
   *            パスワード
   * @return ReceiveMailViewBean[] メール一覧（配列）
   */
  public ReceiveMailViewBean[] getTargetNewRecieveMailList(String demandDateTime, String userName, String password) {

    logger.info("【開始】メール取得サービス ユーザー:" + userName);

    ReceiveMailViewBean[] result = null;
    JetspeedUser user = null;
    try {

      // DB接続
      openDbConecction();

      // ユーザー情報を取得
      user = JetspeedUserManagement.getUser(new UserNamePrincipal(userName));
      if (user == null) {
        return new ReceiveMailViewBean[0];
      }

      // --------------------------------
      // DBよりメールアカウント情報取得
      // --------------------------------

      SelectQuery<EipMMailAccount> mailAccountQuery = Database.query(EipMMailAccount.class);
      Expression exp = ExpressionFactory.matchExp(EipMMailAccount.USER_ID_PROPERTY, Integer.valueOf(user.getUserId()));
      mailAccountQuery.andQualifier(exp);

      // 代理送受信制御テーブルのAccountIDに合致する適用IDを検索し、
      // OR条件としてメールアカウント情報を検索する。
      // 適用タイプが"U"(ユーザー)
      // 送信受信種別が"R"(受信)
      SelectQuery<AvzTMailSendRecvAcl> mailSendRecvAclQuery = Database.query(AvzTMailSendRecvAcl.class);
      Expression expAcl1 = ExpressionFactory.matchExp(AvzTMailSendRecvAcl.TARGET_ID_PROPERTY, user.getUserId());
      Expression expAcl2 = ExpressionFactory.matchExp(AvzTMailSendRecvAcl.ACL_TYPE_PROPERTY, AVZ_T_MAIL_SEND_RECV_ACL_ACL_TYPE_RECV);
      Expression expAcl3 = ExpressionFactory.matchExp(AvzTMailSendRecvAcl.TARGET_TYPE_PROPERTY, AVZ_T_MAIL_SEND_RECV_ACL_TARGET_TYPE_USER);
      mailSendRecvAclQuery.setQualifier(expAcl1.andExp(expAcl2).andExp(expAcl3));

      List<AvzTMailSendRecvAcl> mailSendRecvAclList = mailSendRecvAclQuery.fetchList();
      // 取得したIDをリストに格納
      List<Integer> ids = new ArrayList<Integer>();
      for (AvzTMailSendRecvAcl avzTMailSendRecvAcl : mailSendRecvAclList) {
        ids.add(avzTMailSendRecvAcl.getAccountId());
      }
      if (ids.size() > 0) {
        // OR条件として設定
        Expression exp3 = ExpressionFactory.inDbExp(EipMMailAccount.ACCOUNT_ID_PK_COLUMN, ids);
        mailAccountQuery.orQualifier(exp3);
      }

      logger.debug("search mail account start.");

      List<EipMMailAccount> mailAccountList = mailAccountQuery.fetchList();

      logger.debug("search mail account end.");

      // --------------------------------
      // メールアカウント毎に未受信メールを取得する
      // --------------------------------
      logger.debug("get mail start.");

      List<ReceiveMailViewBean> mailViewList = new ArrayList<ReceiveMailViewBean>();
      for (EipMMailAccount mailAccount : mailAccountList) {

        logger.debug("mailAccount:" + mailAccount.getAccountId());

        // --------------------------------
        // メールを受信する
        // --------------------------------
        // remove start リマインダーでの受信処理をバッチへ移動 2012.07.13
        // receiveMailsThread(user, mailAccount);
        // remove end

        // 前回処理日時を変換
        Date mailCheckDateTime = WsUtils.getDateFromYMDHHMISE(demandDateTime);

        SelectQuery<EipTMail> mailListQuery = Database.query(EipTMail.class);
        mailListQuery.select(EipTMail.MAIL_ID_PK_COLUMN);
        mailListQuery.select("USER_ID");

        // 検索条件：ユーザーIDと一致
        Expression exp1 = ExpressionFactory.matchExp(EipTMail.USER_ID_PROPERTY, mailAccount.getUserId());
        // 検索条件：アカウントIDと一致
        Expression exp2 = ExpressionFactory.matchExp(EipTMail.ACCOUNT_ID_PROPERTY, mailAccount.getAccountId());
        // 検索条件：既読フラグが"未読"
        Expression exp3 = ExpressionFactory.matchExp(EipTMail.READ_FLG_PROPERTY, EIP_T_MAIL_READ_FLG_UNREAD);
        // 検索条件：前回処理日時よりも後
        Expression exp4 = ExpressionFactory.greaterOrEqualExp(EipTMail.CREATE_DATE_PROPERTY, mailCheckDateTime);

        // 未通知のメール情報取得
        mailListQuery.setQualifier(exp1.andExp(exp2).andExp(exp3).andExp(exp4));
        logger.debug("search mail start.");
        List<EipTMail> dbMailList = mailListQuery.fetchList();
        logger.debug("search mail end.");

        // mailListに格納
        for (EipTMail mail : dbMailList) {
          ReceiveMailViewBean rm = new ReceiveMailViewBean();
          rm.setuId(Integer.toString(mail.getMailId()));
          // rm.setReceiveDate(WsUtils.getYMDFromDate(mail.getEventDate()));
          // rm.setReceiveTime(WsUtils.getHHMIFromDate(mail.getEventDate()));
          // rm.setSubject(ALMailUtils.decodeSubject(mail.getSubject()));
          // 代理フラグを立てる
          rm.setIsSubstitute(Integer.parseInt(user.getUserId()) != mail.getUserId());
          mailViewList.add(rm);

          // logger.debug("検知MailID:" + rm.getuId());
        }
      }

      logger.debug("get mail end.");

      // --------------------------------
      // メール一覧をソートする
      // --------------------------------
      // logger.debug("sort mail start.");

      // 条件：受信日時の降順
      // WsMailUtils.sortByTime(mailViewList, false);

      // logger.debug("sort mail end.");

      // --------------------------------
      // ReceiveMailViewBean配列に変換
      // --------------------------------
      result = mailViewList.toArray(new ReceiveMailViewBean[0]);

    } catch (Exception e) {
      // 予期せぬエラーの場合、0件のメールリストを返す
      logger.error("最新メール取得で予期せぬ例外が発生しました。前回受信日時：[" + demandDateTime + "] ユーザー名：[" + userName + "] IPアドレス： [" + getRequest().getRemoteAddr() + "]", e);
      return new ReceiveMailViewBean[0];
    } finally {
      closeDbConecction();
    }
    logger.info("【終了】メール取得サービス ユーザー::" + userName + " 検知数:" + result.length);
    return result;
  }

  /**
   * 同期受信用メソッド
   * <p>
   * 未受信の新着メール情報を受信する。<br>
   * 
   * @param user
   *            受信対象ユーザー
   * @param account
   *            受信対象アカウント
   * @throws Exception
   *             すべての例外
   */
  private void receiveMailsThread(JetspeedUser user, EipMMailAccount account) throws Exception {
    // synchronized (ALPop3MailReceiveThread.KEY_SYNCHRONIZED_LOCK) {

    int accountId = account.getAccountId();
    if (!ALPop3MailReceiveThread.isProcessing(user, accountId)) {
      // メールと接続してなければ新規にスレッドを生成
      Runnable receiver =
        new ALPop3MailReceiveThread(Database.createDataContext(Database.getDomainName()), user, accountId, ALPop3MailReceiveThread.PROCESS_TYPE_RECEIVEMAIL);
      Thread mailthread = new Thread(receiver);
      logger.debug("Receive Thread start.");
      mailthread.start();
      logger.debug("Receive Thread processing.");
      // 待ち合わせを行う
      mailthread.join();
      logger.debug("Receive Thread end.");
    }
    // }
  }

  /**
   * 移行済みフラグ取得サービス
   * <p>
   * ユーザー情報から移行済みフラグを取得する。<br>
   * パラメータのフラグ種類によって、スケジュール移行済みフラグ、もしくは、アドレス移行済みフラグのいずれかを返す。<br>
   * 
   * @param userName
   *            ユーザー名（アカウント）
   * @param password
   *            パスワード
   * @param flagType
   *            フラグ種類
   * @return String 移行済みフラグの値
   */
  public String getMigrateFlag(String userName, String password, String flagType) {

    String result = "";
    JetspeedUser user = null;

    try {
      // DB接続
      openDbConecction();

      // ユーザー情報を取得
      user = JetspeedUserManagement.getUser(new UserNamePrincipal(userName));
      if (user == null) {
        return result;
      }

      // --------------------------------
      // DBより該当フラグ取得
      // --------------------------------

      SelectQuery<TurbineUser> query = Database.query(TurbineUser.class);
      // 検索条件：ユーザーIDと一致
      Expression exp1 = ExpressionFactory.matchDbExp(TurbineUser.USER_ID_PK_COLUMN, Integer.valueOf(user.getUserId()));
      query.andQualifier(exp1); // 検索実行
      List<TurbineUser> resultList = query.fetchList();

      if (flagType.equals(TUBINE_USER_SCHEDULE_MIGRATE)) {
        // スケジュール移行済みフラグを取得
        result = resultList.get(0).getScheduleMigrate();
      } else if (flagType.equals(TUBINE_USER_ADDRESS_MIGRATE)) {
        // アドレス移行済みフラグを取得
        result = resultList.get(0).getAddressMigrate();
      } else {
        return result;
      }

    } catch (Exception e) {
      // 予期せぬエラーの場合、空文字列を返す
      logger.error("移行済みフラグ取得で予期せぬ例外が発生しました。ユーザー名：[" + userName + "] フラグタイプ：[" + flagType + "]", e);
      return "";
    } finally {
      closeDbConecction();
    }
    return result;
  }

  /**
   * スケジュールインポートサービス
   * <p>
   * Outlookの予定データをAipoのスケジュール情報にインポートする。<br>
   * 
   * @param userName
   *            ユーザー名
   * @param password
   *            パスワード
   * @param scheduleList
   *            スケジュール一覧（配列）
   * @param warnData
   *            警告発生データ
   * @param count
   *            登録件数
   * @return WebServiceResultBean 結果格納用インスタンス
   */
  public WebServiceResultBean importOutlookScheduleList(String userName, String password, OutlookScheduleBean[] scheduleList) {

    JetspeedUser user = null;
    EipTSchedule schedule = null;
    StringBuffer errMsg = new StringBuffer();
    String warnMsg = "";
    String skipedData = "";
    int count = 0;
    WebServiceResultBean result = new WebServiceResultBean();

    // 処理開始メッセージログ出力
    logger.info("スケジュールインポート処理開始 ユーザーID：[" + userName + "]");

    // DB接続
    openDbConecction();

    // 標準のトランザクション（オートコミット）
    Transaction baseTx = Transaction.getThreadTransaction();

    // 自己管理トランザクション
    Transaction tx = Transaction.internalTransaction(DataContext.getThreadDataContext().getParentDataDomain().getTransactionDelegate());

    DataContext dataContext = DataContext.getThreadDataContext();
    // 標準のトランザクションを自己管理トランザクションに置き換えます。
    Transaction.bindThreadTransaction(tx);

    try {

      // トランザクション開始
      tx.begin();

      // ユーザー情報を取得
      user = JetspeedUserManagement.getUser(new UserNamePrincipal(userName));
      if (user == null) {
        // 認証失敗の場合
        errMsg.append("認証に失敗しました。");
        result.setResult(false);
        result.setCount(0);
        result.setErrMsg(errMsg.toString());
        result.setSkipData("");
        return result;
      }

      // add start 2012.2.29 スケジュール移行ツール改修
      // 登録前に処理ユーザーの共有カテゴリ"2"のスケジュールを削除
      deleteScheduleCommonCategory2(user.getUserId());
      // add end 2012.2.29

      // 登録処理
      for (OutlookScheduleBean olSchedule : scheduleList) {

        // バリデーション
        String error = OutlookTransferUtils.IsValidSchedule(olSchedule);
        if (!StringUtils.isEmpty(error)) {
          // チェックNGなら処理中止
          result.setResult(false);
          result.setCount(0);
          result.setErrMsg(error);
          result.setSkipData("");

          // エラーメッセージ出力
          logger.error(error);

          return result;
        }

        Boolean isAllDayEvent = Boolean.parseBoolean(olSchedule.getAllDayEvent());
        Boolean isRecurring = Boolean.parseBoolean(olSchedule.getIsRecurring());
        Boolean isNoEndDate = Boolean.parseBoolean(olSchedule.getNoEndDate());
        Boolean isOrganizer = Boolean.parseBoolean(olSchedule.getIsOrganizer());

        // 主催者かどうかを判定
        // <Outlookスケジュール情報>.主催者名が空ではなく、<Outlookスケジュール情報>.主催者フラグがFalse(非主催者)
        if (!StringUtils.isEmpty(olSchedule.getOrganizerName()) && !isOrganizer) {
          warnMsg =
            "非主催者データのため、スキップ　⇒　[件名]："
              + olSchedule.getSubject()
              + " [開始日時]："
              + olSchedule.getStart()
              + " [終了日時]："
              + olSchedule.getEnd()
              + System.getProperty("line.separator");
          logger.warn(warnMsg);
          skipedData += warnMsg;
          continue;
        }

        // 以下の条件により、終日の繰返しデータかどうかを判定
        // <Outlookスケジュール情報>.繰返し有無がTrue(繰返しあり)である。
        // かつ、<Outlookスケジュール情報>.終日フラグがTrue(終日のイベント)である、
        if (isRecurring && isAllDayEvent) {
          // 終日の繰返しデータはスキップ
          warnMsg =
            "終日繰り返しデータのため、スキップ　⇒　[件名]："
              + olSchedule.getSubject()
              + " [開始日時]："
              + olSchedule.getStart()
              + " [終了日時]："
              + olSchedule.getEnd()
              + System.getProperty("line.separator");
          logger.warn(warnMsg);
          skipedData += warnMsg;
          continue;
        }

        // 以下の条件により、日またぎの繰返しデータかどうかを判定
        // <Outlookスケジュール情報>.繰返し有無がTrue(繰返しあり)である。
        // かつ、<Outlookスケジュール情報>.開始日の年月日と<Outlookスケジュール情報>.終了日の年月日が異なる。

        if (isRecurring && !olSchedule.getStart().substring(0, 10).equals(olSchedule.getEnd().substring(0, 10))) {
          // 日またぎの繰返しデータはスキップ
          warnMsg =
            "日またぎ繰り返しデータのため、スキップ　⇒　[件名]："
              + olSchedule.getSubject()
              + " [開始日時]："
              + olSchedule.getStart()
              + " [終了日時]："
              + olSchedule.getEnd()
              + System.getProperty("line.separator");
          logger.warn(warnMsg);
          skipedData += warnMsg;
          continue;
        }

        // 繰り返しパターン判定処理
        String repeatPattern =
          OutlookTransferUtils.getScheduleRepeatPattern(
            isRecurring,
            isAllDayEvent,
            olSchedule.getRecurrenceType(),
            olSchedule.getInterval(),
            olSchedule.getDayOfWeekMask(),
            olSchedule.getDayOfMonth(),
            olSchedule.getMonthOfYear(),
            olSchedule.getInstance(),
            olSchedule.getStart(),
            olSchedule.getEnd(),
            errMsg);

        // エラーの場合
        if (StringUtils.isEmpty(repeatPattern)) {
          result.setResult(false);
          result.setCount(0);
          result.setErrMsg(errMsg.toString());
          result.setSkipData("");

          return result;
        }

        // 繰り返しパターンに応じて開始日と終了日を設定
        Calendar startDateTime = Calendar.getInstance();
        Calendar endDateTime = Calendar.getInstance();

        // パターン開始日をCalendarに変換
        Calendar startDate = Calendar.getInstance();
        startDate.setTime(format.parse(olSchedule.getPatternStartDate()));
        // パターン終了日をCalendarに変換
        Calendar endDate = Calendar.getInstance();
        endDate.setTime(format.parse(olSchedule.getPatternEndDate()));
        // 開始時刻をCalendarに変換
        Calendar startTime = Calendar.getInstance();
        startTime.setTime(format.parse(olSchedule.getStart()));
        // 終了時刻をCalendarに変換
        Calendar endTime = Calendar.getInstance();
        endTime.setTime(format.parse(olSchedule.getEnd()));

        if (repeatPattern.equals(EIP_T_SCHEDULE_REPEAT_PATTERN_SPAN)) { // 期間の場合
          // 開始時刻(終了時刻)の年月日 + "00:00:00"をセット
          startDateTime.set(startTime.get(Calendar.YEAR), startTime.get(Calendar.MONTH), startTime.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
          endDateTime.set(endTime.get(Calendar.YEAR), endTime.get(Calendar.MONTH), endTime.get(Calendar.DAY_OF_MONTH) - 1, // Aipoに合わせて日を-1日ずらす。
            0,
            0,
            0);
        } else if (repeatPattern.equals(EIP_T_SCHEDULE_REPEAT_PATTERN_NORMAL)) { // 通常の場合

          // 開始時刻(終了時刻)をセット
          startDateTime = startTime;
          endDateTime = endTime;
        } else if (repeatPattern.equals(EIP_T_SCHEDULE_REPEAT_PATTERN_ACROSS)) { // 日またぎの場合

          // 開始時刻(終了時刻)をセット
          startDateTime = startTime;
          endDateTime = endTime;
        } else {

          // 開始日時を算出し、<開始日時>に格納する。
          // <Outlookスケジュール情報>.開始日 ＋ <Outlookスケジュール情報>.開始時刻をセット
          startDateTime.set(startDate.get(Calendar.YEAR), startDate.get(Calendar.MONTH), startDate.get(Calendar.DAY_OF_MONTH), startTime
            .get(Calendar.HOUR_OF_DAY), startTime.get(Calendar.MINUTE), startTime.get(Calendar.SECOND));

          // 終了日時を算出し、<終了日時>に格納する。
          if (!isNoEndDate) {
            // 終了日ありの場合
            // <Outlookスケジュール情報>.終了日 ＋ <Outlookスケジュール情報>.終了時刻をセット
            endDateTime.set(
              endDate.get(Calendar.YEAR),
              endDate.get(Calendar.MONTH),
              endDate.get(Calendar.DAY_OF_MONTH),
              endTime.get(Calendar.HOUR_OF_DAY),
              endTime.get(Calendar.MINUTE),
              endTime.get(Calendar.SECOND));
          } else {
            if (isRecurring) {
              // 繰返しありの場合
              // 9999年12月31日 + <Outlookスケジュール情報>.終了時刻にセット
              endDateTime.set(9999, (12 - 1), 31, endTime.get(Calendar.HOUR_OF_DAY), endTime.get(Calendar.MINUTE), endTime.get(Calendar.SECOND));
            } else {
              // 繰返しなしの場合
              // <Outlookスケジュール情報>.終了時刻をセット
              endDateTime.set(
                endTime.get(Calendar.YEAR),
                endTime.get(Calendar.MONTH),
                endTime.get(Calendar.DAY_OF_MONTH),
                endTime.get(Calendar.HOUR_OF_DAY),
                endTime.get(Calendar.MINUTE),
                endTime.get(Calendar.SECOND));
            }
          }
        }

        // --------------------------------
        // DBへ該当スケジュール登録
        // --------------------------------

        int ownerId = Integer.valueOf(user.getUserId());

        // スケジュールテーブルへ登録
        // 新規オブジェクトモデル
        schedule = Database.create(EipTSchedule.class);
        // 親スケジュール ID
        schedule.setParentId(0);
        // ユーザーID
        schedule.setOwnerId(ownerId);
        // 繰り返しパターン
        schedule.setRepeatPattern(repeatPattern);
        // 開始時間
        schedule.setStartDate(startDateTime.getTime());
        // 終了時間
        schedule.setEndDate(endDateTime.getTime());
        // スケジュール名
        schedule.setName(olSchedule.getSubject());
        // 場所
        schedule.setPlace(olSchedule.getLocation());
        // 内容
        schedule.setNote(olSchedule.getBody());
        // 公開フラグ
        schedule.setPublicFlag(olSchedule.getPublicFlgString());
        // 編集フラグ
        schedule.setEditFlag(EIP_T_SCHEDULE_EDIT_FLAG_EDIT);
        // 作成ユーザーID
        schedule.setCreateUserId(ownerId);
        // 更新ユーザーID
        schedule.setUpdateUserId(ownerId);
        // 作成日
        Date now = new Date();
        schedule.setCreateDate(now);
        // 更新日
        schedule.setUpdateDate(now);
        // 繰り返し回数 終了日を登録するためすべてnullとする。
        schedule.setRepeatNum(null);
        dataContext.commitChanges();

        // スケジュールマップテーブルへ登録
        boolean isOwnerRegisterd = false;
        // 主催者名をチェック
        if (olSchedule.getMeetingStatus().equals(OUTLOOK_SCHEDULE_MEETING_STATUS_NON_MEETING) || StringUtils.isEmpty(olSchedule.getOrganizerName())) {
          // 会議でない場合はログインユーザーのみを所有者として登録する。
          // また、主催者が取れない場合は、共有スケジュールではなく各々のスケジュールに個別に登録するため、
          // 出席者の数に関わらず、ログインユーザーのみを所有者として登録する。
          isOwnerRegisterd = false;

        } else {
          // 0件の場合：<所有者登録フラグ>にFalse(未登録)のまま。
          // 1件以上存在する場合：<Outlookスケジュール情報>.出席者の件数分スケジュールマップを登録する。
          if (olSchedule.getRecipientsType() != null && olSchedule.getRecipientsType().length > 0) {
            String recipientStatus = "";
            int recipientuserId = 0;
            for (int i = 0; i < olSchedule.getRecipientsType().length; i++) {
              // <出席者>.会議の受信者タイプが3(リソース)である場合は以下の処理をスキップする。
              if (Integer.parseInt(olSchedule.getRecipientsType()[i]) == 3) {
                continue;
              }
              // <ユーザー情報>.メールアドレスと<出席者>.アドレスが一致するかどうか判定する。
              if (user.getEmail().equals(olSchedule.getRecipientsAddress()[i])) {
                // 一致する場合、所有者は登録済み
                // <状態>に"O"(所有者)を格納する。
                recipientStatus = EIP_T_SCHEDULE_MAP_STATUS_OWNER;
                // <出席者ユーザーID>に<ユーザー情報>.ユーザーIDを格納する。
                recipientuserId = ownerId;
                // <所有者登録フラグ>にTrue(登録済)を格納する。
                isOwnerRegisterd = true;
              } else {
                // 一致しない場合、所有者は未登録
                // <状態>に<出席者>.会議への返信状況をコード変換した値を格納する。
                recipientStatus = olSchedule.getMeetingResponseStatusString(olSchedule.getMeetingResponseStatus()[i]);
                // [アドレスユーザー検索]を実行し、<出席者>.アドレスから出席者の<ユーザー情報>を取得し、
                // <出席者ユーザー情報>に格納する。
                try {
                  recipientuserId = OutlookTransferUtils.getUserFromEmail(olSchedule.getRecipientsAddress()[i]).getUserId();
                } catch (Exception e) {
                  // 受入テスト障害275
                  // 当該アドレスがAipoに存在しない場合は、そのアドレスをスキップして会議案内の登録を行なうように修正する。
                  continue;
                }
              }

              EipTScheduleMap map = Database.create(EipTScheduleMap.class);
              map.setEipTSchedule(schedule);
              // スケジュールID
              map.setScheduleId(schedule.getScheduleId());
              // ユーザーID
              map.setUserId(recipientuserId);
              // 状態
              map.setStatus(recipientStatus);
              // 種別 U：ユーザー
              map.setType(EIP_T_SCHEDULE_MAP_TYPE_USER);
              // 共有カテゴリーID
              EipTCommonCategory category =
              // change start 2012.2.29 スケジュール移行ツール改修
                // 共有カテゴリを"2"に変更
                // CommonCategoryUtils.getEipTCommonCategory(Long.valueOf(1));
                CommonCategoryUtils.getEipTCommonCategory(Long.valueOf(2));
              // change end 2012.2.29
              map.setEipTCommonCategory(category);
              map.setCommonCategoryId(1);
              map.setEipTSchedule(schedule);
              // 必須フラグ
              map.setRequired(olSchedule.getRecipientTypeString(Integer.parseInt(olSchedule.getRecipientsType()[i])));
              // 重要度
              map.setPriority(EIP_T_SCHEDULE_MAP_PRIORITY_FALSE);
              // ダミー未回答フラグ
              map.setDummyNonResponse(null);
              dataContext.commitChanges();
            }
          }
        }
        // <所有者登録フラグ>がFalse(未登録)の場合：ログインユーザーを所有者として登録する。
        if (!isOwnerRegisterd) {
          EipTScheduleMap map = Database.create(EipTScheduleMap.class);
          map.setEipTSchedule(schedule);
          map.setScheduleId(schedule.getScheduleId());
          // ユーザーID
          map.setUserId(ownerId);
          // 状態 O：所有者
          map.setStatus(EIP_T_SCHEDULE_MAP_STATUS_OWNER);
          // 種別 U：ユーザー
          map.setType(EIP_T_SCHEDULE_MAP_TYPE_USER);
          // 共有カテゴリーID
          EipTCommonCategory category =
          // change start 2012.2.29 スケジュール移行ツール改修
            // 共有カテゴリを"2"に変更
            // CommonCategoryUtils.getEipTCommonCategory(Long.valueOf(1));
            CommonCategoryUtils.getEipTCommonCategory(Long.valueOf(2));
          // change end 2012.2.29
          map.setEipTCommonCategory(category);
          map.setCommonCategoryId(1);
          // 必須フラグ 必須：R
          map.setRequired(EIP_T_SCHEDULE_MAP_REQUIERED_REQUIERED);
          // 重要度
          map.setPriority(EIP_T_SCHEDULE_MAP_PRIORITY_FALSE);
          // ダミー未回答フラグ
          map.setDummyNonResponse(null);
          dataContext.commitChanges();
        }
        count++;
      }

      // スケジュール移行済みフラグを更新
      TurbineUser turbineUser = Database.get(TurbineUser.class, user.getUserId());
      turbineUser.setScheduleMigrate(MIGRATE_FLAG_COMPLETED);

      // 登録処理を確定
      Database.commit();

    } catch (Exception e) {
      // 予期せぬエラーの場合、エラーメッセージを返す
      errMsg.append("スケジュール登録処理で予期せぬ例外が発生しました。" + e.toString());
      logger.error(errMsg, e);

      // 登録処理をキャンセル
      Database.rollback();
      result.setResult(false);
      result.setCount(0);
      result.setErrMsg(errMsg.toString());
      result.setSkipData("");
      return result;

    } finally {
      // 自己管理トランザクションを標準のトランザクションに戻します。
      Transaction.bindThreadTransaction(baseTx);
      closeDbConecction();
      // 処理終了メッセージログ出力
      logger.info("スケジュールインポート処理終了 ユーザーID：[" + userName + "]");
    }

    // 結果格納用インスタンスに結果を格納する。
    result.setResult(true);
    result.setCount(count);
    result.setErrMsg(errMsg.toString());
    result.setSkipData(skipedData);
    return result;
  }

  /**
   * アドレス帳インポートサービス
   * <p>
   * Outlookの連絡先データをAipoのアドレス帳に登録する。<br>
   * 
   * @param userName
   *            ユーザー名
   * @param password
   *            パスワード
   * @param scheduleList
   *            アドレス一覧（配列）
   * @param warnData
   *            警告発生データ
   * @param count
   *            登録件数
   * @return WebServiceResultBean 結果格納用インスタンス
   */
  public WebServiceResultBean importOutlookAddressList(String userName, String password, OutlookAddressBookBean[] addressList) {

    JetspeedUser user = null;
    EipMAddressbook addressBook = null;
    int count = 0;
    StringBuffer errMsg = new StringBuffer();
    WebServiceResultBean result = new WebServiceResultBean();

    // 処理開始メッセージログ出力
    logger.info("アドレス帳インポート処理開始 ユーザーID：[" + userName + "]");

    // DB接続
    openDbConecction();

    // 標準のトランザクション（オートコミット）
    Transaction baseTx = Transaction.getThreadTransaction();

    // 自己管理トランザクション
    Transaction tx = Transaction.internalTransaction(DataContext.getThreadDataContext().getParentDataDomain().getTransactionDelegate());

    DataContext dataContext = DataContext.getThreadDataContext();
    // 標準のトランザクションを自己管理トランザクションに置き換えます。
    Transaction.bindThreadTransaction(tx);

    try {

      // トランザクション開始
      tx.begin();

      // ユーザー情報を取得
      user = JetspeedUserManagement.getUser(new UserNamePrincipal(userName));
      if (user == null) {
        // 認証失敗の場合
        errMsg.append("認証に失敗しました。");
        result.setResult(false);
        result.setCount(0);
        result.setErrMsg(errMsg.toString());
        return result;
      }

      // 登録処理
      for (OutlookAddressBookBean olAddressBook : addressList) {

        // バリデーションチェック
        String error = OutlookTransferUtils.IsValidAddress(olAddressBook);
        if (!StringUtils.isEmpty(error)) {
          // チェックNGなら処理中止
          result.setResult(false);
          result.setCount(0);
          result.setErrMsg(error);

          // エラーメッセージ出力
          logger.error(error);

          return result;
        }

        // --------------------------------
        // DBへ該当アドレス登録
        // --------------------------------

        int ownerId = Integer.valueOf(user.getUserId());

        // 個人アドレス帳テーブルへ登録
        // 新規オブジェクトモデル
        addressBook = Database.create(EipMAddressbook.class);
        // ユーザーID
        addressBook.setOwnerId(Integer.valueOf(ownerId));

        // 表題を名前として登録。ない場合は姓名を使用
        if (StringUtils.isEmpty(olAddressBook.getFullName())) {
          // 名
          addressBook.setFirstName(olAddressBook.getFirstName());
          // 姓
          addressBook.setLastName(olAddressBook.getLastName());
        } else {
          // 名
          addressBook.setFirstName("");
          // 姓
          addressBook.setLastName(olAddressBook.getFullName());
        }

        // 名カナ
        addressBook.setFirstNameKana(olAddressBook.getYomiFirstName());
        // 姓カナ
        addressBook.setLastNameKana(olAddressBook.getYomiLastName());
        // メールアドレス
        addressBook.setEmail(olAddressBook.getEmail1Address());
        // 電話番号
        addressBook.setTelephone(olAddressBook.getBusinessTelephoneNumber());
        // 携帯電話番号
        addressBook.setCellularPhone(olAddressBook.getMobileTelephoneNumber());
        // 携帯メールアドレス
        addressBook.setCellularMail(olAddressBook.getEmail2Address());
        // 役職名
        addressBook.setPositionName(olAddressBook.getJobTitle());
        // 公開フラグ
        addressBook.setPublicFlag(EIP_M_ADDRESSBOOK_PUBLIC_FLAG_CLOSED);
        // 登録ユーザーID
        addressBook.setCreateUserId(ownerId);
        // 更新ユーザーID
        addressBook.setUpdateUserId(ownerId);
        // 作成日
        addressBook.setCreateDate(format.parse(olAddressBook.getCreationTime()));
        // 更新日
        addressBook.setUpdateDate(format.parse(olAddressBook.getLastModificationTime()));
        // FAX番号
        addressBook.setFaxNumber(olAddressBook.getBusinessFaxNumber());

        // 備考に部署と住所を登録
        String note = "";
        if (!StringUtils.isEmpty(olAddressBook.getDepartment())) {
          note += "部署：";
          note += olAddressBook.getDepartment();
          note += System.getProperty("line.separator");
        }
        if (!StringUtils.isEmpty(olAddressBook.getBusinessAddressCountry())
          || !StringUtils.isEmpty(olAddressBook.getBusinessAddressPostalCode())
          || !StringUtils.isEmpty(olAddressBook.getBusinessAddressState())
          || !StringUtils.isEmpty(olAddressBook.getBusinessAddressCity())
          || !StringUtils.isEmpty(olAddressBook.getBusinessAddressStreet())) {

          note += "住所：";
          // 国/地域
          if (!StringUtils.isEmpty(olAddressBook.getBusinessAddressCountry())) {
            note += olAddressBook.getBusinessAddressCountry() + " ";
          }
          // 郵便番号
          if (!StringUtils.isEmpty(olAddressBook.getBusinessAddressPostalCode())) {
            note += "〒" + olAddressBook.getBusinessAddressPostalCode() + " ";
          }
          // 都道府県
          if (!StringUtils.isEmpty(olAddressBook.getBusinessAddressState())) {
            note += olAddressBook.getBusinessAddressState();
          }
          // 市区町村
          if (!StringUtils.isEmpty(olAddressBook.getBusinessAddressCity())) {
            note += olAddressBook.getBusinessAddressCity();
          }
          // 番地
          if (!StringUtils.isEmpty(olAddressBook.getBusinessAddressStreet())) {
            note += olAddressBook.getBusinessAddressStreet();
          }
        }
        addressBook.setNote(note);
        dataContext.commitChanges();

        // 会社名を登録する場合
        if (!StringUtils.isEmpty(olAddressBook.getCompanyName())) {

          // 会社名を検索し、存在しなかったらアドレス帳社外グループテーブル、アドレス帳社外グループマップテーブルへ登録
          EipMAddressGroup addressGroup = OutlookTransferUtils.getEipMAddressGroupFromGroupName(olAddressBook.getCompanyName(), ownerId);
          if (addressGroup.getGroupId() == null) {
            // アドレス帳社外グループを登録
            addressGroup = Database.create(EipMAddressGroup.class);
            addressGroup.setGroupName(olAddressBook.getCompanyName());
            addressGroup.setOwnerId(ownerId);
            addressGroup.setPublicFlag(EIP_M_ADDRESS_GROUP_PUBLIC_FLAG_CLOSED);
            Date now = new Date();
            addressGroup.setCreateDate(now); // 作成日
            addressGroup.setUpdateDate(now); // 更新日
            dataContext.commitChanges();
          }

          // アドレス帳社外グループマップテーブルへ登録
          EipTAddressbookGroupMap addressGroupMap = Database.create(EipTAddressbookGroupMap.class);
          addressGroupMap.setEipMAddressbook(addressBook);
          addressGroupMap.setEipTAddressGroup(addressGroup);
          dataContext.commitChanges();
        } else {
          // 会社名が存在しない場合は未所属へ追加
          EipMAddressGroup addressGroup = AddressBookUserUtils.getDefaultEipMAddressGroup();

          // アドレス帳社外グループマップテーブルへ登録
          EipTAddressbookGroupMap addressGroupMap = Database.create(EipTAddressbookGroupMap.class);
          addressGroupMap.setEipMAddressbook(addressBook);
          addressGroupMap.setEipTAddressGroup(addressGroup);
          dataContext.commitChanges();
        }
        count++;
      }

      // アドレス移行済みフラグを更新
      TurbineUser turbineUser = Database.get(TurbineUser.class, user.getUserId());
      turbineUser.setAddressMigrate(MIGRATE_FLAG_COMPLETED);

      // 登録処理を確定
      Database.commit();

    } catch (Exception e) {
      // 予期せぬエラーの場合、エラーメッセージを返す
      errMsg.append("アドレス登録処理で予期せぬ例外が発生しました。" + e.toString());
      logger.error(errMsg, e);

      // 登録処理をキャンセル
      Database.rollback();
      result.setResult(false);
      result.setCount(0);
      result.setErrMsg(errMsg.toString());
      return result;

    } finally {
      // 自己管理トランザクションを標準のトランザクションに戻します。
      Transaction.bindThreadTransaction(baseTx);
      closeDbConecction();
      // 処理終了メッセージログ出力
      logger.info("アドレス帳インポート処理終了 ユーザーID：[" + userName + "]");
    }

    // 結果格納用インスタンスに結果を格納する。
    result.setResult(true);
    result.setCount(count);
    result.setErrMsg(errMsg.toString());
    return result;
  }

  /**
   * メールアカウント移行バッチサービス
   * <p>
   * メールアカウント情報テーブルからデータを出力し、<br>
   * メールアカウントリストを作成する。<br>
   */
  public boolean mailAccountListExportBatchExecuter() {
    try {
      // DB接続
      openDbConecction();
      // エクスポート処理実行
      MailAccountListExportBatchExecuter exec = new MailAccountListExportBatchExecuter();
      exec.executeMailAccountListExportBatch();
    } finally {
      // DBクローズ
      closeDbConecction();
    }
    return true;
  }

  /**
   * ユーザー情報移行バッチサービス
   * <p>
   * 部署情報、ユーザー情報などの各種マスタ情報をファイルから読み込み、<br>
   * Aipoデータベースのテーブルにインポートする。<br>
   */
  public boolean userInfoImportBatchExecuter() {
    try {
      // DB接続
      openDbConecction();
      UserInfoImportBatchExecuter exec = new UserInfoImportBatchExecuter();
      // インポート処理実行
      exec.executeUserInfoImportBatch();
    } finally {
      // DBクローズ
      closeDbConecction();
    }
    return true;
  }

  /**
   * Aipoリマインダーのバージョンが"1.2.0"以上の場合はパスワードを復号する
   * 
   * @param user
   * @param password
   * @return パスワード
   */
  private String decryptPassword(String user, String password) {
    if (getAipoReminderVersion() >= 120) {
      try {
        return WsUtils.decryptDes(password);
      } catch (Exception e) {
        logger.error("パスワードの復号に失敗しました。User:" + user, e);
      }
    } else {
      logger.info("v1.2.0以前のAipoリマインダーからのリクエストのため復号化は行ないません。User:" + user);
    }
    return password;
  }

  /**
   * ユーザーエージェントからAipoリマインダーのバージョンを取得
   * 
   * @return Aipoリマインダーのバージョンを取得
   */
  private int getAipoReminderVersion() {
    int version = 0;
    HttpServletRequest req = getRequest();
    String tmp = req.getHeader("User-Agent");
    if (tmp != null && !"".equals(tmp)) {
      if (tmp.indexOf("AipoReminder") >= 0) {
        String[] versions = tmp.split("/");
        if (versions.length == 2) {
          version = Integer.parseInt(versions[1]);
        }
      }
    }
    return version;
  }

  /**
   * HTTPリクエスト取得処理
   * 
   * @return HTTPリクエスト
   */
  private HttpServletRequest getRequest() {
    MessageContext context = MessageContext.getCurrentMessageContext();
    HttpServletRequest req = (HttpServletRequest) context.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST);
    return req;
  }

  /**
   * 共有カテゴリ"2"のスケジュール削除
   * 
   */
  private void deleteScheduleCommonCategory2(String user_id) {

    SelectQuery<EipTSchedule> query = Database.query(EipTSchedule.class);

    Expression exp1 = ExpressionFactory.matchExp(EipTSchedule.OWNER_ID_PROPERTY, user_id);
    Expression exp2 = ExpressionFactory.matchExp(EipTSchedule.EIP_TSCHEDULE_MAPS_PROPERTY + "." + EipTScheduleMap.COMMON_CATEGORY_ID_PROPERTY, "2");
    query.setQualifier(exp1.andExp(exp2));
    List<EipTSchedule> dellist = query.fetchList();
    // スケジュールの削除
    Database.deleteAll(dellist);

  }
}
