// 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.Data;
using System.IO;
using System.Net.Mime;
using System.Reflection;
using System.Text;
using System.Web;
using System.Xml;
using TERASOLUNA.Fw.Common;
using TERASOLUNA.Fw.Common.BLogic;
using TERASOLUNA.Fw.Common.Logging;
using TERASOLUNA.Fw.Common.Validation;
using TERASOLUNA.Fw.Web.Configuration.ExceptionCode;

namespace TERASOLUNA.Fw.Web.Controller
{
    /// <summary>
    /// クライアントからのビジネスロジック実行要求を解決するコントローラクラスです。
    /// </summary>
    /// <remarks>
    /// <para>ファイルのアップロード、およびダウンロードを伴わないビジネスロジック実行要求を解決する
    /// コントローラクラスです。</para>
    /// </remarks>
    public class BLogicRequestController : IHttpHandler
    {
        /// <summary>
        /// <see cref="ILog"/> 実装クラスのインスタンスです。
        /// </summary>
        /// <remarks>
        /// ログ出力に利用します。
        /// </remarks>
        private static ILog _log = LogFactory.GetLogger(typeof(BLogicRequestController));

        /// <summary>
        /// ビジネスロジックの入力パラメータとなる <see cref="DataSet"/> を <see cref="HttpContext.Items"/> へ
        /// 設定/取得する際にキーとなる文字列です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "TERASOLUNA_InputParam" です。
        /// </remarks>
        protected static readonly string KEY_CONTEXT_ITEM_INPUTPARAM = "TERASOLUNA_InputParam";

        /// <summary>
        /// ビジネスロジックの結果オブジェクト <see cref="BLogicResult"/> を
        /// <see cref="HttpContext.Items"/> へ
        /// 設定/取得する際にキーとなる文字列です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "TERASOLUNA_BLogicResult" です。
        /// </remarks>
        protected static readonly string KEY_CONTEXT_ITEM_BLOGIC_RESULT = "TERASOLUNA_BLogicResult";

        /// <summary>
        /// 入力値検証の結果オブジェクト <see cref="ValidationResult"/> を <see cref="HttpContext.Items"/> へ
        /// 設定/取得する際にキーとなる文字列です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "TERASOLUNA_ValidationResult" です。
        /// </remarks>
        protected static readonly string KEY_CONTEXT_ITEM_VALIDATION_RESULT = "TERASOLUNA_ValidationResult";
        /// <summary>
        /// エラー種別ヘッダ名称です。 
        /// </summary>
        /// <remarks>
        /// この定数の値は "exception" です。
        /// </remarks>
        protected static readonly string EXCEPTION_HEADER_NAME = "exception";
        /// <summary>
        /// 入力値検証エラー時にエラー種別ヘッダに設定する値です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "validateException" です。
        /// </remarks>
        protected static readonly string EXCEPTION_VALIDATE = "validateException";
        /// <summary>
        /// システムエラー時にエラー種別ヘッダに設定する値。
        /// </summary>
        /// <remarks>
        /// この定数の値は "exception" です。
        /// </remarks>
        protected static readonly string EXCEPTION_SYSTEM = "exception";
        /// <summary>
        /// レスポンスのステータス文字列として設定する値です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "OK" です。
        /// </remarks>
        protected static readonly string STATUS_DESCRIPTION = "OK";
        /// <summary>
        /// 例外コード設定が未設定の場合にレスポンスのエラーコードに設定する既定値です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "E_UNKNOWN_EXCEPTION" です。
        /// </remarks>
        protected static readonly string UNKNOWN_EXCEPTION_ERROR_CODE = "E_UNKNOWN_EXCEPTION";
        /// <summary>
        /// 入力値検証ファイルパスのルートとなるディレクトリ名称です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "Bin" です。
        /// </remarks>
        protected static readonly string VALIDATION_FILE_PATH_ROOT = "Bin";

        /// <summary>
        /// 別の要求で <see cref="IHttpHandler"/> インスタンスを使用できるかどうかを示す値を取得します。
        /// </summary>
        /// <value>
        /// <see cref="IHttpHandler"/> インスタンスを使用できるかどうかを示す値。
        /// </value>
        /// <returns>
        /// 常に true を返却します。
        /// </returns>
        public virtual bool IsReusable
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// HTTP Web 要求を処理します。
        /// </summary>
        /// <param name="context"> 
        /// HTTP 要求を処理するために使用する、組み込みのサーバー オブジェクト
        /// (たとえば、 Request 、 Response 、 Session 、および Server )
        /// への参照を提供する <see cref="HttpContext"/> クラスのインスタンス。
        /// </param>
        /// <remarks>
        /// 次の順序でリクエスト処理を行います。
        /// <list type="number">
        /// <item>
        /// <see cref="ValidateRequestHeader"/> を呼び出し、ヘッダ検証を行います。
        /// </item>
        /// <item>
        /// <see cref="ParseRequestBody"/> を呼び出し、ボディの解析を行います。
        /// </item>
        /// <item>
        /// <see cref="ValidateInputData"/> を呼び出し、入力値の検証を行います。
        /// </item>
        /// <item>
        /// <see cref="ExecuteBLogic"/> を呼び出し、ビジネスロジックの実行を行います。
        /// </item>
        /// <item>
        /// <see cref="WriteSuccessResponse"/> を呼び出し、正常時のレスポンス書き込みを行います。
        /// </item>
        /// <item>
        /// <see cref="ValidateInputData"/> の戻り値が false であった場合、 <see cref="WriteValidationErrorResponse"/> を呼び出し、
        /// 入力値検証エラーのレスポンス書き込みを行います。
        /// </item>
        /// <item>
        /// <description>
        /// <see cref="ExecuteBLogic"/> が false であった場合、 <see cref="WriteBLogicErrorResponse"/> を呼び出し、
        /// 業務エラーのレスポンス書き込みを行います。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// 処理中に例外が発生した場合、 <see cref="WriteSystemErrorResponse"/> を呼び出し、
        /// システムエラーのレスポンス書き込みを行います。
        /// </description>
        /// </item>
        /// </list>
        /// </remarks>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public virtual void ProcessRequest(HttpContext context)
        {
            try
            {
                // ヘッダ検証
                ValidateRequestHeader(context);

                // ボディ解析
                ParseRequestBody(context);

                // 入力値検証
                if (!ValidateInputData(context))
                {
                    if (_log.IsDebugEnabled)
                    {
                        _log.Debug(Properties.Resources.D_VALIDATION_FAILED);
                    }

                    // 入力値検証エラーレスポンス書き込み
                    WriteValidationErrorResponse(context);
                }
                else
                {
                    // ビジネスロジック実行
                    if (!ExecuteBLogic(context))
                    {

                        // 業務エラーレスポンス書き込み
                        WriteBLogicErrorResponse(context);
                    }
                    else
                    {
                        // 正常レスポンス書き込み
                        WriteSuccessResponse(context);
                    }
                }
            }
            catch (Exception ex)
            {
                if (_log.IsWarnEnabled)
                {
                    _log.Warn(string.Format(Properties.Resources.W_CONTROLLER_EXCEPTION, this.GetType().FullName), ex);
                }

                // すべての例外についてシステムエラーレスポンスに書込
                WriteSystemErrorResponse(context, ex);
            }
        }

