/*********************************************************************
*	 All Rights reserved,Copyright (c) K-Opticom 
**********************************************************************
*＜プログラム内容＞
*   システム名      ：eo顧客基幹システム
*   モジュール名    ：JECNA0110001TPMA
*   ソースファイル名：JECNA0110001TPMA.java
*   作成者          ：富士通
*   日付            ：2014年01月10日
*＜機能概要＞
*   主処理(共通)<BR>
*   050番号利用停止・利用停止解除依頼処理部品です。
*＜修正履歴＞
*   バージョン  修正日      修正者      修正内容
*   v8.00.00    2014/01/10  富士通      新規作成
*
**********************************************************************/

package eo.ejb.cbs.mainproc;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.util.HashMap;
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.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.ws.http.HTTPException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

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.ECNA0110001CBSMsg;

/**
 * <p>
 * 050番号利用停止・利用停止解除依頼処理部品クラスです。
 * </p>
 * @author 富士通
 */
public class JECNA0110001TPMA implements TemplateMainHandler
{
	// HTTP関連定数定義
	/**
	 * HTTPヘッダ Content-TYPE
	 */
	private static final String CONTENT_TYPE = "Content-Type";
	
	/**
	 * HTTPヘッダ Conten-TYPE（値）
	 */
	private static final String CONTENT_TYPE_VALUE = "application/soap+xml; charset=UTF-8";
	
	/**
	 * HTTPヘッダ リクエストメソッド定数
	 */
	private static final String HTTP_REQUEST_METHOD = "POST";
	
	/**
	 * HTTPヘッダ SOAPAction定義
	 */
	private static final String HTTP_SOAP_ACTION = "SOAPAction";
	
	//SOAP 関連定数定義
	/**
	 * SOAP1.2用Namespace
	 */
	private static final String SOAP12 = "http://schemas.xmlsoap.org/soap/envelope/";
	
	/**
	 * SOAPエンベロープのPrefix
	 */
	private static final String SOAP_PREFIX = "soapenv";
	
	// SAOP-ENV:body内部系
	/**
	 * SOAP-ENV BODY用のPrefix
	 */
	private String SERVICE_SETTING = "";
	
	/**
	 * パッケージ用Namespace
	 */
	private static final String BODY_NAMESPACE = "http://setting.webservice.sts.softfront.com";
	
	// CRLF定数定義
	/**
	 * CRLF定数定義
	 */
	private static final String CRLF = "\r\n";

	// SOAP ACTION定義名
	/**
	 * SOAP ACTION(設置)
	 */
	private static final String ACTION_SET = "set";
	
	// XML要素情報定数定義（結果コード取得電文）
	/**
	 * SOALFAULT（処理例外）
	 */
	private static final String SOAP_FAULT = "fault";
	
	/**
	 * エラーフラグ
	 */
	private static final String ERR_FLG = "0";
	
	/** 通信方式SSL*/
	private static final String S_HTTPS = "https";
	
	// スタブモードON時の返却値用定数
	/** 戻り値（エラーコード） */
	private static final String STB_RETURN_CODE = "000";
	
	// メッセージwriterから実装を移管
	/**
	 * XMLStreamWriterクラス
	 */
	private XMLStreamWriter writer = null;
	
	/**
	 * Writerクラス
	 */
	private Writer wt = null;
	
	// 文字化け対策
	/**
	 * StringWriterクラス
	 */
	private StringWriter sw = new StringWriter();
	
	// 戻り値設定用変数
	/** 戻り値（エラーコード） */
	String return_code = null;
	
