/*********************************************************************
*	 All Rights reserved,Copyright (c) K-Opticom 
**********************************************************************
*＜プログラム内容＞
*   システム名      ：eo顧客基幹システム
*   モジュール名    ：JECNA0040002TPMA
*   ソースファイル名：JECNA0040002TPMA.java
*   作成者          ：富士通
*   日付            ：2011年10月14日
*＜機能概要＞
*   主処理(共通)<BR>
*   クリアパスHTTPSリクエスト処理部品です。
*＜修正履歴＞
*   バージョン  修正日      修正者       修正内容
*   v1.00.00    2015/09/09  FJ)李擇臣   【ANK-2586-00-00】新規作成
**********************************************************************/

package eo.ejb.cbs.mainproc;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.http.HTTPException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.fujitsu.futurity.common.JSYLogBase;
import com.fujitsu.futurity.common.JSYbpmLog;
import com.fujitsu.futurity.model.base.CAANMsg;
import com.fujitsu.futurity.model.common.JCMAPLConstMgr;
import com.fujitsu.futurity.model.ejb.common.JSYejbLog;
import com.fujitsu.futurity.model.ejb.common.fw.AgentDispatchContext;
import com.fujitsu.futurity.model.ejb.common.fw.TemplateMainHandler;

import eo.common.constant.JCNStrConst;
import eo.ejb.cbs.cbsmsg.ECNA0040002CBSMsg;

/**
 * <p>
 * クリアパスHTTPSリクエスト独自処理部品クラスです。
 * </p>
 * @author 富士通
 */
public class JECNA0040002TPMA implements TemplateMainHandler {
	/** 部品ID*/
	public static final String PGID = "JECNA0040002TPMA";
	
	/** パラメータセパレート文字:=*/
	private static final String S_SEP_EQ = JCNStrConst.S_SEP_EQ;
	
	/** パラメータセパレート文字:&*/
	private static final String S_SEP_AN = JCNStrConst.S_SEP_AN;
	
	/** 通信方式パラメータ*/
	private static final String S_SSL = JCNStrConst.S_SSL;
	
	/** リクエスト種別パラメータ*/
	private static final String S_POST = JCNStrConst.S_POST;
	
	/** クリアパスAPIシステムエラー*/
	private static final String S_AUTH_SYSERR = JCNStrConst.S_AUTH_SYSERR;
	
	/** 通信方式SSL*/
	private static final String S_HTTPS = "https";

	/** コマンドタイプ  */
	private static final String CMD = "cmd";
	
	/** 処理区分  */
	private static final String EXEC_TYPE = "exec_type";
	
	/** システム接続設定番号 */
	private static final String CONNECT_NO = "connect_no";
	
	/** 接続キー */
	private static final String CONNECT_KEY = "connect_key";
	
	/** グループコード */
	private static final String COMP_CD = "comp_cd";
	
	/** 事業者コード */
	private static final String SHOP_CD = "shop_cd";
	
	/** 結果コード */
	private static final String RESULT_CD = "result_cd";

	/** 詳細コード */
	private static final String RESULT_INFO_CD = "result_info_cd";

	/** エラーコード */
	private static final String ERR_CD = "err_cd";

	/** トランザクションID */
	private static final String TRAN_ID = "tran_id";

	/** クリアパス取引ID */
	private static final String CP_TRAN_ID = "cp_tran_id";
	
	/** コマンドタイプ "EXEC"(取引実行処理)*/
	private static final String CMD_EXEC = "EXEC";
	
	/** 処理区分 "195"（オーソリ取消） */
	private static final String EXEC_TYPE_195 = "195";
	
	/** システム接続設定番号（アプリケーションプロパティ） */
	private static final String CN_CONNECT_NO = "CN_CONNECT_NO";
	
	/** 接続キー（アプリケーションプロパティ） */
	private static final String CN_CONNECT_KEY = 	"CN_CONNECT_KEY";
	