        /// <summary>
        /// HTTP リクエストのヘッダ設定値を検証します。
        /// </summary>
        /// <param name="context"> 
        /// HTTP 要求を処理するために使用する、組み込みのサーバー オブジェクト
        /// (たとえば、 Request 、 Response 、 Session 、および Server )
        /// への参照を提供する <see cref="HttpContext"/> クラスのインスタンス。
        /// </param>
        /// <remarks>
        /// <see cref="ValidateContentType"/> を呼び出し、
        /// <c>content-type</c> ヘッダ設定値を検証します。
        /// </remarks>
        /// <exception cref="InvalidRequestException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="context.Reqeust.ContentType"/> が null 参照です。
        /// </item>
        /// <item>
        /// <paramref name="context.Reqeust.ContentType"/> が空文字です。
        /// </item>
        /// <item>
        /// <paramref name="context.Reqeust.ContentType"/> のフォーマットが不正です。
        /// </item>
        /// <item>
        /// <paramref name="context.Reqeust.ContentType"/> のメディアタイプが <seealso name="MediaTypeNames.Text.Xml"/> と一致しません。
        /// </item>
        /// <item>
        /// <seealso name="MediaTypeNames.Text.Xml"/> が <c>text/xml</c> である場合に、 
        /// <paramref name="context.Reqeust.ContentType"/> の <c>charset</c> が <c>utf-8</c> ではありません。
        /// </item>
        /// </list>
        /// </exception>
        protected virtual void ValidateRequestHeader(HttpContext context)
        {
            ValidateContentType(context.Request.ContentType, MediaTypeNames.Text.Xml);
        }

        /// <summary>
        /// content-type ヘッダの検証を行います。
        /// </summary>
        /// <param name="requestContentType">content-type ヘッダ設定値。</param>
        /// <param name="expectedMediaType">期待するメディアタイプ。</param>
        /// <remarks>
        /// <paramref name="requestContentType"/> が 期待する値であるか検証を行います。
        /// 期待する値でなかった場合、例外をスローします。
        /// </remarks>
        /// <exception cref="InvalidRequestException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="requestContentType"/> が null 参照です。
        /// </item>
        /// <item>
        /// <paramref name="requestContentType"/> が空文字です。
        /// </item>
        /// <item>
        /// <paramref name="requestContentType"/> のフォーマットが不正です。
        /// </item>
        /// <item>
        /// <paramref name="requestContentType"/> のメディアタイプが 
        /// <paramref name="expectedMediaType"/> と一致しません。
        /// </item>
        /// <item>
        /// <paramref name="expectedMediaType"/> が <c>text/xml</c> である場合に、
        /// <paramref name="requestContentType"/> の <c>charset</c> が <c>utf-8</c> ではありません。
        /// </item>
        /// </list>
        /// </exception>
        protected virtual void ValidateContentType(string requestContentType, string expectedMediaType)
        {
            if (string.IsNullOrEmpty(requestContentType))
            {
                InvalidRequestException exception = new InvalidRequestException(string.Format(
                    Properties.Resources.E_REQUIRED_HEADER_EMPTY, "content-type"));
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }

            ContentType ct = null;
            try
            {
                ct = new ContentType(requestContentType);
            }
            catch (FormatException ex)
            {
                InvalidRequestException exception = new InvalidRequestException(string.Format(
                Properties.Resources.E_INVALID_CONTENTTYPE_FORMAT, requestContentType), ex);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }

            if (!expectedMediaType.Equals(ct.MediaType, StringComparison.OrdinalIgnoreCase))
            {
                InvalidRequestException exception = new InvalidRequestException(string.Format(
                Properties.Resources.E_INVALID_CONTENTTYPE_MEDIATYPE, requestContentType, expectedMediaType));
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }
            if (expectedMediaType.Equals(MediaTypeNames.Text.Xml, StringComparison.OrdinalIgnoreCase))
            {
                // MediaTypeがtext/xmlの場合はcharsetがUTF-8であるかチェックする。
                if (!Encoding.UTF8.WebName.Equals(ct.CharSet, StringComparison.OrdinalIgnoreCase))
                {
                    InvalidRequestException exception = new InvalidRequestException(string.Format(
                        Properties.Resources.E_INVALID_CONTENTTYPE_CHARSET_UTF8, ct.CharSet));
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error(exception.Message, exception);
                    }
                    throw exception;
                }
            }
        }