	/**
	 * データ定義、XML要素変換テーブル
	 */
	private static HashMap<String, String> elementToMap = new HashMap<String, String>() 
	{
		{
			// ０５０電話番号
			put(ECNA0110001CBSMsg.TELNUMBER		, ECNA0110001CBSMsg.TELNUMBER);
			// ＩＰ電話サービス用ＩＤ
			put(ECNA0110001CBSMsg.SIP_USERNAME	, ECNA0110001CBSMsg.SIP_USERNAME);
			// ＩＰ電話サービス用パスワード
			put(ECNA0110001CBSMsg.SIP_PASSWORD	, ECNA0110001CBSMsg.SIP_PASSWORD);
			// ステータス設定
			put(ECNA0110001CBSMsg.STATUS_REASON	, ECNA0110001CBSMsg.STATUS_REASON);
			// 対象サービス
			put(ECNA0110001CBSMsg.SERVICE_FLG	, ECNA0110001CBSMsg.SERVICE_FLG);
		}
	};
	
	/**
	 * データ定義、XML要素変換テーブル（戻り電文）
	 */
	private static HashMap<String, String> mapToElement = new HashMap<String, String>() 
	{
		{
			put(ECNA0110001CBSMsg.RETURN_CODE					, ECNA0110001CBSMsg.RETURN_CODE);
		}
	};
	
	/**
	 * コンストラクタ.
	 */
	public JECNA0110001TPMA()
	{
		super();
	}
	
	/**
	 * クリアパスHTTPSリクエスト独自処理部品です。
	 * @param inCBSMsg CAANメッセージ
	 * @param inContext コンテキスト
	 * @throws Exception 
	 */
	public void invoke(CAANMsg inCBSMsg, AgentDispatchContext inContext) throws Exception
	{
		// 開始ログ出力を実施。
		JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0110001TPMA#invoke start");
		
		// スタブモードのON、OFFで処理を変更
		if ("ON".equals(JCMAPLConstMgr.getString("CN_OPT_SET_TEST_MODE"))) {
			inCBSMsg = setResult(inCBSMsg);
		} else {
			
			// 入力値設定用
			HashMap<Object, Object> setmap = new HashMap< Object, Object>();
			
			// ０５０電話番号
			setmap.put(ECNA0110001CBSMsg.TELNUMBER, inCBSMsg.getString(ECNA0110001CBSMsg.TELNUMBER));
			
			// ＩＰ電話サービス用ＩＤ
			setmap.put(ECNA0110001CBSMsg.SIP_USERNAME, inCBSMsg.getString(ECNA0110001CBSMsg.SIP_USERNAME));
			
			// ＩＰ電話サービス用パスワード
			setmap.put(ECNA0110001CBSMsg.SIP_PASSWORD, inCBSMsg.getString(ECNA0110001CBSMsg.SIP_PASSWORD));
			
			// ステータス設定
			setmap.put(ECNA0110001CBSMsg.STATUS_REASON, inCBSMsg.getString(ECNA0110001CBSMsg.STATUS_REASON));
			
			// 対象サービス
			setmap.put(ECNA0110001CBSMsg.SERVICE_FLG, inCBSMsg.getString(ECNA0110001CBSMsg.SERVICE_FLG));
			
			inCBSMsg = execute(setmap, inCBSMsg);
		}
		
		JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0110001TPMA#invoke end");
	}
	
