// Copyright (c) 2008, NTT DATA Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Net;
using TERASOLUNA.Fw.Client.Communication;
using TERASOLUNA.Fw.Common;
using TERASOLUNA.Fw.Common.BLogic;
using TERASOLUNA.Fw.Common.Logging;

namespace TERASOLUNA.Fw.Client.BLogic
{
    /// <summary>
    /// サーバ通信を行うビジネスロジックの基底クラスです。
    /// </summary>
    /// <typeparam name="TParam">
    /// サーバに送信するデータオブジェクトの型。
    /// <list type="bullet">
    /// <item>
    /// <paramref name="TParam"/> は、<see cref="ICommunicator{TParam}.Communicate"/> の
    /// 引数で渡す送信データオブジェクトの型となります。
    /// </item>
    /// <item>
    /// サーバと通信を行う <see cref="ICommunicator{TParam}"/> は、<see cref="CommunicateBLogicBase{TParam}"/> 派生クラスで 
    /// <see cref="CreateCommunicator"/> をオーバーライドして生成します。
    /// </item>
    /// </list>
    /// </typeparam>
    public abstract class CommunicateBLogicBase<TParam> : ClientBLogicBase
        where TParam : class
    {
        /// <summary>
        /// <see cref="ILog"/> 実装クラスのインスタンスです。
        /// </summary>
        /// <remarks>
        /// ログ出力に利用します。
        /// </remarks>
        private static ILog _log = LogFactory.GetLogger(typeof(CommunicateBLogicBase<TParam>));

        /// <summary>
        /// 通信相手に通知するリクエスト名のキー値です。
        /// </summary>
        /// <remarks>この定数の値は "RequestName" です。</remarks>
        public static readonly string REQUEST_NAME = "RequestName";

        /// <summary>
        /// アプリケーション構成ファイル、または <see cref="Execute"/> メソッドの引数
        /// <see cref="BLogicParam.Items"/> に設定する、リクエストタイムアウト時間の
        /// キー文字列です。
        /// </summary>
        /// <remarks>この定数の値は "ReqeustTimeout" です。</remarks>
        public static readonly string REQUEST_TIMEOUT = "RequestTimeout";

        /// <summary>
        /// <see cref="BLogicParam.Items"/> に設定されたリクエストヘッダを取得するためのキー文字列です。
        /// </summary>
        /// <remarks>この定数の値は "RequestHeaders" です。</remarks>
        public static readonly string REQUEST_HEADERS = "RequestHeaders";

        /// <summary>
        /// サーバ側で想定しない例外が発生した場合の結果文字列です。
        /// </summary>
        /// <remarks>この定数の値は "serverException" です。</remarks>
        public static readonly string SERVER_EXCEPTION = "serverException";

        /// <summary>
        /// 通信時に例外が発生した場合の結果文字列です。
        /// </summary>
        /// <remarks>この定数の値は "communicationException" です。</remarks>
        public static readonly string COMMUNICATION_EXCEPTION = "communicationException";

        /// <summary>
        /// 通信機能を提供する <see cref="ICommunicator{TParam}"/> を保持するフィールドです。
        /// </summary>
        private ICommunicator<TParam> _communicator = null;

        /// <summary>
        /// <see cref="ClientBLogicBase.Cancelled"/> の設定時に利用される同期用のオブジェクトです。
        /// </summary>
        private object _syncRoot = new object();

        /// <summary>
        /// 通信機能を提供する <see cref="ICommunicator{TParam}"/> を取得します。
        /// </summary>
        /// <value>
        /// 通信機能を提供する <see cref="ICommunicator{TParam}"/>。
        /// </value>
        protected ICommunicator<TParam> Communicator
        {
            get
            {
                return _communicator;
            }
        }

        /// <summary>
        /// <see cref="CommunicateBLogicBase{TParam}"/> クラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <remarks>
        /// デフォルトコンストラクタです。
        /// <list type="bullet">
        /// <item>
        /// <see cref="CreateCommunicator"/> でサーバと通信を行う <see cref="ICommunicator{TParam}"/> を生成します。
        /// </item>
        /// <item>
        /// <see cref="IProgressChangedEventInvoker.ProgressChanged"/> に <see cref="DoCommunicateProgressChanged"/> を登録します。
        /// </item>
        /// </list>
        /// </remarks>
        protected CommunicateBLogicBase()
        {
            _communicator = CreateCommunicator();
            _communicator.ProgressChanged += new ExecuteProgressChangedEventHandler(DoCommunicateProgressChanged);
        }

        /// <summary>
        /// 通信機能を提供する <see cref="ICommunicator{TParam}"/> を生成します。
        /// </summary>
        /// <remarks>
        /// サーバと通信を行う <see cref="ICommunicator{TParam}"/> は、<see cref="CommunicateBLogicBase{TParam}"/> 派生クラスで 
        /// <see cref="CreateCommunicator"/> をオーバーライドして生成します。
        /// </remarks>
        /// <returns>
        /// <see cref="ICommunicator{TParam}"/> 実装クラス。
        /// </returns>
        protected abstract ICommunicator<TParam> CreateCommunicator();

        /// <summary>
        /// <see cref="BLogicResult"/> 派生クラスを生成します。
        /// </summary>
        /// <remarks>
        /// デフォルトの値は <see cref="BLogicResult"/> です。
        /// </remarks>
        /// <returns>
        /// <see cref="BLogicResult"/> 派生クラス。
        /// </returns>
        protected virtual BLogicResult CreateBLogicResult()
        {
            return new BLogicResult();
        }

        /// <summary>
        /// サーバとの通信を行うビジネスロジックを実行します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// 以下の順序で処理を実行します。
        /// <list type="number">
        /// <item>リクエストヘッダの生成 <seealso cref="CreateRequestHeaders"/></item>
        /// <item>通信の前処理 <seealso cref="BeforeCommunicate"/></item>
        /// <item>通信処理実行 <seealso cref="Communicate"/></item>
        /// <item>通信の後処理 <seealso cref="AfterCommunicate"/></item>
        /// </list>
        /// 各処理のメソッドをオーバーライドすることで、通信の前処理や後処理の拡張ができます。
        /// </para>
        /// <para>
        /// サーバアプリケーションとの通信では、リクエストヘッダにリクエスト名を必ず設定します。
        /// リクエスト名は <see cref="BLogicParam.Items"/> に <see cref="REQUEST_NAME"/> を
        /// キーとして設定してください。
        /// その他の値をリクエストヘッダに設定したい場合は、リクエストのヘッダを構成する名前と値のペアを
        /// 格納した <see cref="IDictionary{String, String}"/> を、<see cref="BLogicParam.Items"/> に 
        /// <see cref="REQUEST_HEADERS"/> をキーとして設定してください。
        /// </para>
        /// <para>
        /// リクエストヘッダの生成から通信の後処理までの間で捕捉されない例外が発生した場合は、
        /// 以下のように <see cref="HandleException"/> で例外をハンドリングします。
        /// <list type="bullet">
        /// <item>
        ///     例外が <see cref="CommunicationException"/> の場合
        ///     <list type="bullet">
        ///     <item>
        ///     例外を捕捉してビジネスロジックを終了する。<see cref="BLogicResult"/> の 
        ///     <see cref="BLogicResult.ResultString"/> には <see cref="COMMUNICATION_EXCEPTION"/> を設定し、
        ///     <see cref="BLogicResult.Errors"/> には例外メッセージを格納する。
        ///     </item>
        ///     </list>
        /// </item>
        /// <item>
        ///     例外が <see cref="ServerException"/> で、<see cref="ServerException.ErrorType"/> が
        ///     <see cref="SERVER_EXCEPTION"/> 以外の場合
        ///     <list type="bullet">
        ///     <item>
        ///     例外を捕捉してビジネスロジックを終了する。
        ///     <see cref="BLogicResult"/> の <see cref="BLogicResult.ResultString"/> には、
        ///     サーバアプリケーションで入力値検証エラーが発生した場合には 
        ///     <see cref="Communication.ReceiverBase.SERVER_VALIDATE_EXCEPTION"/> を設定する。
        ///     その他、サーバアプリケーションからクライアントに通知すべきエラーが送られてきた場合は、
        ///     サーバアプリケーションから送信された <see cref="ServerException.ErrorType"/> を設定する。
        ///     <see cref="BLogicResult"/> の <see cref="BLogicResult.Errors"/> には、
        ///     <see cref="ServerException"/> の <see cref="ServerException.Errors"/> を格納する。
        ///     </item>
        ///     </list>
        /// </item>
        /// <item>
        ///     例外が <see cref="ServerException"/> で、<see cref="ServerException.ErrorType"/> が
        ///     <see cref="SERVER_EXCEPTION"/> の場合
        ///     <list type="bullet">
        ///     <item>
        ///     サーバ側で想定しない例外が発生したため、例外を捕捉しないで再スローする。
        ///     </item>
        ///     </list>
        /// </item>
        /// <item>
        ///     その他の例外の場合
        ///     <list type="bullet">
        ///     <item>
        ///     例外を捕捉しないで再スローする。
        ///     </item>
        ///     </list>
        /// </item>
        /// </list>
        /// </para>
        /// </remarks>
        /// <param name="blogicParam">
        /// ビジネスロジックの入力となる <see cref="BLogicParam"/>。
        /// </param>
        /// <returns>
        /// ビジネスロジックの実行結果を格納した <see cref="BLogicResult"/>。
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="blogicParam"/> が null 参照です。
        /// </item>
        /// <item>
        /// 通信処理で必要なデータが null 参照です。
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="ArgumentException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <see cref="BLogicParam.Items"/> に <see cref="REQUEST_NAME"/> をキーとしてリクエスト名が設定されていません。
        /// </item>
        /// <item>
        /// <see cref="BLogicParam.Items"/> から <see cref="REQUEST_NAME"/> をキーとして取得した
        /// リクエスト名が null 参照または空文字です。
        /// </item>
        /// <item>
        /// <see cref="BLogicParam.Items"/> から <see cref="REQUEST_HEADERS"/> をキーとして取得した
        /// リクエストヘッダのコレクションが、<see cref="IDictionary{String, String}"/> 型ではありません。
        /// </item>
        /// <item>
        /// リクエストヘッダのキーとして空文字または制限付きヘッダーを設定できません。
        /// </item>
        /// <item>
        /// リクエストヘッダのキーに無効な文字が含まれています。
        /// </item>
        /// <item>
        /// <see cref="BLogicParam.Items"/> から <see cref="REQUEST_TIMEOUT"/> をキーとして
        /// 取得したリクエストタイムアウト時間が整数値ではありません。
        /// </item>
        /// <item>
        /// 通信処理で必要なデータが不正です。
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="System.IO.FileNotFoundException">
        /// 通信処理で必要なファイルが見つかりません。
        /// </exception>
        /// <exception cref="ServerException">
        /// サーバアプリケーションで想定しない例外が発生しました。
        /// </exception>
        public override BLogicResult Execute(BLogicParam blogicParam)
        {
            if (blogicParam == null)
            {
                throw new ArgumentNullException("blogicParam");
            }

            if (blogicParam.ParamData == null)
            {
                throw new ArgumentException(string.Format(Properties.Resources.E_NULL_PARAMETER, "blogicParam.ParamData"));
            }

            // ビジネスロジックの結果クラス
            BLogicResult blogicResult = CreateBLogicResult();

            // キャンセルの際は結果オブジェクトは使われないが、nullではエラーとなる。
            if (Cancelled)
            {
                return blogicResult;
            }

            try
            {
                // リクエストヘッダの生成
                IDictionary<string, string> requestHeaders = CreateRequestHeaders(blogicParam);

                // 前処理
                BeforeCommunicate(blogicParam, requestHeaders);

                // 通信処理実行
                CommunicationResult communicationResult = Communicate(blogicParam, requestHeaders);

                // 後処理
                AfterCommunicate(communicationResult, blogicResult);
            }
            catch (Exception ex)
            {
                if (_log.IsWarnEnabled)
                {
                    _log.Warn(Properties.Resources.W_BLOGIC_EXCEPTION, ex);
                }

                lock (_syncRoot)
                {
                    if (Cancelled)
                    {
                        return blogicResult;
                    }
                }

                if (!HandleException(ex, blogicResult))
                {
                    throw;
                }
            }

            return blogicResult;
        }

        /// <summary>
        /// リクエストヘッダを生成します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// リクエスト名は必須です。入力値となる<see cref="BLogicParam.Items"/> に <see cref="REQUEST_NAME"/> 
        /// をキーとしてリクエスト名を設定してください。
        /// </para>
        /// <para>
        /// その他の値をリクエストヘッダに設定したい場合は、リクエストのヘッダを構成する名前と値のペアを
        /// 格納した <see cref="IDictionary{String, String}"/> を、<see cref="BLogicParam.Items"/> に 
        /// <see cref="REQUEST_HEADERS"/> をキーとして設定してください。
        /// </para>
        /// <para>
        /// <see cref="BLogicParam.Items"/> に <see cref="REQUEST_HEADERS"/> をキーとして null を設定した場合は、
        /// リクエスト名のみをリクエストヘッダに設定します。
        /// </para>
        /// </remarks>
        /// <returns>
        /// リクエストのヘッダを構成する名前と値のペアを格納している <see cref="IDictionary{String, String}"/>。
        /// </returns>
        /// <exception cref="ArgumentException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <see cref="BLogicParam.Items"/> に <see cref="REQUEST_NAME"/> をキーとしてリクエスト名が設定されていません。
        /// </item>
        /// <item>
        /// <see cref="BLogicParam.Items"/> から <see cref="REQUEST_NAME"/> をキーとして取得した
        /// リクエスト名が null 参照または空文字です。
        /// </item>
        /// <item>
        /// <see cref="BLogicParam.Items"/> から <see cref="REQUEST_HEADERS"/> をキーとして取得した
        /// リクエストヘッダのコレクションが、<see cref="IDictionary{String, String}"/> 型ではありません。
        /// </item>
        /// <item>
        /// リクエストヘッダのキーとして空文字または制限付きヘッダーを設定できません。
        /// </item>
        /// <item>
        /// リクエストヘッダのキーに無効な文字が含まれています。
        /// </item>
        /// </list>
        /// </exception>
        /// <param name="blogicParam">
        /// ビジネスロジックの入力 <see cref="BLogicParam"/>。
        /// </param>
        protected virtual IDictionary<string, string> CreateRequestHeaders(BLogicParam blogicParam)
        {
            IDictionary<string, string> requestHeaders = new Dictionary<string, string>();

            // リクエストヘッダの設定
            bool containsRequestHeaders = blogicParam.Items.ContainsKey(REQUEST_HEADERS) && blogicParam.Items[REQUEST_HEADERS] != null;
            if (containsRequestHeaders)
            {
                IDictionary<string, string> headers = blogicParam.Items[REQUEST_HEADERS] as IDictionary<string, string>;
                if (headers != null)
                {
                    // ビジネスロジックのItemsに設定されたリクエストヘッダを
                    // 通信クラスのリクエストヘッダに設定
                    foreach (string key in headers.Keys)
                    {
                        if (string.IsNullOrEmpty(key))
                        {
                            throw new ArgumentException(Properties.Resources.E_EMPTY_REQUEST_HEADER_KEY);
                        }

                        bool isRestrictedKey = false; 
                        try
                        {
                            isRestrictedKey = WebHeaderCollection.IsRestricted(key);
                        }
                        catch (ArgumentException ex)
                        {
                            throw new ArgumentException(
                                string.Format(Properties.Resources.E_INVALID_REQUEST_HEADER_KEY, key), ex);
                        }

                        if (isRestrictedKey)
                        {
                            throw new ArgumentException(
                                string.Format(Properties.Resources.E_RESTRICTED_REQUEST_HEADER_KEY, key));
                        }

                        requestHeaders.Add(key, headers[key]);
                    }
                }
                else
                {
                    throw new ArgumentException(string.Format(Properties.Resources.E_REFERENCE_TYPE_INVALID_CAST,
                        REQUEST_HEADERS, blogicParam.Items[REQUEST_HEADERS].GetType().FullName, typeof(IDictionary<string, string>).FullName));
                }
            }

            // リクエスト名の設定
            bool containsRequestName = blogicParam.Items.ContainsKey(REQUEST_NAME)
                && blogicParam.Items[REQUEST_NAME] != null && blogicParam.Items[REQUEST_NAME].ToString().Length != 0;
            if (containsRequestName)
            {
                requestHeaders[REQUEST_NAME] = blogicParam.Items[REQUEST_NAME].ToString();
            }
            else
            {
                throw new ArgumentException(Properties.Resources.E_REQUEST_NAME_NOT_FOUND);
            }

            return requestHeaders;
        }

        /// <summary>
        /// 通信の前処理を実行します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// 通信の前処理を実行します。デフォルトでは、パラメータか構成ファイルから取得したリクエストタイムアウト
        /// 時間を <see cref="ICommunicator{TParam}.RequestTimeout"/> に設定します。
        /// </para>
        /// <para>
        /// <see cref="ICommunicator{TParam}"/> の既定のリクエストタイムアウト時間以外の値を設定したい場合は、
        /// <see cref="BLogicParam.Items"/> か、あるいは構成ファイルの<c>AppSettings</c>要素に 
        /// <see cref="REQUEST_TIMEOUT"/> をキーとして 0 以上の整数値を設定してください。
        /// </para>
        /// <para>
        /// <see cref="BLogicParam.Items"/> と構成ファイルのどちらにもリクエストタイムアウト時間の指定がある
        /// 場合、<see cref="BLogicParam.Items"/> で指定された値が利用されます。どちらにもリクエストタイムアウト
        /// 時間が指定されていない場合、<see cref="ICommunicator{TParam}"/> の既定のリクエストタイムアウト時間が
        /// 利用されます。
        /// </para>
        /// </remarks>
        /// <param name="blogicParam">
        /// ビジネスロジックの入力 <see cref="BLogicParam"/>。
        /// </param>
        /// <param name="requestHeaders">
        /// リクエストのヘッダを構成する名前と値のペアを格納している <see cref="IDictionary{String, String}"/>。
        /// </param>
        /// <exception cref="ArgumentException">
        /// <see cref="BLogicParam.Items"/> から <see cref="REQUEST_TIMEOUT"/> をキーとして
        /// 取得したリクエストタイムアウト時間が整数値ではありません。
        /// </exception>
        protected virtual void BeforeCommunicate(BLogicParam blogicParam, IDictionary<string, string> requestHeaders)
        {
            string requestTimeoutString = null;

            // リクエストタイムアウト値を取得する
            if (blogicParam.Items.ContainsKey(REQUEST_TIMEOUT) && blogicParam.Items[REQUEST_TIMEOUT] != null)
            {
                requestTimeoutString = blogicParam.Items[REQUEST_TIMEOUT].ToString();
            }

            if (string.IsNullOrEmpty(requestTimeoutString))
            {
                requestTimeoutString = ConfigurationManager.AppSettings[REQUEST_TIMEOUT];
            }

            // リクエストタイムアウト値を取得した場合、パースしてコミュニケータに設定する
            if (!string.IsNullOrEmpty(requestTimeoutString))
            {
                int requestTimeout = -1;
                if (!int.TryParse(requestTimeoutString, out requestTimeout))
                {
                    throw new ArgumentException(
                        string.Format(Properties.Resources.E_VALUE_TYPE_INVALID_CAST,
                        REQUEST_TIMEOUT, requestTimeoutString, requestTimeout.GetType().FullName));
                }

                Communicator.RequestTimeout = requestTimeout;
            }
        }

        /// <summary>
        /// 通信処理を実行します。
        /// </summary>
        /// <param name="blogicParam">ビジネスロジック入力 <see cref="BLogicParam"/>。</param>
        /// <param name="requestHeaders">リクエストのヘッダを構成する名前と値のペアを格納している <see cref="IDictionary{String, String}"/>。</param>
        /// <returns>通信結果を格納した <see cref="CommunicationResult"/>。</returns>
        protected abstract CommunicationResult Communicate(BLogicParam blogicParam, IDictionary<string, string> requestHeaders);

        /// <summary>
        /// 通信の後処理を実行します。
        /// </summary>
        /// <remarks>
        /// 通信結果を格納した <paramref name="communicationResult"/> から
        /// ビジネスロジックの実行結果を格納する <paramref name="blogicResult"/> に通信結果を反映します。
        /// <see cref="AfterCommunicate"/> は通信処理成功時に実行されるため、
        /// <paramref name="blogicResult.ResultString"/> には <see cref="BLogicResult.SUCCESS"/> が設定されます。
        /// </remarks>
        /// <param name="communicationResult">通信結果を格納した <see cref="CommunicationResult"/>。</param>
        /// <param name="blogicResult">ビジネスロジックの実行結果を格納する <see cref="BLogicResult"/>。</param>
        protected virtual void AfterCommunicate(CommunicationResult communicationResult, BLogicResult blogicResult)
        {
            blogicResult.ResultData = communicationResult.ResultData;
            blogicResult.ResultString = BLogicResult.SUCCESS;
        }

        /// <summary>
        /// リクエストヘッダの生成から通信の後処理までの間で捕捉されない例外が
        /// 発生した場合に呼ばれる例外ハンドラです。
        /// </summary>
        /// <remarks>
        /// 例外のハンドリングは以下のように行います。
        /// <list type="bullet">
        /// <item>
        ///     例外が <see cref="CommunicationException"/> の場合
        ///     <list type="bullet">
        ///     <item>
        ///     例外を捕捉してビジネスロジックを終了する。<see cref="BLogicResult"/> の 
        ///     <see cref="BLogicResult.ResultString"/> には <see cref="COMMUNICATION_EXCEPTION"/> を設定し、
        ///     <see cref="BLogicResult.Errors"/> には例外メッセージを格納する。
        ///     </item>
        ///     </list>
        /// </item>
        /// <item>
        ///     例外が <see cref="ServerException"/> で、<see cref="ServerException.ErrorType"/> が
        ///     <see cref="SERVER_EXCEPTION"/> 以外の場合
        ///     <list type="bullet">
        ///     <item>
        ///     例外を捕捉してビジネスロジックを終了する。
        ///     <see cref="BLogicResult"/> の <see cref="BLogicResult.ResultString"/> には、
        ///     サーバアプリケーションで入力値検証エラーが発生した場合には 
        ///     <see cref="Communication.ReceiverBase.SERVER_VALIDATE_EXCEPTION"/> を設定する。
        ///     その他、サーバアプリケーションからクライアントに通知すべきエラーが送られてきた場合は、
        ///     サーバアプリケーションから送信された <see cref="ServerException.ErrorType"/> を設定する。
        ///     <see cref="BLogicResult"/> の <see cref="BLogicResult.Errors"/> には、
        ///     <see cref="ServerException"/> の <see cref="ServerException.Errors"/> を格納する。
        ///     </item>
        ///     </list>
        /// </item>
        /// <item>
        ///     例外が <see cref="ServerException"/> で、<see cref="ServerException.ErrorType"/> が
        ///     <see cref="SERVER_EXCEPTION"/> の場合
        ///     <list type="bullet">
        ///     <item>
        ///     サーバ側で想定しない例外が発生したため、例外を捕捉しないで再スローする。
        ///     </item>
        ///     </list>
        /// </item>
        /// <item>
        ///     その他の例外の場合
        ///     <list type="bullet">
        ///     <item>
        ///     例外を捕捉しないで再スローする。
        ///     </item>
        ///     </list>
        /// </item>
        /// </list>
        /// </remarks>
        /// <param name="ex">
        /// リクエストヘッダの生成から通信の後処理までの間で捕捉されなかった例外。
        /// </param>
        /// <param name="blogicResult">
        /// ビジネスロジックの実行結果を格納する <see cref="BLogicResult"/>。
        /// </param>
        /// <returns>
        /// <paramref name="ex"/> を捕捉してビジネスロジックに処理を戻す場合は true。
        /// <paramref name="ex"/> を再スローする場合は false。
        /// </returns>
        protected virtual bool HandleException(Exception ex, BLogicResult blogicResult)
        {
            if (ex is CommunicationException)
            {
                MessageInfo messageInfo = new MessageInfo(COMMUNICATION_EXCEPTION, ex.Message);
                blogicResult.ResultString = COMMUNICATION_EXCEPTION;
                blogicResult.Errors.Add(messageInfo);
                return true;
            }

            ServerException serverException = ex as ServerException;
            if (serverException != null)
            {
                if (SERVER_EXCEPTION.Equals(serverException.ErrorType))
                {
                    // サーバで想定外の例外が発生時
                    return false;
                }
                else
                {
                    // サーバ入力値例外及びサーバ業務例外時
                    blogicResult.ResultString = serverException.ErrorType;
                    blogicResult.AddErrors(serverException.Errors);
                    return true;
                }
            }

            // その他の想定外の例外が発生時
            return false;
        }

        /// <summary>
        /// 通信処理をキャンセルします。
        /// </summary>
        public override void Cancel()
        {
            lock (_syncRoot)
            {
                _communicator.Cancel();
                Cancelled = true;
            }
        }

        /// <summary>
        /// <see cref="Communicator"/> からの進行状況イベントの通知を受け、
        /// <see cref="ClientBLogicBase.ProgressChanged"/> に登録されたイベントハンドラに対して通知します。
        /// </summary>
        /// <param name="sender">イベント発生元オブジェクト。</param>
        /// <param name="e">イベントデータを格納している <see cref="ExecuteProgressChangedEventArgs"/>。</param>
        protected virtual void DoCommunicateProgressChanged(object sender, ExecuteProgressChangedEventArgs e)
        {
            OnProgressChanged(e);
        }
    }
}