        /// <summary>
        /// HTTP リクエストのボディ部を解析します。
        /// </summary>
        /// <param name="context"> 
        /// HTTP 要求を処理するために使用する、組み込みのサーバー オブジェクト
        /// (たとえば、 Request 、 Response 、 Session 、および Server )
        /// への参照を提供する <see cref="HttpContext"/> クラスのインスタンス。
        /// </param>
        /// <remarks>
        /// <para>
        /// <see cref="HttpContext.Items"/> の <seealso cref="RequestControllerConstants.KEY_INPUT_DATASET_CLASS_TYPE"/> から
        /// ビジネスロジッククラスの入力として指定されている <see cref="DataSet"/> の <see cref="Type"/> を取得します。
        /// </para>
        /// <para>
        /// <see cref="CreateInputDataSet"/> を呼び出し、指定された <see cref="DataSet"/> の
        /// インスタンスを生成および、リクエストの入力ストリームから内容の読み込みを行います。
        /// </para>
        /// <para>
        /// <see cref="HttpContext.Items"/> の <see cref="KEY_CONTEXT_ITEM_INPUTPARAM"/> へ 
        /// 生成した <see cref="DataSet"/> を格納します。
        /// </para>
        /// </remarks>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="HttpContext.Items"/> の <seealso cref="RequestControllerConstants.KEY_INPUT_DATASET_CLASS_TYPE"/> 
        /// が設定されていません。
        /// </item>
        /// <item>
        /// <paramref name="HttpContext.Items"/> の <seealso cref="RequestControllerConstants.KEY_INPUT_DATASET_CLASS_TYPE"/> が 
        /// <see cref="Type"/> ではありません。
        /// </item>
        /// <item>
        /// <paramref name="HttpContext.Items"/> の <seealso cref="RequestControllerConstants.KEY_INPUT_DATASET_CLASS_TYPE"/> に指定された型のクラスのコンストラクタが不正です。
        /// </item>
        /// <item>
        /// <paramref name="HttpContext.Items"/> の <seealso cref="RequestControllerConstants.KEY_INPUT_DATASET_CLASS_TYPE"/> に指定された型のクラスが <see cref="DataSet"/> の
        /// 派生クラスではありません。
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="InvalidRequestException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="context.Request.InputStream"/> に設定されているデータがXMLではありません。
        /// </item>
        /// <item>
        /// <paramref name="context.Request.InputStream"/> に設定されているデータが
        /// 生成対象の <see cref="DataSet"/> のスキーマと異なっています。
        /// </item>
        /// </list>
        /// </exception>
        protected virtual void ParseRequestBody(HttpContext context)
        {
            Type dataSetType = context.Items[RequestControllerConstants.KEY_INPUT_DATASET_CLASS_TYPE] as Type;
            if (dataSetType == null)
            {
                // 入力データセットタイプが未設定の場合
                TerasolunaException exception = new TerasolunaException(Properties.Resources.E_EMPTY_INPUT_DATASET_TYPE);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }
            Stream inputStream = context.Request.InputStream;
            DataSet inputParam = CreateInputDataSet(dataSetType, inputStream);
            context.Items[KEY_CONTEXT_ITEM_INPUTPARAM] = inputParam;
        }

        /// <summary>
        /// リクエストの入力ストリームを元に、ビジネスロジックの入力値となるデータセットを生成します。
        /// </summary>
        /// <param name="dataSetType">生成する <see cref="DataSet"/> の <see cref="Type"/> 。</param>
        /// <param name="inputStream">HTTPリクエストの入力ストリーム。</param>
        /// <returns>生成された <see cref="DataSet"/> のインスタンス。</returns>
        /// <remarks>
        /// <para>
        /// <paramref name="dataSetType"/>に指定された <see cref="Type"/> の
        /// インスタンスを生成し、 <paramref name="inputStream"/> から内容を読み込みます。
        /// </para>
        /// <para>
        /// 対象の <see cref="DataSet"/> に存在しないカラムやテーブルの情報がリクエストの
        /// 入力ストリームに存在する場合、その値は無視されます。
        /// </para>
        /// </remarks>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="dataSetType"/> に指定されたクラスのコンストラクタが不正です。
        /// </item>
        /// <item>
        /// <paramref name="dataSetType"/> に指定されたクラスが <see cref="DataSet"/> の
        /// 派生クラスではありません。
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="InvalidRequestException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="inputStream"/> に設定されているデータがXMLではありません。
        /// </item>
        /// <item>
        /// <paramref name="inputStream"/> に設定されているデータが
        /// 生成対象の <see cref="DataSet"/> のスキーマと異なっています。
        /// </item>
        /// </list>
        /// </exception>
        protected virtual DataSet CreateInputDataSet(Type dataSetType, Stream inputStream)
        {
            DataSet inputDs = null;

            try
            {
                inputDs = Activator.CreateInstance(dataSetType) as DataSet;
            }
            catch (MemberAccessException mae)
            {
                TerasolunaException exception = new TerasolunaException(string.Format(
                    Properties.Resources.E_INVALID_CONSTRUCTOR, dataSetType.FullName), mae);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }
            catch (TargetInvocationException tie)
            {
                TerasolunaException exception = new TerasolunaException(string.Format(
                    Properties.Resources.E_INVALID_CONSTRUCTOR, dataSetType.FullName), tie);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }

            if (inputDs == null)
            {
                TerasolunaException exception = new TerasolunaException(string.Format(
                    Properties.Resources.E_INVALID_BASE_CLASS, dataSetType.FullName, typeof(DataSet).FullName));
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }

            try
            {
                inputDs.ReadXml(inputStream);
            }
            catch (XmlException ex)
            {
                InvalidRequestException exception = new InvalidRequestException(Properties.Resources.E_INVALID_REQUEST + ex.Message, ex);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }
            catch (FormatException fe)
            {
                InvalidRequestException exception = new InvalidRequestException(Properties.Resources.E_INVALID_REQUEST + fe.Message, fe);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }

            return inputDs;
        }