	/**
	 * SOAP電文送信処理を実行する。
	 * 
	 * @param serviceMap 視聴制御オーダ送信のインターフェイス情報
	 * @return 受信結果
	 */
	private CAANMsg execute(Map<Object, Object>serviceMap, CAANMsg inCBSMsg) 
	{
		HttpURLConnection connection = null;
		InputStream is = null;
		InetAddress inetaddress = null;
		String responseContentType = null;
		OutputStream os = null;
		int timeout = Integer.parseInt(JCMAPLConstMgr.getString("CN_050_SOAP_TIMEOUT"));
		int retryCount = Integer.parseInt(JCMAPLConstMgr.getString("CN_050_SOAP_RETRYCOUNT"));
		int retryInterval = Integer.parseInt(JCMAPLConstMgr.getString("CN_050_SOAP_RETRYINTERVAL"));
		
		try
		{
			URL url = null;
			
			// アプリケーションプロパティよりURLを設定
			String urlString = JCMAPLConstMgr.getString("CN_050_SOAP_URL2");
			// アプリケーションプロパティよりAPINAMEを設定
			SERVICE_SETTING = JCMAPLConstMgr.getString("CN_050_SOAP_APINAME2");
			
			url = new URL(urlString);
			
			// アプリケーションプロパティよりProxy設定値を取得
			String strPrxyHost = JCMAPLConstMgr.getString("CN_050_SOAP_HTTPS_PROXY_HOST");
			String strPrxyPort = JCMAPLConstMgr.getString("CN_050_SOAP_HTTPS_PROXY_PORT");
			
			int intProxyPort = 0;
			if(strPrxyHost != null)
			{
				intProxyPort = Integer.parseInt(strPrxyPort);
			}
			
			// 通信方式判定
			boolean bSslFlg = false;
			if(urlString.startsWith(S_HTTPS))
			{
				bSslFlg = true;
			}
			
			if(bSslFlg){
				// HTTPS通信用のKeyManager生成
				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 
					{
						// TODO 自動生成されたメソッドスタブ
					}

					/**
					 * checkServerTrustedスタブメソッド
					 * @param arg0
					 * @param arg1
					 * @throws CertificateException
					 */
					public void checkServerTrusted(
							java.security.cert.X509Certificate[] arg0, String arg1)
							throws CertificateException 
					{
						// TODO 自動生成されたメソッドスタブ
					}
				} };
				
				SSLContext sslcontext = SSLContext.getInstance(JCNStrConst.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());
			}
			
			for(int i = 0 ; i < retryCount ; i++)
			{
				try
				{
					// 電文を送信
					if(bSslFlg){
						// Proxy設定値がある場合はProxyをセットしてコネクションをオープン
						if(strPrxyHost != null
								&& strPrxyPort != null)
						{
							Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(strPrxyHost, intProxyPort));
							connection = (HttpsURLConnection) url.openConnection(proxy);
						}
						else
						{
							connection = (HttpsURLConnection) url.openConnection();
						}
					} else {
						connection = (HttpURLConnection) url.openConnection();
					}
					
					connection.setDoInput(true);
					connection.setDoOutput(true);
					
					// SOAP通信におけるタイムアウト値
					Integer pkg_Timeout = timeout;
					connection.setReadTimeout(pkg_Timeout);
					
					connection.setRequestMethod(HTTP_REQUEST_METHOD);
					connection.setRequestProperty(CONTENT_TYPE, CONTENT_TYPE_VALUE);
					connection.setRequestProperty(HTTP_SOAP_ACTION, "");
					
					os = connection.getOutputStream();
					
					this.write(serviceMap, os);
					
					// 接続開始ログ
					JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0110001TPMA#connect start");
					
					connection.connect();
					
					// 接続終了ログ
					JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0110001TPMA#connect end");
					
					// レスポンスコード出力
					JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0110001TPMA#statusCode=" + connection.getResponseCode());
					
					inetaddress =  InetAddress.getLocalHost();
					
					// 電文の戻りを取得
					is = connection.getInputStream();
					
					responseContentType = connection.getHeaderField("Content-Type");
						
					inCBSMsg = this.read(inetaddress.getHostName(), inetaddress.getHostAddress(), responseContentType, is, inCBSMsg);
					
					break;
				}
				catch(HTTPException he)
				{
					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(),"#JECNA0110001TPMA(HTTPException)発生" + sb.toString());
					