	/** グループコード（アプリケーションプロパティ） */
	private static final String CN_COMP_CD = "CN_COMP_CD";
	
	/** 事業者コード（アプリケーションプロパティ） */
	private static final String CN_SHOP_CD = "CN_SHOP_CD";
	
	/**
	 * コマンド実行結果
	 */
    public static final String CMD_RESULT_CD = "CMD_RESULT_CD";
    
    /**
	 * レコード
	 */
    public static final String RECORD = "record";
    
    /**
	 * サービスIF
	 */
	public static final String SERVICEIF = "service_if";
    
	/**
	 * タグ属性(=TYPE)
	 */
	private static final String[] typeValues = {"service_if","service_if_list"};
	
	/**
	 * タグ名
	 */
	private static final String[] tagNames = {"Envelope","Body","Header","initResponse"};
	
	/**
	 * コマンド実行結果（エラー）
	 */
    public static final String CMD_ERROR_MESSAGE = "CMD_ERROR_MESSAGE";
    
    /**
	 * SOALFAULT（処理例外）
	 */
	private static final String SOAP_FAULT = "fault";

	/**
	 * SOALFAULT（エラーコード）
	 */
	private static final String SOAP_FAULT_CODE = "reason";
	
	/**
	 * SOALFAULT（エラー詳細）
	 */
	private static final String SOAP_FAULT_STRING = "detail";

	/**
	 * コンストラクタ.
	 */
	public JECNA0040002TPMA() {
		super();
	}

	/**
	 * クリアパスHTTPSリクエスト独自処理部品です。
	 * @param inCBSMsg CAANメッセージ
	 * @param inContext コンテキスト
	 * @throws Exception 
	 */
	public void invoke(CAANMsg inCBSMsg, AgentDispatchContext inContext) throws Exception {
		// 開始ログ出力を実施。
		JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#invoke start");
		
		HttpURLConnection conn = null;
		URL url = null;
		
		try {
			//system.properties設定値取得
			String strUrl = JCMAPLConstMgr.getString("CN_CLEARPASS_URL");
			String strApiNm = JCMAPLConstMgr.getString("CN_CLEARPASS_APINAME");
			int timeout = Integer.parseInt(JCMAPLConstMgr.getString("CN_CLEARPASS_AUTH_TIMEOUT"));
			int retryCount = Integer.parseInt(JCMAPLConstMgr.getString("CN_CLEARPASS_AUTH_RETRYCOUNT"));
			int retryInterval = Integer.parseInt(JCMAPLConstMgr.getString("CN_CLEARPASS_RETRYINTERVAL"));
			
			// アプリケーションプロパティ設定チェック
			if(strUrl == null || strApiNm == null) {
				inCBSMsg.set(ECNA0040002CBSMsg.RESULT_CD, S_AUTH_SYSERR);
				return;
			}
			
			
			// 通信方式判定
			boolean bSslFlg = false;
			if(strUrl.startsWith(S_HTTPS)) {
				bSslFlg = true;
			}
			
			// SSL通信の場合、キーマネージャを生成
			if(bSslFlg) {
				KeyManager[] km = null;
				TrustManager[] tm = { new X509TrustManager() {
	
					/**
					 * X509Certificateスタブメソッド
					 * @return null
					 */
					public java.security.cert.X509Certificate[] getAcceptedIssuers()  {
						return null;
					}
	
					/**
					 * checkClientTrustedスタブメソッド
					 * @param arg0
					 * @param arg1
					 * @throws CertificateException
					 */
					public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws CertificateException {
						// 自動生成されたメソッドスタブ
					}
	
					/**
					 * checkServerTrustedスタブメソッド
					 * @param arg0
					 * @param arg1
					 * @throws CertificateException
					 */
					public void checkServerTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws CertificateException {
						// 自動生成されたメソッドスタブ
					}
				} };
				SSLContext sslcontext = SSLContext.getInstance(S_SSL);
				sslcontext.init(km, tm, new SecureRandom());
				HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
					
					/**
					 * verifyスタブメソッド
					 * @param aHostname
					 * @param aSession
					 * @return クリアパスAPIへのリクエストパラメータ
					 */
					public boolean verify(String aHostname, SSLSession aSession) {
						return true;
					}
				});
				HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
			}
			