        /// <summary>
        /// 入力値の検証を行います。
        /// </summary>
        /// <param name="context"> 
        /// HTTP 要求を処理するために使用する、組み込みのサーバー オブジェクト
        /// (たとえば、 Request 、 Response 、 Session 、および Server )
        /// への参照を提供する <see cref="HttpContext"/> クラスのインスタンス。
        /// </param>
        /// <returns>
        /// 検証結果がエラーであった場合は false 、それ以外の場合は true を返却します。
        /// </returns>
        /// <remarks>
        /// <para>
        /// <see cref="HttpContext.Items"/> の 
        /// <see cref="KEY_CONTEXT_ITEM_INPUTPARAM"/> から
        /// ビジネスロジッククラスの入力として指定されている <see cref="DataSet"/> の 
        /// <see cref="Type"/> を対象として入力値の検証を行い、
        /// 検証結果である <see cref="ValidationResult"/> オブジェクトを <see cref="HttpContext.Items"/> の 
        /// <see cref="KEY_CONTEXT_ITEM_VALIDATION_RESULT"/> へ格納します。
        /// </para>
        /// <para>
        /// <see cref="HttpContext.Items"/> の 
        /// <see cref="RequestControllerConstants.KEY_VALIDATION_FILE_PATH"/> に指定されたファイルをスキーマとし、 
        /// <see cref="ValidatorFactory"/> で取得される <see cref="IValidator"/> インタフェース実装クラスを
        /// 利用して検証を行います。
        /// 既定では、<see cref="VabValidator"/> クラスが利用されます。
        /// </para>
        /// <para>
        /// <see cref="HttpContext.Items"/> の <see cref="RequestControllerConstants.KEY_VALIDATION_FILE_PATH"/> が未設定の場合は、
        /// 入力値検証を行わず、常に true を返却します。
        /// </para>
        /// <para>
        /// 入力値検証に <see cref="VabValidator"/> クラス以外を利用する方法は、 
        /// <see cref="ValidatorFactory.CreateValidator"/> の設定例を参照してください。 
        /// </para>
        /// </remarks>
        /// <exception cref="ArgumentException">
        /// 設定ファイルに定義している <see cref="IValidator"/> クラスは抽象クラスです。
        /// </exception>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <see cref="HttpContext.Items"/> の 
        /// <see cref="RequestControllerConstants.KEY_VALIDATION_FILE_PATH"/> にファイルパスの上限文字数を超える
        /// 長さの文字列が設定されています。
        /// </item>
        /// <item>
        /// <see cref="HttpContext.Items"/> の 
        /// <see cref="RequestControllerConstants.KEY_VALIDATION_FILE_PATH"/> にファイル名称として
        /// 利用できない文字が設定されています。
        /// </item>
        /// <item>
        /// <see cref="HttpContext.Items"/> の 
        /// <see cref="RequestControllerConstants.KEY_VALIDATION_FILE_PATH"/> に、アプリケーションルート
        /// 配下のパスでない値が設定されています。
        /// </item>
        /// <item>
        /// <see cref="HttpContext.Items"/> の 
        /// <see cref="RequestControllerConstants.KEY_VALIDATION_FILE_PATH"/> に、指定されたファイルが
        /// 見つかりません。
        /// </item>
        /// <item>
        /// <see cref="IValidator"/> を生成できません。
        /// </item>
        /// </list>
        /// </exception>
        protected virtual bool ValidateInputData(HttpContext context)
        {
            string validationFilePath = context.Items[RequestControllerConstants.KEY_VALIDATION_FILE_PATH] as string;
            
            //ValidationInfo属性のvalidationFilePathが設定されている場合のみチェックを行います。
            if (validationFilePath != null)
            {
                // 相対パスを絶対パスへ変換。
                string absolutePath = null;
                try
                {
                    absolutePath = context.Server.MapPath(context.Request.ApplicationPath + "\\" + VALIDATION_FILE_PATH_ROOT + "\\" + validationFilePath);
                }
                catch(HttpException he)
                {
                    TerasolunaException exception = new TerasolunaException(string.Format(
                        Properties.Resources.E_INVALID_VALIDATION_FILE_PATH, validationFilePath), he);
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error(exception.Message, exception);
                    }
                    throw exception;
                }

                if (!File.Exists(absolutePath))
                {
                    // 入力値検証ファイルが見つからない場合など、入力値検証機能がTerasolunaExceptionをスローするが、
                    // 絶対パスがメッセージとして設定されている。
                    // 例外メッセージはクライアントに流れてしまう可能性があるため、ここでファイル存在確認し、
                    // メッセージを置き換えた、TerasolunaExceptionをスローする。
                    TerasolunaException exception = new TerasolunaException(string.Format(
                        Properties.Resources.E_VALIDATION_FILE_NOT_FOUND, absolutePath));
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error(exception.Message, exception);
                    }
                    throw exception;
                }

                string ruleSet = context.Items[RequestControllerConstants.KEY_VALIDATION_RULESET] as string;
                if (string.IsNullOrEmpty(ruleSet))
                {
                    ruleSet = "Default";
                }

                DataSet inputDataSet = context.Items[KEY_CONTEXT_ITEM_INPUTPARAM] as DataSet;

                // validatorの生成・初期化
                IValidator validator = ValidatorFactory.CreateValidator();
                validator.ValidationFilePath = absolutePath;
                validator.RuleSet = ruleSet;

                // 検証の実施
                ValidationResult result = validator.Validate(inputDataSet);
                