					// リトライ
					if(i < retryCount)
					{
						// スリープ開始ログ
						JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0110001TPMA#sleep start");
						// スリープ処理
						Thread.sleep(retryInterval);
						// スリープ終了ログ
						JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0110001TPMA#sleep end");
						// リトライカウントログ
						JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0110001TPMA#retryCount="
								+ i);
						continue;
					}
					else
					{
						throw he;
					}
				}
				catch(IOException ie)
				{
					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(),"#JECNA0110001TPMA(IOException)発生" + sb.toString());
					
					// リトライ
					if(i < retryCount)
					{
						// スリープ開始ログ
						JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0110001TPMA#sleep start");
						// スリープ処理
						Thread.sleep(retryInterval);
						// スリープ終了ログ
						JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0110001TPMA#sleep end");
						// リトライカウントログ
						JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "JECNA0110001TPMA#retryCount="
								+ i);
						continue;
					}
					else
					{
						throw ie;
					}
				}
			}
		}catch(Exception e) 
		{
			JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(),"#JECNA0110001TPMA(Exception)発生" + e.toString());
			
			return inCBSMsg;
		}
		finally
		{
			if(os != null)
			{
				try {
					os.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(is != null)
			{
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (connection != null)
			{
				//切断処理
				connection.disconnect();
				connection = null;
			}
		}
		return inCBSMsg;
	}
	
	/**
	 * スタブモードがONの場合は定数を返却する。
	 * @param inCBSMsg
	 * @return 返却用CAANMsg
	 */
	private CAANMsg setResult(CAANMsg inCBSMsg) {
		
		inCBSMsg.set(ECNA0110001CBSMsg.RETURN_CODE					, STB_RETURN_CODE);
		
		return inCBSMsg;
	}
	
	/**
	 * 書き込み
	 * 
	 * @param userData
	 * @param os
	 * @throws IOException
	 * @throws XMLStreamException
	 */
	private void write(Map<Object, Object> userData,OutputStream os) throws IOException,XMLStreamException
	{
		OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
		try 
		{
			// 本文
			this.writeXML(userData,osw);
			osw.write(CRLF);
		}
		finally 
		{
			if(osw != null) {
				osw.flush();
				osw.close();
			}
			if(os != null) {
				os.flush();
				os.close();
			}
		}
	}
	
	/**
	 * 電文読み込み処理
	 * 
	 * @param host
	 * @param ip
	 * @param contentType
	 * @param is
	 * @return XML電文解析結果
	 * @throws IOException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 */
	private CAANMsg read(String host, String ip, String contentType, InputStream is, CAANMsg inCBSMsg) 
	throws IOException,ParserConfigurationException,SAXException
	{
		String charset = this.getEncoding(contentType);
		StringReader sr = readSingleResource(charset, is);
		HashMap<Object, Object>result = null;
		
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		dbf.setNamespaceAware(true);
		DocumentBuilder db = dbf.newDocumentBuilder();
		
		Document doc = db.parse(new InputSource(sr));
		
		Element elem = doc.getDocumentElement();
		
		// XMLの実行結果を取得する。
		result = analyzeXml(elem,true);
		
		// 正常に取得された場合
		if ("0".equals(result.get(ERR_FLG)))
		{
			// 取得結果を戻り値にセットする。
			inCBSMsg.set(ECNA0110001CBSMsg.RETURN_CODE, return_code);
			
		// エラー発生時
		} else
		{
			// 取得結果を戻り値にセットする。
			inCBSMsg.set(ECNA0110001CBSMsg.RETURN_CODE, return_code);
		}
		return inCBSMsg;
	}
	
	/**
	 * コマンド送信戻り電文解析処理
	 * 
	 * @param elem コマンド解析電文rootXML要素
	 * @return XML解析結果
	 */
	private HashMap<Object, Object> analyzeXml(Node elem, boolean rootflag) 
	{
		Node node = elem.getFirstChild();
		HashMap<Object, Object> resultMap = null;
		
		//電文の要素をすべて解析する。
		while(node != null)
		{
			if(node.getNodeType() == Node.ELEMENT_NODE) 
			{
				String nodename = node.getLocalName();
				
				// SOAP例外の場合
				if(SOAP_FAULT.equals(nodename.toLowerCase()))
				{
					resultMap = new HashMap<Object, Object>();
					analyzeFaultXml(node);
					resultMap.put(ERR_FLG, "1");
					return resultMap;
				} else
				{
					if(resultMap == null)
					{
						resultMap = new HashMap<Object, Object>();
					}
					
					// 子要素に設定されている値を変数に格納する。
					analyzeResultXml(node);
					
					resultMap.put(ERR_FLG,"0");
					
				}
				// 子供のノードがある場合、さらに子供の要素をチェックする。(最下層はチェック対象外)
				if(node.hasChildNodes()
						&& !"callforward".equals(node.getLocalName())
						&& !"voicemail".equals(node.getLocalName())
						&& !"callrejection".equals(node.getLocalName())
						&& !"dialplan".equals(node.getLocalName())
						&& !"chat_user_setting".equals(node.getLocalName())) 
				{
					HashMap<Object, Object> child = analyzeXml(node,false);
					if(child != null)
					{
						return child;
					}
				}
			}
			node = node.getNextSibling();
		}
		
		// 結果なしの場合
		if(rootflag && resultMap == null)
		{
			resultMap = new HashMap<Object, Object>();
			resultMap.put(ERR_FLG,"0");
			return resultMap;
		}
		
		return resultMap;
	}
	
	/**
	 * XMLFAULT電文解析処理
	 * 
	 * @param elem XMLFAULT電文
	 * @return ArrayListエラー情報
	 */
	private void analyzeFaultXml(Node elem) 
	{
		Node node = elem.getFirstChild();
		
		while(node != null) {
			
			String nodename = node.getLocalName();
			if ("detail".equals(nodename)
					|| SOAP_FAULT.equals(nodename)) {
				 this.analyzeFaultXml(node);
			}
			
			if(node.getNodeType() == Node.ELEMENT_NODE) 
			{
				nodename = node.getLocalName();
				
				if(ECNA0110001CBSMsg.RETURN_CODE.equals(nodename.toLowerCase()))
				{
					
					Node child = node.getFirstChild();
					if(child != null)
					{
						return_code = child.getTextContent();
					}
				}
			}
			
			node = node.getNextSibling();
		}
	}
	
	/**
	 * コマンド結果発行戻り電文解析処理
	 * 
	 * @param elem XMLコマンド発行電文戻りNode
	 */
	private void analyzeResultXml(Node elem) 
	{
		
		Node node = elem.getFirstChild();
		
		while(node != null) {
			
			if(node.getNodeType() == Node.ELEMENT_NODE)
			{
				String nodename = node.getLocalName();
				String elementString = this.getMapName(nodename);
				
				// 名称が存在する場合、該当する変数に取得結果を格納する。
				if(elementString != null)
				{
					if (elementString.equals(ECNA0110001CBSMsg.RETURN_CODE)) {
						return_code = node.getTextContent();
					}
				}
			}
			
			node = node.getNextSibling();
		}
	}
	
	/**
	 * クローズ処理
	 */
	private void close() throws XMLStreamException,IOException
	{
		writer.flush();
		writer.close();
		
		// リクエストログ出力
		String xml = sw.toString();
		JSYejbLog.outlog(null, JSYejbLog.DEBUG, this.getClass(), xml);
		
		// 最後に全部出す。
		this.wt.write(sw.toString());
	}
	
	/**
	 * 項目名からSoapElement名を取得します。
	 * 
	 * @param obj 項目名
	 * @return XMLElement名
	 */
	private String getElementName(String obj) 
	{
		return elementToMap.get(obj);
	}
	
	/**
	 * エンコーディングデータ取得処理
	 * 
	 * @param contentType HTTPContentType文字列
	 * @return エンコーディング
	 */
	private String getEncoding(String contentType)
	{
		if(contentType != null)
		{
			// 半角空白で切って、
			String[] contentTypes = contentType.split(" ");
			
			// 開始がcharsetで始まるものを探して、
			for(String ct : contentTypes){
				if(ct.startsWith("charset=")){
					ct = ct.trim();
					return ct.substring("charset=".length());
				}
			}
		}
		// 見つけられなかった場合
		return "UTF-8";
	}
	
	/**
	 * Soap項目名から項目名を取得します。
	 * 
	 * @param obj　Soap項目名
	 * @return 項目名
	 */
	private String getMapName(String obj) 
	{
		return mapToElement.get(obj);
	}
	
	/**
	 * 電文送信初期処理
	 * @param wt Writerオブジェクト
	 */
	private void init(Writer wt) throws FactoryConfigurationError,XMLStreamException
	{
		this.wt = wt;
		
		XMLOutputFactory outfactory = XMLOutputFactory.newInstance();
		this.writer = outfactory.createXMLStreamWriter(this.sw);
		this.writer.setPrefix(SOAP_PREFIX, SOAP12);
			
	}
	
	/**
	 * 電文読み取り処理（添付ファイルなし）
	 * 
	 * @param charSet
	 * @param is
	 * @return
	 * @throws IOException
	 */
	private StringReader readSingleResource(String charSet, InputStream is) throws IOException
	{
		
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		
		byte[] buf = new byte[1024*8];
		
		int length = readLine(is, buf, 0, buf.length);
		
		while(length != -1){
			// ボディ部
			baos.write(buf, 0, length);
			length = readLine(is, buf, 0, buf.length);
		}
		
		byte[] contents = baos.toByteArray();
		
		// コンテントタイプから、encoding指定を得る。
		StringReader reader = new StringReader(new String(contents, charSet));
		// レスポンスログ出力
		String xml = new String(contents, charSet);
		JSYejbLog.outlog(null, JSYejbLog.DEBUG, this.getClass(), xml);
		
		return reader;
	}
	
	/**
	 * 行データ取得処理
	 * 
	 * @param is InputStream 
	 * @param abyte0 abyte0[]
	 * @param i i
	 * @param j j
	 * @return int 実行結果
	 * @throws IOException
	 */
	private int readLine(InputStream is, byte abyte0[], int i, int j) 
	throws IOException 
	{
		if (j <= 0) 
		{
			return 0;
		}
		int k = 0;
		int l;
		do 
		{
			if((l = is.read()) == -1) 
			{
				break;
			}
			abyte0[i++] = (byte)l;
			k++;
		}
		while(l != 10 && k != j);
		
		return k <= 0 ? -1 : k;
	}
	
	/**
	 * XML組み立て
	 * @param serviceMap
	 * @throws XMLStreamException
	 */
	private void trace(Map<Object, Object>serviceMap) throws XMLStreamException
	{
		
		this.writeStartElement(SERVICE_SETTING);
		
		// ０５０電話番号
		if(serviceMap.containsKey(ECNA0110001CBSMsg.TELNUMBER)) 
		{
			this.writeStartElement(getElementName(ECNA0110001CBSMsg.TELNUMBER));
			this.writeCharacters((String)serviceMap.get(ECNA0110001CBSMsg.TELNUMBER));
			this.writeEndElement(getElementName(ECNA0110001CBSMsg.TELNUMBER));
		}
		
		// ＩＰ電話サービス用ＩＤ
		if(serviceMap.containsKey(ECNA0110001CBSMsg.SIP_USERNAME)) 
		{
			this.writeStartElement(getElementName(ECNA0110001CBSMsg.SIP_USERNAME));
			this.writeCharacters((String)serviceMap.get(ECNA0110001CBSMsg.SIP_USERNAME));
			this.writeEndElement(getElementName(ECNA0110001CBSMsg.SIP_USERNAME));
		}
		
		// ＩＰ電話サービス用パスワード
		if(serviceMap.containsKey(ECNA0110001CBSMsg.SIP_PASSWORD)) 
		{
			this.writeStartElement(getElementName(ECNA0110001CBSMsg.SIP_PASSWORD));
			this.writeCharacters((String)serviceMap.get(ECNA0110001CBSMsg.SIP_PASSWORD));
			this.writeEndElement(getElementName(ECNA0110001CBSMsg.SIP_PASSWORD));
		}
		
		// ステータス設定
		if(serviceMap.containsKey(ECNA0110001CBSMsg.STATUS_REASON)) 
		{
			this.writeStartElement(getElementName(ECNA0110001CBSMsg.STATUS_REASON));
			this.writeCharacters((String)serviceMap.get(ECNA0110001CBSMsg.STATUS_REASON));
			this.writeEndElement(getElementName(ECNA0110001CBSMsg.STATUS_REASON));
		}
		
		// 対象サービス
		if(serviceMap.containsKey(ECNA0110001CBSMsg.SERVICE_FLG)) 
		{
			this.writeStartElement(getElementName(ECNA0110001CBSMsg.SERVICE_FLG));
			this.writeCharacters((String)serviceMap.get(ECNA0110001CBSMsg.SERVICE_FLG));
			this.writeEndElement(getElementName(ECNA0110001CBSMsg.SERVICE_FLG));
		}
		
		this.writeEndElement(SERVICE_SETTING);
	}
	
	/**
	 * XML出力処理
	 * 
	 * @param userData
	 * @param osw
	 * @throws XMLStreamException
	 */
	private void writeXML(Map<Object, Object> userData,OutputStreamWriter osw) throws XMLStreamException,IOException
	{
		
		this.init(osw);
		
		this.writeStartDocument("UTF-8");
		this.writeStartSOAPElement("Envelope");
		this.writeNamespace(SOAP_PREFIX, SOAP12);
		this.writeNamespace(ACTION_SET, BODY_NAMESPACE);
		
		this.writeUserData(userData);
		
		this.writeEndSOAPElement("Envelope");
		this.writeEndDocument();
		
		this.close();
	}
	
	/**
	 * XMLドキュメント開始タグ出力処理
	 * 
	 * @param encoding
	 * @throws XMLStreamException
	 */
	private void writeStartDocument(String encoding) throws XMLStreamException
	{
		this.writer.writeStartDocument(encoding, "1.0");
	}
	
	/**
	 * XMLSoap要素開始タグ出力処理(SOAP)
	 * 
	 * @param name
	 * @throws XMLStreamException
	 */
	private void writeStartSOAPElement(String name) throws XMLStreamException
	{
		this.writer.writeStartElement(SOAP_PREFIX, name, SOAP12);
	}
	
	/**
	 * XML要素開始タグ出力処理
	 * 
	 * @param name
	 * @throws XMLStreamException
	 */
	private void writeStartElement(String name) 
	throws XMLStreamException
	{
		this.writer.writeStartElement(BODY_NAMESPACE , name);
	}
	
	/**
	 * XML名前空間書き込み処理
	 * 
	 * @param prefix Prefix
	 * @param uri 名前空間URL
	 * @throws XMLStreamException
	 */
	private void writeNamespace(String prefix, String uri) throws XMLStreamException
	{
		this.writer.writeNamespace(prefix, uri);
	}
	
	/**
	 * ユーザデータ部分のメッセージ生成
	 * @param writer
	 * @param userData
	 * @throws XMLStreamException
	 */
	private void writeUserData(Map<Object, Object> userData) throws XMLStreamException
	{
		this.writeStartSOAPElement("Header");
		this.writeEndSOAPElement("Header");
		this.writeStartSOAPElement("Body");
		
		this.trace(userData);
		
		this.writeEndSOAPElement("Body");
		
	}
	
	/**
	 * XML要素終了タグ出力処理
	 * 
	 * @param name
	 * @throws XMLStreamException
	 */
	private void writeEndElement(String name) throws XMLStreamException
	{
		this.writer.writeEndElement();
	}
	
	/**
	 * XML要素終了タグ出力処理(SOAP)
	 * 
	 * @param name
	 * @throws XMLStreamException
	 */
	private void writeEndSOAPElement(String name) throws XMLStreamException
	{
		this.writer.writeEndElement();
	}
	
	/**
	 * XMLドキュメント終了タグ出力処理
	 * @throws XMLStreamException
	 * 
	 */
	private void writeEndDocument() throws XMLStreamException
	{
		this.writer.writeEndDocument();
	}

	/**
	 * XMLデータ出力処理
	 * 
	 * @param str
	 * @throws XMLStreamException
	 */
	private void writeCharacters(String str) 
	throws XMLStreamException
	{
		if(str == null)
		{
			this.writer.writeCharacters("");
		} else
		{
			this.writer.writeCharacters(str);
		}
	}
}