			// URL生成
			url = new URL(strUrl + strApiNm);
			
			for(int i = 0 ; i < retryCount ; i++) {
				//リクエスト結果読み出し try-catch外で生成するよう修正
				InputStream is = null;
				BufferedReader reader = null;
				
				try {
					// アプリケーションプロパティよりProxy設定値を取得
					String strPrxyHost = JCMAPLConstMgr.getString("CN_HTTPS_PROXY_HOST");
					String strPrxyPort = JCMAPLConstMgr.getString("CN_HTTPS_PROXY_PORT");
					
					int intProxyPort = 0;
					if(strPrxyHost != null) {
						intProxyPort = Integer.parseInt(strPrxyPort);
					}
					
					// SSLの場合、HttpsURLConnectionへキャスト
					if(bSslFlg) {
						// Proxy設定値がある場合はProxyをセットしてコネクションをオープン
						if(strPrxyHost != null && strPrxyPort != null) {
							Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(strPrxyHost, intProxyPort));
							conn = (HttpsURLConnection) url.openConnection(proxy);
						}
						else {
							conn = (HttpsURLConnection) url.openConnection();
						}
						
					}
					else {
						conn = (HttpURLConnection) url.openConnection();
					}
					
					// リクエスト設定
					conn.setRequestMethod(S_POST);
					conn.setDoOutput(true);
					conn.setInstanceFollowRedirects(true);
					
					
					//タイムアウト設定
					conn.setReadTimeout(timeout);
					
					//POSTパラメータストリームの生成
					OutputStream os = conn.getOutputStream();
					PrintStream ps = new PrintStream(os);
					ps.print(requestParamMake(inCBSMsg,inContext));
					ps.close();
					
					
					// 接続開始ログ
					JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#connect start");
					
					// 接続実行
					conn.connect();
					
					// 接続終了ログ
					JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#connect end");
					
					int statusCode = 500;
					statusCode = conn.getResponseCode();
					
					JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#statusCode=" + statusCode);

					Hashtable<String, Object> ret = new Hashtable<String, Object>();
					Transformer transformer = null;
					Document responseXml = null;
					StringWriter sw = null;
					
					TransformerFactory transformerFactory = TransformerFactory.newInstance();
					transformer = transformerFactory.newTransformer();
					
					JSYejbLog.outlog(null, JSYejbLog.DEBUG, this.getClass(), "SOAPレスポンス受信 START");
					is = conn.getInputStream();
					DOMResult dr = new DOMResult();
					transformer.transform(new StreamSource(is), dr);
					responseXml = (Document)dr.getNode();
					JSYejbLog.outlog(null, JSYejbLog.DEBUG, this.getClass(), "SOAPレスポンス受信 END");
					
					/**
					 * 下り電文のログ出力
					 */
					sw = new StringWriter();
					transformer.transform(new DOMSource(responseXml), new StreamResult(sw));

					/**
					 * SOAPレスポンス読込処理
					 */
					JSYejbLog.outlog(null, JSYejbLog.DEBUG, this.getClass(), "SOAPレスポンス読込処理 START");
					ret = read(responseXml);
					JSYejbLog.outlog(null, JSYejbLog.DEBUG, this.getClass(), ret);
					JSYejbLog.outlog(null, JSYejbLog.DEBUG, this.getClass(), "SOAPレスポンス読込処理 END");
					
					resultPut(inCBSMsg, ret);
					
					StringBuffer sb = new StringBuffer();
					sb.append("クリアパス出力結果=statusCode=");
					sb.append(statusCode);
					sb.append(makeLogStr(ret));
					JSYbpmLog.println(JSYLogBase.EXECUTION, this.getClass() ,sb, "",  new String[]{"", ""}, "");
					
					// 通信レスポンスコードが異常だった場合は処理結果にシステムエラーを設定
					if (statusCode != 200)  {
						inCBSMsg.set(ECNA0040002CBSMsg.RESULT_CD, S_AUTH_SYSERR);
					}
					
				} catch(HTTPException he) {
					inCBSMsg.set(ECNA0040002CBSMsg.RESULT_CD, S_AUTH_SYSERR);
					
					StringBuilder sb = new StringBuilder();
					StringWriter sw = new StringWriter();
					PrintWriter pw = new PrintWriter(sw);
					he.printStackTrace(pw);
					sb.append("\n" + sw.toString());
					JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(),"#JECNA0040002TPMA(HTTPException)発生" + sb.toString());	
					