                if (!result.IsValid)
                {
                    context.Items[KEY_CONTEXT_ITEM_VALIDATION_RESULT] = result;
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 入力値検証エラー時のレスポンス情報を書き込みます。
        /// </summary>
        /// <param name="context"> 
        /// HTTP 要求を処理するために使用する、組み込みのサーバー オブジェクト
        /// (たとえば、 Request 、 Response 、 Session 、および Server )
        /// への参照を提供する <see cref="HttpContext"/> クラスのインスタンス。
        /// </param>
        /// <remarks>
        /// <para>
        /// <see cref="WriteValidationErrorResponseHeader"/> を呼び出し、入力値検証エラー時の
        /// レスポンスのヘッダ情報を設定します。
        /// </para>
        /// <para>
        /// <see cref="WriteValidationErrorResponseBody"/> を呼び出し、入力値検証エラー時の
        /// レスポンスデータを <see cref="HttpResponse.OutputStream"/> に書き込みます。
        /// </para>
        /// </remarks>
        protected virtual void WriteValidationErrorResponse(HttpContext context)
        {
            // レスポンスヘッダ設定
            WriteValidationErrorResponseHeader(context.Response);

            // レスポンスボディ書き込み
            ValidationResult result = context.Items[KEY_CONTEXT_ITEM_VALIDATION_RESULT] as ValidationResult;
            WriteValidationErrorResponseBody(context.Response, result);
        }

        /// <summary>
        /// 入力値検証エラー時のレスポンスヘッダ情報を設定します。
        /// </summary>
        /// <param name="response">ヘッダの設定対象となる <see cref="HttpResponse"/> 。</param>
        /// <remarks>
        /// 次の項目をヘッダに設定します。
        /// <list type="table">
        /// <listheader>
        /// <term>
        /// ヘッダ名称
        /// </term>
        /// <description>
        /// 設定値
        /// </description>
        /// </listheader>
        /// <item>
        /// <term>
        /// content-type
        /// </term>
        /// <description>
        /// text/xml; charset=utf-8
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// exception
        /// </term>
        /// <description>
        /// validateException
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// ステータス文字列
        /// </term>
        /// <description>
        /// OK
        /// </description>
        /// </item>
        /// </list>
        /// </remarks>
        protected virtual void WriteValidationErrorResponseHeader(HttpResponse response)
        {
            // content-type
            ContentType ct = new ContentType();
            ct.MediaType = MediaTypeNames.Text.Xml;
            ct.CharSet = Encoding.UTF8.WebName;
            response.ContentType = ct.ToString();
            // エラー種別
            response.AddHeader(EXCEPTION_HEADER_NAME, EXCEPTION_VALIDATE);
            // ステータス文字列
            response.StatusDescription = STATUS_DESCRIPTION;
        }

        /// <summary>
        /// 入力値検証エラー時のレスポンスデータをレスポンスボディに書き込みます。
        /// </summary>
        /// <param name="response">書き込み対象となる <see cref="HttpResponse"/>。</param>
        /// <param name="validationResult">入力値検証結果オブジェクト。</param>
        /// <remarks>
        /// <paramref name="validationResult"/> から入力値検証エラー時のレスポンスXMLデータを作成し、
        /// <paramref name="response"/> の 出力ストリームに書き込みます。
        /// <para>次にに示す電文フォーマットでXMLデータが作成されます。</para>
        /// <code>
        /// <![CDATA[
        /// <errors>
        ///   <error>
        ///     <replace-values></replace-value>
        ///     <error-message></error-message>
        ///     <error-code></error-code>
        ///     <error-field></error-field>
        ///   </error>
        /// </errors>
        /// ]]>
        /// </code>
        /// <list type="table">
        /// <listheader>
        /// <term>
        /// 要素名
        /// </term>
        /// <description>
        /// 説明
        /// </description>
        /// </listheader>
        /// <item>
        /// <term>
        /// errors
        /// </term>
        /// <description>
        /// ルートノードです。
        /// 必ず設定されます。
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// error
        /// </term>
        /// <description>
        /// エラー情報です。errors要素の直下に設定されます。
        /// <see cref="ValidationResult.Errors"/> の件数だけ、繰り返し設定されます。
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// replace-values
        /// </term>
        /// <description>
        /// error-message要素の設定値で置換されて設定された文字列を設定します。
        /// error要素の直下に設定されます。
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// error-message
        /// </term>
        /// <description>
        /// 入力値検証エラーのメッセージです。
        /// error要素の直下に1件のみ設定されます。
        /// <see cref="MessageInfo.Message"/> で取得した値が設定されます。
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// error-code
        /// </term>
        /// <description>
        /// 入力値検証エラーの種類を表す識別子を指定します。
        /// error要素の直下に1件のみ設定されます。
        /// <see cref="MessageInfo.Key"/> で取得した値が設定されます。
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// error-field
        /// </term>
        /// <description>
        /// 入力値検証エラーが発生した箇所を設定します。
        /// error要素の直下に1件のみ設定されます。
        /// <see cref="ValidationMessageInfo.ErrorPath"/> で取得した値が設定されます。
        /// </description>
        /// </item>
        /// </list>
        /// </remarks>
        protected virtual void WriteValidationErrorResponseBody(HttpResponse response, ValidationResult validationResult)
        {
            XmlDocument responseXmlDoc = CreateInitializeErrorDocument();

            XmlElement rootElement = responseXmlDoc.DocumentElement;
            IList<MessageInfo> errMessageList = validationResult.Errors;
            foreach (MessageInfo msgInfo in errMessageList)
            {
                ValidationMessageInfo validationMsgInfo = msgInfo as ValidationMessageInfo;

                XmlElement errElement = responseXmlDoc.CreateElement("error");
                XmlElement msgElement = responseXmlDoc.CreateElement("error-message");
                msgElement.InnerText = validationMsgInfo.Message;
                XmlElement codeElement = responseXmlDoc.CreateElement("error-code");
                codeElement.InnerText = validationMsgInfo.Key;
                XmlElement fieldElement = responseXmlDoc.CreateElement("error-field");
                fieldElement.InnerText = validationMsgInfo.ErrorPath;
                errElement.AppendChild(msgElement);
                errElement.AppendChild(codeElement);
                errElement.AppendChild(fieldElement);
                rootElement.AppendChild(errElement);
            }

            responseXmlDoc.Save(response.OutputStream);
        }

        /// <summary>
        /// ビジネスロジックを実行します。
        /// </summary>
        /// <param name="context"> 
        /// HTTP 要求を処理するために使用する、組み込みのサーバー オブジェクト
        /// (たとえば、Request、Response、Session、および Server)
        /// への参照を提供する <see cref="HttpContext"/> クラスのインスタンス。
        /// </param>
        /// <returns>
        /// ビジネスロジックが正常終了した場合 true 、
        /// 業務エラーが発生した場合 false を返却します。
        /// </returns>
        /// <remarks>
        /// <para>
        /// <seealso cref="HttpContext.Items"/> の 
        /// <seealso cref="RequestControllerConstants.KEY_BLOGIC_CLASS_TYPE"/> から取得した、 
        /// <see cref="Type"/> の <see cref="IBLogic"/> インタフェース実装クラスのインスタンスを
        /// <see cref="BLogicFactory"/> を利用して生成し、
        /// 生成したインスタンスの <seealso cref="IBLogic.Execute"/> メソッドに
        /// <seealso cref="HttpContext.Items"/> の 
        /// <seealso cref="RequestControllerConstants.KEY_BLOGIC_CLASS_TYPE"/> 
        /// から取得した<see cref="DataSet"/> を引数に指定して実行します。
        /// </para>
        /// <para>
        /// 返却されたビジネスロジック結果オブジェクト <see cref="BLogicResult"/> を
        /// <seealso cref="HttpContext.Items"/> の 
        /// <seealso cref="KEY_CONTEXT_ITEM_BLOGIC_RESULT"/> に格納します。
        /// </para>
        /// </remarks>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <see cref="IBLogic"/> 実装クラスを生成できません。
        /// </item>
        /// <item>
        /// <seealso cref="IBLogic.Execute"/> メソッドの返却値が null 参照です。
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="ConfigurationErrorsException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// 構成ファイルを読み込むことができません。
        /// </item>
        /// <item>
        /// 構成ファイルが不正です。
        /// </item>
        /// </list>
        /// </exception>
        protected virtual bool ExecuteBLogic(HttpContext context)
        {
            string requestName = context.Items[RequestControllerConstants.KEY_REQUEST_NAME] as string;
            IBLogic blogic = BLogicFactory.CreateBLogic(requestName);
            DataSet inputDs = context.Items[KEY_CONTEXT_ITEM_INPUTPARAM] as DataSet;

            if (_log.IsDebugEnabled)
            {
                _log.Debug(string.Format(
                    Properties.Resources.D_BEGIN_BLOGIC_FUNCTION, blogic.GetType().FullName));
            }

            BLogicParam blogicParam = new BLogicParam();
            blogicParam.ParamData = inputDs;
            BLogicResult result = blogic.Execute(blogicParam);

            if (_log.IsDebugEnabled)
            {
                _log.Debug(string.Format(
                    Properties.Resources.D_END_BLOGIC_FUNCTION, blogic.GetType().FullName));
            }

            if (result == null)
            {
                // 結果がnullの場合は電文を返せないので例外とする。
                TerasolunaException exception = new TerasolunaException(Properties.Resources.E_EMPTY_BLOGIC_RESULT);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }

            context.Items[KEY_CONTEXT_ITEM_BLOGIC_RESULT] = result;

            if (result.Errors.Count > 0)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// 業務エラー時のレスポンス情報を書き込みます。
        /// </summary>
        /// <param name="context"> 
        /// HTTP 要求を処理するために使用する、組み込みのサーバー オブジェクト
        /// (たとえば、Request、Response、Session、および Server)
        /// への参照を提供する <see cref="HttpContext"/> クラスのインスタンス。
        /// </param>
        /// <remarks>
        /// <para>
        /// <see cref="WriteBLogicErrorResponseHeader"/>を呼び出し、業務エラー時の
        /// レスポンスのヘッダ情報を設定します。
        /// </para>
        /// <para>
        /// <see cref="WriteBLogicErrorResponseBody"/>を呼び出し、業務エラー時の
        /// レスポンスデータを <see cref="HttpResponse.OutputStream"/> に書き込みます。
        /// </para>
        /// </remarks>
        protected virtual void WriteBLogicErrorResponse(HttpContext context)
        {
            BLogicResult result = context.Items[KEY_CONTEXT_ITEM_BLOGIC_RESULT] as BLogicResult;
            WriteBLogicErrorResponseHeader(context.Response, result.ResultString);
            WriteBLogicErrorResponseBody(context.Response, result);
        }

        /// <summary>
        /// 業務エラー時のレスポンスヘッダ情報を設定します。
        /// </summary>
        /// <param name="response">ヘッダの設定対象となる <see cref="HttpResponse"/>。</param>
        /// <param name="errorType">エラー種別。</param>
        /// <remarks>
        /// 次の項目をヘッダに設定します。
        /// <list type="table">
        /// <listheader>
        /// <term>
        /// ヘッダ名称
        /// </term>
        /// <description>
        /// 設定値
        /// </description>
        /// </listheader>
        /// <item>
        /// <term>
        /// content-type
        /// </term>
        /// <description>
        /// text/xml; charset=utf-8
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// exception
        /// </term>
        /// <description>
        /// <paramref name="errorType"/> に指定された値を設定します。
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// ステータス文字列
        /// </term>
        /// <description>
        /// OK
        /// </description>
        /// </item>
        /// </list>
        /// </remarks>
        /// <exception cref="TerasolunaException">
        /// ビジネスロジックでエラーが設定されていますが、ResultStringにエラー種別が設定されていません。
        /// </exception>
        protected virtual void WriteBLogicErrorResponseHeader(HttpResponse response, string errorType)
        {
            if (string.IsNullOrEmpty(errorType))
            {
                // ResultStringがnullまたは空文字列の場合は電文を返せないので例外とする。
                TerasolunaException exception = new TerasolunaException(Properties.Resources.E_NOT_FOUND_RESULT_STRING);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }

            // content-type
            ContentType ct = new ContentType();
            ct.MediaType = MediaTypeNames.Text.Xml;
            ct.CharSet = Encoding.UTF8.WebName;
            response.ContentType = ct.ToString();
            // エラー種別
            response.AddHeader(EXCEPTION_HEADER_NAME, errorType);
            // ステータス文字列
            response.StatusDescription = STATUS_DESCRIPTION;
        }

        /// <summary>
        /// 業務エラー時のレスポンスデータをレスポンスボディに書き込みます。
        /// </summary>
        /// <param name="response">書き込み対象となる <see cref="HttpResponse"/> 。</param>
        /// <param name="result">ビジネスロジック結果オブジェクト。</param>
        /// <remarks>
        /// <paramref name="result"/> から業務エラー時のレスポンス XML データを作成し、
        /// <paramref name="response"/> の 出力ストリームに書き込みます。
        /// <para>次にに示す電文フォーマットで XML データが作成されます。</para>
        /// <code>
        /// <![CDATA[
        /// <errors>
        ///   <error>
        ///     <replace-values></replace-value>
        ///     <error-message></error-message>
        ///     <error-code></error-code>
        ///   </error>
        /// </errors>
        /// ]]>
        /// </code>
        /// <list type="table">
        /// <listheader>
        /// <term>
        /// 要素名
        /// </term>
        /// <description>
        /// 説明
        /// </description>
        /// </listheader>
        /// <item>
        /// <term>
        /// errors
        /// </term>
        /// <description>
        /// ルートノードです。
        /// 必ず設定されます。
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// error
        /// </term>
        /// <description>
        /// エラー情報です。errors 要素の直下に設定されます。
        /// <see cref="BLogicResult.Errors"/> の件数だけ、繰り返し設定されます。
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// error-message
        /// </term>
        /// <description>
        /// 業務エラーのメッセージです。
        /// error 要素の直下に1件のみ設定されます。
        /// <see cref="MessageInfo.Message"/> で取得した値が設定されます。
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// error-code
        /// </term>
        /// <description>
        /// 業務エラーの種類を表す識別子を指定します。
        /// error要素の直下に1件のみ設定されます。
        /// <see cref="MessageInfo.Key"/> で取得した値が設定されます。
        /// </description>
        /// </item>
        /// </list>
        /// </remarks>
        protected virtual void WriteBLogicErrorResponseBody(HttpResponse response, BLogicResult result)
        {
            XmlDocument responseXmlDoc = CreateInitializeErrorDocument();
            XmlElement rootElement = responseXmlDoc.DocumentElement;
            IList<MessageInfo> errMessageList = result.Errors;
            foreach (MessageInfo msgInfo in errMessageList)
            {
                XmlElement errElement = responseXmlDoc.CreateElement("error");
                XmlElement msgElement = responseXmlDoc.CreateElement("error-message");
                msgElement.InnerText = msgInfo.Message;
                XmlElement codeElement = responseXmlDoc.CreateElement("error-code");
                codeElement.InnerText = msgInfo.Key;
                errElement.AppendChild(msgElement);
                errElement.AppendChild(codeElement);
                rootElement.AppendChild(errElement);
            }

            responseXmlDoc.Save(response.OutputStream);
        }

        /// <summary>
        /// 正常終了時のレスポンス情報を書き込みます。
        /// </summary>
        /// <param name="context"> 
        /// HTTP 要求を処理するために使用する、組み込みのサーバー オブジェクト
        /// (たとえば、Request、Response、Session、および Server)
        /// への参照を提供する <see cref="HttpContext"/> クラスのインスタンス。
        /// </param>
        /// <remarks>
        /// <para>
        /// <see cref="WriteSuccessXmlResponseHeader"/>を呼び出し、正常終了時の
        /// レスポンスのヘッダ情報を設定します。
        /// </para>
        /// <para>
        /// <see cref="WriteSuccessXmlResponseBody"/>を呼び出し、正常終了時の
        /// レスポンスデータを <see cref="HttpResponse.OutputStream"/> に書き込みます。
        /// </para>
        /// </remarks>
        /// <exception cref="TerasolunaException">
        /// <see cref="HttpContext.Items"/> の <see cref="KEY_CONTEXT_ITEM_BLOGIC_RESULT"/> に設定された、 
        /// <see cref="BLogicResult.ResultData"/> が null 参照です。
        /// </exception>
        protected virtual void WriteSuccessResponse(HttpContext context)
        {
            // レスポンスデータの設定確認。
            BLogicResult result = context.Items[KEY_CONTEXT_ITEM_BLOGIC_RESULT] as BLogicResult;
            if (result.ResultData == null)
            {
                TerasolunaException exception = new TerasolunaException(string.Format(
                    Properties.Resources.E_EMPTY_RESULT_DATA, "ResultData"));
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }
            // レスポンスヘッダ設定
            WriteSuccessXmlResponseHeader(context.Response);

            // レスポンスボディ書き込み
            WriteSuccessXmlResponseBody(context.Response, result.ResultData);            
        }

        /// <summary>
        /// レスポンスに正常終了時のヘッダ情報を設定します。
        /// </summary>
        /// <param name="response">ヘッダの設定対象となる <see cref="HttpResponse"/> 。</param>
        /// <remarks>
        /// 次の項目をヘッダに設定します。
        /// <list type="table">
        /// <listheader>
        /// <term>
        /// ヘッダ名称
        /// </term>
        /// <description>
        /// 設定値
        /// </description>
        /// </listheader>
        /// <item>
        /// <term>
        /// content-type
        /// </term>
        /// <description>
        /// text/xml; charset=utf-8
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// ステータス文字列
        /// </term>
        /// <description>
        /// OK
        /// </description>
        /// </item>
        /// </list>
        /// </remarks>
        /// <exception cref="HttpException">
        /// HTTP ヘッダーが送信された後に、 StatusDescription が設定されています。
        /// </exception>
        protected virtual void WriteSuccessXmlResponseHeader(HttpResponse response)
        {
            // content-type
            ContentType ct = new ContentType();
            ct.MediaType = MediaTypeNames.Text.Xml;
            ct.CharSet = Encoding.UTF8.WebName;
            response.ContentType = ct.ToString();
            // ステータス文字列
            response.StatusDescription = STATUS_DESCRIPTION;
        }

        /// <summary>
        /// レスポンスの出力ストリームに正常終了時の XML 電文を設定します。
        /// </summary>
        /// <param name="response">書き込み対象となる <see cref="HttpResponse"/> 。</param>
        /// <param name="resultDataSet">ビジネスロジックから返却された結果データ。</param>
        /// <remarks>
        /// <para>
        /// <paramref name="resultDataSet"/> のデータを XML フォーマットで、 
        /// <paramref name="response"/> の 出力ストリームに書き込みます。
        /// </para>
        /// </remarks>
        protected virtual void WriteSuccessXmlResponseBody(HttpResponse response, DataSet resultDataSet)
        {
            resultDataSet.WriteXml(response.OutputStream);
        }

        /// <summary>
        /// システムエラー時のレスポンス情報の書き込みを行います。
        /// </summary>
        /// <param name="context"> 
        /// HTTP 要求を処理するために使用する、組み込みのサーバー オブジェクト
        /// (たとえば、 Reques t、 Response 、 Session 、および Server )
        /// への参照を提供する <see cref="HttpContext"/> クラスのインスタンス。
        /// </param>
        /// <param name="ex">発生した例外オブジェクト。</param>
        /// <remarks>
        /// <para>
        /// <see cref="WriteSystemErrorResponseHeader"/> を呼び出し、システムエラー時の
        /// レスポンスのヘッダ情報を設定します。
        /// </para>
        /// <para>
        /// <see cref="WriteSystemErrorResponseBody"/> を呼び出し、システムエラー時の
        /// レスポンスデータを <see cref="HttpResponse.OutputStream"/> に書き込みます。
        /// </para>
        /// </remarks>
        protected virtual void WriteSystemErrorResponse(HttpContext context, System.Exception ex)
        {
            WriteSystemErrorResponseHeader(context.Response);
            WriteSystemErrorResponseBody(context.Response, ex);
        }

        /// <summary>
        /// システムエラー時のレスポンスヘッダ情報の書き込みを行います。
        /// </summary>
        /// <param name="response">ヘッダの設定対象となる <see cref="HttpResponse"/> 。</param>
        /// <remarks>
        /// 次の項目をヘッダに設定します。
        /// <list type="table">
        /// <listheader>
        /// <term>
        /// ヘッダ名称
        /// </term>
        /// <description>
        /// 設定値
        /// </description>
        /// </listheader>
        /// <item>
        /// <term>
        /// content-type
        /// </term>
        /// <description>
        /// text/xml; charset=utf-8
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// exception
        /// </term>
        /// <description>
        /// exception
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// ステータス文字列
        /// </term>
        /// <description>
        /// OK
        /// </description>
        /// </item>
        /// </list>
        /// </remarks>
        protected virtual void WriteSystemErrorResponseHeader(HttpResponse response)
        {
            // content-type
            ContentType ct = new ContentType();
            ct.MediaType = MediaTypeNames.Text.Xml;
            ct.CharSet = Encoding.UTF8.WebName;
            response.ContentType = ct.ToString();
            // エラー種別
            response.AddHeader(EXCEPTION_HEADER_NAME, EXCEPTION_SYSTEM);
            // ステータス文字列
            response.StatusDescription = STATUS_DESCRIPTION;
        }

        /// <summary>
        /// システムエラー時のレスポンスボディ情報の書き込みを行います。
        /// </summary>
        /// <param name="response">書き込み対象となる <see cref="HttpResponse"/> 。</param>
        /// <param name="ex">発生例外オブジェクト。</param>
        /// <remarks>
        /// <paramref name="ex"/> からシステムエラー時のレスポンス XML データを作成し、
        /// <paramref name="response"/> の 出力ストリームに書き込みます。
        /// <para>次にに示す電文フォーマットで XML データが作成されます。</para>
        /// <code>
        /// <![CDATA[
        /// <errors>
        ///   <error>
        ///     <error-message></error-message>
        ///     <error-code></error-code>
        ///   </error>
        /// </errors>
        /// ]]>
        /// </code>
        /// <list type="table">
        /// <listheader>
        /// <term>
        /// 要素名
        /// </term>
        /// <description>
        /// 説明
        /// </description>
        /// </listheader>
        /// <item>
        /// <term>
        /// errors
        /// </term>
        /// <description>
        /// ルートノードです。
        /// 必ず設定されます。
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// error
        /// </term>
        /// <description>
        /// エラー情報です。errors要素の直下に設定されます。
        /// <see cref="BLogicResult.Errors"/> の件数だけ、繰り返し設定されます。
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// error-message
        /// </term>
        /// <description>
        /// システムエラーのメッセージです。
        /// error要素の直下に1件のみ設定されます。
        /// <see cref="Exception.Message"/> で取得した値が設定されます。
        /// <para>
        /// ただし、例外コード取得時に <see cref="ConfigurationErrorsException"/> が発生した場合は補足し、
        /// 補足した例外の <see cref="Exception.Message"/> が設定されます。
        /// </para>
        /// </description>
        /// </item>
        /// <item>
        /// <term>
        /// error-code
        /// </term>
        /// <description>
        /// <para>エラーの種類を表す識別子を指定します。</para>
        /// <para>この要素は、error要素の直下に1件のみ設定されます。</para>
        /// <para>設定値は、例外コード設定ファイルに設定されている <paramref name="ex"/> に指定された例外に該当する
        /// 例外コードが設定されます。</para>
        /// <para>設定が存在しない、取得に時に例外が発生した場合は既定値として、
        /// <see cref="UNKNOWN_EXCEPTION_ERROR_CODE"/> を設定します。</para>
        /// </description>
        /// </item>
        /// </list>
        /// </remarks>
        protected virtual void WriteSystemErrorResponseBody(HttpResponse response, Exception ex)
        {
            Exception targetEx = ex;

            // 例外コードを取得する
            string errCode = null;
            try
            {
                errCode = ExceptionCodeConfiguration.GetExceptionCode(ex);
            }
            catch (ConfigurationErrorsException cee)
            {
                if (_log.IsWarnEnabled)
                {
                    _log.Warn(Properties.Resources.W_CONTROLLER_ERROR_CODE_NOT_FOUND, cee);
                }

                // 例外コード設定が不正な場合は設定ファイル不正例外を
                // 対象に置き換える。
                targetEx = cee;
            }
            if (errCode == null)
            {
                // 対象例外のエラーコードが未設定であった場合は
                // 例外不明エラーコードを設定する。
                errCode = UNKNOWN_EXCEPTION_ERROR_CODE;
            }

            XmlDocument responseXmlDoc = CreateInitializeErrorDocument();
            XmlElement rootElement = responseXmlDoc.DocumentElement;
            XmlElement errElement = responseXmlDoc.CreateElement("error");
            XmlElement msgElement = responseXmlDoc.CreateElement("error-message");
            msgElement.InnerText = targetEx.Message;
            XmlElement codeElement = responseXmlDoc.CreateElement("error-code");
            codeElement.InnerText = errCode;
            errElement.AppendChild(msgElement);
            errElement.AppendChild(codeElement);
            rootElement.AppendChild(errElement);

            responseXmlDoc.Save(response.OutputStream);
        }

        /// <summary>
        /// 異常終了時の初期化されたXmlDocumentを生成します。
        /// </summary>
        /// <returns>生成した <see cref="XmlDocument"/> 。</returns>
        /// <remarks>
        /// エラー終了時に共通のルートノードとして利用する &lt;errors&gt; を設定した <see cref="XmlDocument"/> を生成します。
        /// </remarks>
        protected virtual XmlDocument CreateInitializeErrorDocument()
        {
            XmlDocument responseXmlDoc = new XmlDocument();
            responseXmlDoc.LoadXml("<errors></errors>");
            return responseXmlDoc;
        }
    } 
}