					// リトライ
					if(i < retryCount) {
						// スリープ開始ログ
						JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#sleep start");
						// スリープ処理
						Thread.sleep(retryInterval);
						// スリープ終了ログ
						JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#sleep end");
						// リトライカウントログ
						JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#retryCount="
								+ i);
						continue;
					} else {
						throw he;
					}
				} catch(IOException ie) {
					inCBSMsg.set(ECNA0040002CBSMsg.RESULT_CD, S_AUTH_SYSERR);
					
					StringBuilder sb = new StringBuilder();
					StringWriter sw = new StringWriter();
					PrintWriter pw = new PrintWriter(sw);
					ie.printStackTrace(pw);
					sb.append("\n" + sw.toString());
					JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(),"#JECNA0040002TPMA(IOException)発生" + sb.toString());
					
					// リトライ
					if(i < retryCount) {
						// スリープ開始ログ
						JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#sleep start");
						// スリープ処理
						Thread.sleep(retryInterval);
						// スリープ終了ログ
						JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#sleep end");
						// リトライカウントログ
						JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#retryCount="
								+ i);
						continue;
					} else {
						throw ie;
					}
				} finally {
					// ストリームreaderクローズ前にnullチェックを実施するよう修正
					if(reader != null) {
						// readerクローズ ストリームreaderをfainallyでクローズするよう修正
						reader.close();
					}
				}
				break;
			}
			
		} catch (Exception e) {
			StringBuilder sb = new StringBuilder();
			StringWriter sw = new StringWriter();
			PrintWriter pw = new PrintWriter(sw);
			e.printStackTrace(pw);
			sb.append("\n" + sw.toString());
			JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(),"#JECNA0040002TPMA(Exception)発生" + sb.toString());
			
			inCBSMsg.set(ECNA0040002CBSMsg.RESULT_CD, S_AUTH_SYSERR);
			return;
		} finally {
			if (conn != null) {
				//切断処理
				conn.disconnect();
				conn = null;
			}
			
			// 処理結果ログ出力
			JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#CR_RESULTCODE=" + 
					inCBSMsg.getString(ECNA0040002CBSMsg.RESULT_CD));	// 処理結果コード
			JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#RESULT_INFO_CD=" + 
					inCBSMsg.getString(ECNA0040002CBSMsg.RESULT_INFO_CD));		// 詳細コード
			JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#ERR_CD=" + 
					inCBSMsg.getString(ECNA0040002CBSMsg.ERR_CD));		// エラーコード
			JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#TRAN_ID=" + 
					inCBSMsg.getString(ECNA0040002CBSMsg.TRAN_ID));		// トランザクションＩＤ
			JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#CP_TRAN_ID=" + 
					inCBSMsg.getString(ECNA0040002CBSMsg.CP_TRAN_ID));		// 取引ＩＤ
		}
		
		JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#invoke end");
	}

	/**
	 * クリアパスAPIに対して送信するリクエストパラメータを作成する。
	 * @param inCBSMsg メッセージキャリア
	 * @param strCrCpid 加盟店ID
	 * @param strCrPasswd 加盟店パスワード
	 * @param inContext コンテキスト
	 * @return クリアパスAPIへのリクエストパラメータ
	 */
	private String requestParamMake(CAANMsg inCBSMsg, AgentDispatchContext inContext) {

		// リクエストパラメータ作成(CBSMsgより読み出し)
		StringBuffer strBuf = new StringBuffer();
		
		// コマンドタイプ  固定値:"EXEC"（取引実行処理）
		strBuf.append(CMD);
		strBuf.append(S_SEP_EQ);
		strBuf.append(CMD_EXEC);

		// 処理区分　固定値:"195"（オーソリ取消）
		strBuf.append(S_SEP_AN);
		strBuf.append(EXEC_TYPE);
		strBuf.append(S_SEP_EQ);
		strBuf.append(EXEC_TYPE_195);

		// システム接続設定番号 
		strBuf.append(S_SEP_AN);
		strBuf.append(CONNECT_NO);
		strBuf.append(S_SEP_EQ);
		strBuf.append(JCMAPLConstMgr.getString(CN_CONNECT_NO));

		// 接続キー 
		strBuf.append(S_SEP_AN);
		strBuf.append(CONNECT_KEY);
		strBuf.append(S_SEP_EQ);
		strBuf.append(JCMAPLConstMgr.getString(CN_CONNECT_KEY));

		// グループコード 
		strBuf.append(S_SEP_AN);
		strBuf.append(COMP_CD);
		strBuf.append(S_SEP_EQ);
		strBuf.append(JCMAPLConstMgr.getString(CN_COMP_CD));

		// 事業者コード 
		strBuf.append(S_SEP_AN);
		strBuf.append(SHOP_CD);
		strBuf.append(S_SEP_EQ);
		strBuf.append(JCMAPLConstMgr.getString(CN_SHOP_CD));

		// クリアパス取引ID
		strBuf.append(S_SEP_AN);
		strBuf.append(CP_TRAN_ID);
		strBuf.append(S_SEP_EQ);
		strBuf.append(inCBSMsg.getString(CP_TRAN_ID));
		
		
		JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0040002TPMA#reqParam=" + strBuf.toString());
		
		StringBuilder logStr = new StringBuilder(300);
		logStr.append("cmd="+CMD_EXEC + ",");                                                                        // コマンドタイプ  
		logStr.append("exec_type="+EXEC_TYPE_195 + ",");                                                             // 処理区分  
		logStr.append("connect_no="+JCMAPLConstMgr.getString(CN_CONNECT_NO) + ",");                                  // システム接続設定番号 
		logStr.append("connect_key="+JCMAPLConstMgr.getString(CN_CONNECT_KEY)+ ",");                                 // 接続キー 
		logStr.append("comp_cd="+JCMAPLConstMgr.getString(CN_COMP_CD)+ ",");                                         // グループコード 
		logStr.append("shop_cd="+JCMAPLConstMgr.getString(CN_SHOP_CD)+ ",");                                         // 事業者コード 
		logStr.append("cp_tran_id="+inCBSMsg.getString(CP_TRAN_ID) + ",");                                       // 顧客ID 

		JSYbpmLog.println(JSYLogBase.EXECUTION, this.getClass() ,
				"クリアパス入力結果=" + logStr.toString(), "",  new String[]{"", ""}, "");
		
		return strBuf.toString();
	}
	/**
	 * クリアパスAPI処理結果を格納する。
	 * @param inCBSMsg メッセージキャリア
	 * @param key レスポンスキー
	 * @param value レスポンスバリュー
	 */
	private void resultPut(CAANMsg inCBSMsg, Hashtable<String, Object> resultMap)
	{
		
		// 結果コード
		inCBSMsg.set(ECNA0040002CBSMsg.RESULT_CD, (String)resultMap.get(RESULT_CD));

		JSYejbLog.println(JSYejbLog.EXECUTION, this.getClass(),"結果コード:"+ (String)resultMap.get(RESULT_CD));
		// 詳細コードが存在していた場合、CBSMsgに設定する
		if(!isNullBlank((String)resultMap.get(RESULT_INFO_CD)))
		{
			// 詳細コード
			inCBSMsg.set("result_info_cd", resultMap.get(RESULT_INFO_CD));
			JSYejbLog.println(JSYejbLog.EXECUTION, this.getClass(),"詳細コード:"+ (String)resultMap.get(RESULT_INFO_CD));
		}
		
		// エラーコードが存在していた場合、CBSMsgに設定する
		if(!isNullBlank((String)resultMap.get(ERR_CD)))
		{
			// エラーコード
			inCBSMsg.set("err_cd", resultMap.get(ERR_CD));
			JSYejbLog.println(JSYejbLog.EXECUTION, this.getClass(),"エラーコード:"+ (String)resultMap.get(ERR_CD));
		}
		
		// トランザクションIDが存在していた場合、CBSMsgに設定する
		if(!isNullBlank((String)resultMap.get(TRAN_ID)))
		{
			// トランザクションID
			inCBSMsg.set("tran_id", resultMap.get(TRAN_ID));
			JSYejbLog.println(JSYejbLog.EXECUTION, this.getClass(),"トランザクションID:"+ (String)resultMap.get(TRAN_ID));
		}
		
		//クリアパス取引IDが存在していた場合、CBSMsgに設定する
		if(!isNullBlank((String)resultMap.get(CP_TRAN_ID)))
		{
			// クリアパス取引ID
			inCBSMsg.set(ECNA0040002CBSMsg.CP_TRAN_ID, resultMap.get(CP_TRAN_ID));
			JSYejbLog.println(JSYejbLog.EXECUTION, this.getClass(),"クリアパス取引ID:"+ (String)resultMap.get(CP_TRAN_ID));
		}
	}
	
	/**
	 * null又は空文字の場合、trueを返却する
	 * 
	 * @param str 検査対象文字列
	 * @return true:null、又は空文字／false:左記以外
	 */
	public static boolean isNullBlank(String str) {
		if (null == str || "".equals(str))
		{
			return true;
		}
		return false;
	}
	
	/**
	 * 電文読み込み処理
	 * @param soapResponse
	 * @return XML電文解析結果
	 * @throws IOException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 */
	@SuppressWarnings("unchecked")
	private Hashtable<String, Object> read(Document soapResponse) throws IOException,ParserConfigurationException,SAXException {
		
		// レスポンス解析
		Element root =  soapResponse.getDocumentElement();//ルート要素(<SOAP-ENV:Envelope>)	
		Hashtable<String, Object> result = analyzeXml(root, true);

		return result;
	}
	
	/**
	 * XMLFAULT電文解析処理
	 * 
	 * @param elem XMLFAULT電文
	 * @return ArrayListエラー情報
	 */
	private String analyzeFaultXml(Node elem) {
		Node node = elem.getFirstChild();
		String errmsg = "";
		
		while(node != null) {
			if(node.getNodeType() == Node.ELEMENT_NODE)  {
				String nodename = node.getLocalName();
				
				if(JECNA0040002TPMA.SOAP_FAULT_CODE.equals(nodename.toLowerCase())) {
					Node child = node.getFirstChild();
					if(child != null) {
						errmsg = errmsg + "エラーコード：" + child.getTextContent();
					}
				}
				if(JECNA0040002TPMA.SOAP_FAULT_STRING.equals(nodename.toLowerCase())) {
					errmsg = errmsg + "エラー詳細：" + node.getTextContent();
				}
			}
			
			node = node.getNextSibling();
		}
		
		return errmsg;
	}
	
	/**
	 * コマンド送信戻り電文解析処理
	 * @param elem コマンド解析電文rootXML要素
	 * @param rootflag ルートフラグ
	 * @return XML解析結果
	 */
	@SuppressWarnings("unchecked")
	public Hashtable analyzeXml(Node elem, boolean rootflag) {
		
		Node node = elem.getFirstChild();
		Hashtable<String, Object> resultMap = null;
		List<Map<String, Object>>Bodynodelist = new ArrayList<Map<String, Object>>();
		
		//電文の要素をすべて解析する。
		while(node != null) {
			
			JSYejbLog.outlog(null, JSYejbLog.DEBUG , this.getClass(), node.getLocalName() + "：" + node.getTextContent());

			if(node.getNodeType() == Node.ELEMENT_NODE) {
				
				String nodename = node.getLocalName();
				String typeValue = null;
				if(node.getAttributes().getNamedItem("type") != null) {
					typeValue = node.getAttributes().getNamedItem("type").getNodeValue();
				}
				
				String childname = "";
				
				NodeList nodelist = node.getChildNodes();
				if(nodelist != null) {
					for(int i = 0 ; i < nodelist.getLength() ; i++) {
						Node child = nodelist.item(i);
						
						if(child.getNodeType() == Node.ELEMENT_NODE) {
							childname = child.getLocalName();
							break;
						}
					}
				}
				
				// SOAP例外の場合
				if(JECNA0040002TPMA.SOAP_FAULT.equals(nodename.toLowerCase()) || JECNA0040002TPMA.SOAP_FAULT.equals(childname.toLowerCase())) {
					resultMap = new Hashtable<String, Object>();
					String faultinfo = analyzeFaultXml(node);
					resultMap.put(JECNA0040002TPMA.CMD_RESULT_CD,"1");
					resultMap.put(JECNA0040002TPMA.CMD_ERROR_MESSAGE,faultinfo);	
					return resultMap;
				} else {
					if(resultMap == null) {
						resultMap = new Hashtable<String, Object>();
					}
					Hashtable<String, Object>resultinfo = analyzeXml(node, false);
					if(JECNA0040002TPMA.RECORD.equals(nodename) || JECNA0040002TPMA.SERVICEIF.equals(nodename) || Arrays.asList(typeValues).contains(typeValue)) {
						if(resultinfo != null && !resultinfo.isEmpty()) {
							Bodynodelist.add(resultinfo);
						}	
						
						resultMap.put(nodename,Bodynodelist);
					
					} else if(Arrays.asList(tagNames).contains(nodename)) {
						resultMap.putAll(analyzeXml(node, false));
					} else {
						resultMap.put(nodename,node.getTextContent());
						if(node.getAttributes().getNamedItem("err") != null) {
							String StrErr = node.getAttributes().getNamedItem("err").getTextContent();
							resultMap.put(nodename + "_ERR", StrErr);
						}
					}
					
				}
			}
			
			node = node.getNextSibling();
		}
		
		if(rootflag) {
			
			if(resultMap == null) {
				resultMap = new Hashtable<String, Object>();
			}
			
			resultMap.put(JECNA0040002TPMA.CMD_RESULT_CD,"0");
			return resultMap;
		}
		
		return resultMap;
	}

	/**
	 * クリアパスからの返却結果をログに出力するため編集を行う
	 * 編集内容
	 *   key名がカード番号(card_no)の場合はマスキング処理を行う
	 * @param ret クリアパスからの返却結果
	 * @return ログ出力用文字列
	 */
	private String makeLogStr(Hashtable<String, Object> ret){
	
		//ログ用文字列組み立て用の変数を宣言する
		StringBuilder sb = new StringBuilder();
		
		//クリアパスからの返却結果よりkeyリストを取得する
		Enumeration keys = ret.keys();
		
		//取得したkeyの件数分ループを行う
		while(keys.hasMoreElements()){
			
			//valueを取得するためkeyを取得する
			String key = (String)keys.nextElement();
			//keyを元にvalueを取得する
			String value = (String)ret.get(key);
	
			//カード番号以外の場合は、返却結果のそのまま出力する
			sb.append(key + "=" + value + ",");
			
		}
		
	
		return sb.toString();
	}
}
