/*
このファイルの中身は、NTT オープンソースライセンス　バージョン 1.0（「本
契約」という）の適用を受けます。
本契約を遵守しない限り、このファイルを使用してはなりません。
本契約のコピーは、次のＵＲＬから入手できます。
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

本契約に基づき頒布されるソフトウェアは、現状のまま、明示又は黙示のあらゆ
る種類の保証なしで、頒布されます。本契約に基づく権利及び制限を律する特定
の文言については、本契約を参照してください。

「オリジナルコード」は、 NTT Cyber Space Laboratories Code です。 
「オリジナルコード」の「初期開発者」は、日本電信電話株式会社です。  
日本電信電話株式会社により創作された部分は、Copyright (C) 2004 
日本電信電話株式会社 です。
全ての権利が留保されます。 
「コントリビュータ」：_____________________________________ 


The contents of this file are subject to the NTT Opensource License
Version 1.0 (the License); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

Software distributed under the License is distributed on an AS IS
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is NTT Cyber Space Laboratories Code .

The Initial Developer of the Original Code is NIPPON TELEGRAPH AND 
TELEPHONE CORPORATION.
Portions created by the NIPPON TELEGRAPH AND TELEPHONE CORPORATION 
are Copyright (C) 2004 NIPPON TELEGRAPH AND TELEPHONE CORPORATION. 
All Rights Reserved.

Contributor(s) ______________________________________.
*/

//
//	ScormLoEndLearnServlet
//	変更履歴
//		2004.02.01	新規作成
//      2006.12.15　変更 T.Kiyokawa
//                  終了時送信ログをXMLからテキスト形式に変更し、高速化
//

package jp.co.ntt.lms.lo.scorm;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Hashtable;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jp.co.ntt.lms.lo.LOException;
import jp.co.ntt.lms.lo.LearnSession;
import jp.co.ntt.lms.lo.LoConstant;
import jp.co.ntt.lms.lo.LoTopClient;
import jp.co.ntt.lms.xmo.Environment;
import jp.co.ntt.lms.xmo.Assignments.AssignmentsClient;
import jp.co.ntt.lms.xmo.util.DebugLog;

/**
 * SCORM LO学習終了用サーブレット。<br>
 * SCORM LOサーバが学習の終了または中断を実行するときに、
 * SCORM LOサーバが呼び出します。
 * このサーブレットでは、送信されてきた情報をもとに、
 * ログ情報、中断情報、トレース情報を所定のデータベースまたはファイルに
 * 保存・登録して、実行結果を呼び出しもとのSCORM LOサーバに返答します。
 * 返答は、以下のXML形式となります。
 * 	<code>&lt;result&gt;処理結果&lt;/result&gt;</code>
 * 処理結果は、正常終了の場合&quot;OK&quot;、
 * 異常終了の場合&quot;NG&quot;となります。
 *
 * @author T.Nishiki
 */
public class ScormLoEndLearnWithoutXmlServlet extends HttpServlet {

	/**
	 * GETリクエストを処理します。<br>
	 * @param request HTTPリクエスト。
	 * @param response HTTPレスポンス。
	 * @throws IOException リクエストからの入力またはレスポンスへの出力時に
	 * 　　　　　エラーとなった場合。
	 * @see HttpServlet#doGet(HttpServletRequest, HttpServletResponse)
	 * @see ScormLoEndLearnWithoutXmlServlet#doRequest(HttpServletRequest, HttpServletResponse)
	 */
	protected void doGet(
		HttpServletRequest request,
		HttpServletResponse response)
		throws IOException {
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD doGet(reqeust,response)",
			DebugLog.ROW);

		doRequest(request, response);

		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL doGet(reqeust,response)",
			DebugLog.ROW);
	}

	/**
	 * POSTリクエストを処理します。<br>
	 * @param request HTTPリクエスト。
	 * @param response HTTPレスポンス。
	 * @throws IOException リクエストからの入力またはレスポンスへの出力時に
	 * 　　　　　エラーとなった場合。
	 * @see HttpServlet#doPost(HttpServletRequest, HttpServletResponse)
	 * @see ScormLoEndLearnWithoutXmlServlet#doRequest(HttpServletRequest, HttpServletResponse)
	 */
	protected void doPost(
		HttpServletRequest request,
		HttpServletResponse response)
		throws IOException {
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD doPost(reqeust,response)",
			DebugLog.ROW);

		doRequest(request, response);

		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL doPost(reqeust,response)",
			DebugLog.ROW);
	}

	/**
	 * リクエストを処理します。<br>
	 *
	 * @param request HTTPリクエスト。
	 * @param response HTTPレスポンス。
	 * @throws IOException リクエストからの入力またはレスポンスへの出力時に
	 * 　　　　　エラーとなった場合。
	 */
	private void doRequest(
		HttpServletRequest request,
		HttpServletResponse response)
		throws IOException {

		////////////////////////////////////////////////////////////////////////
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD doRequest(reqeust,response)",
			DebugLog.ROW);
		// 結果
		String status = "";
		// レスポンス用テキスト出力ストリーム。
		PrintWriter responseWriter = null;
		// リクエスト用入力ストリーム。
		InputStream requestStream = null;
		try {
			////////////////////////////////////////////////////////////////////
			ScormLoEnv scormEnv = new ScormLoEnv();
			// レスポンスのエンコードを取得
			String responseEncode = scormEnv.getProperty(
				ScormLoEnv.CHARSET_RESPONSE_END_LEARN);
			response.setContentType(
				"text/xml;charset="	+ responseEncode );
			// 書き込み開始
			responseWriter = response.getWriter();
			// XML宣言
			responseWriter.println(
				"<?xml version=\"1.0\" encoding=\"" + responseEncode + "\"?>");
			// リクエスト入力ストリームを取得しておきます。
			requestStream = request.getInputStream();
			////////////////////////////////////////////////////////////////////
			// 学習終了情報をリクエストパラメータから生成します。
			ScormLoEndLearnData endLearnData = makeScormLoEndLearnData(request);
			////////////////////////////////////////////////////////////////////
			// ログインセッション回数をチェック
			// 存在する場合は、これ以降、処理しない。
			if ( isExistLoginSession(endLearnData) == true ) {
				String message =
					"[LoginSession:" + endLearnData.getLoginSession() + "]"
					+ "is Exist (UserID:" + endLearnData.getUserID()
					+ ",LOID:" + endLearnData.getLoID() + ").";
				throw new LOException(message);
			}
			////////////////////////////////////////////////////////////////////
			// 起動情報ファイルの削除（ファイルがない場合はエラー）
			String targetLOID = deleteSessionFile(endLearnData);
			////////////////////////////////////////////////////////////////////
			// 前段階が完了した後に、学習終了メイン処理を実行する
			processEndLearnMain(endLearnData, requestStream, targetLOID);
			// tryブロックの最後まできたら成功
			status = ScormLoConstant.ENDLEARN_STATUS_SUCCESS;
		}
		catch (LOException e) {
			status = ScormLoConstant.ENDLEARN_STATUS_FAILURE;
		}
		catch (Exception e) {
			DebugLog.write(getClass(), e, DebugLog.HIGHT);
			status = ScormLoConstant.ENDLEARN_STATUS_FAILURE;
		}
		////////////////////////////////////////////////////////////////////////
		// レスポンスに書込み
		responseWriter.print(
			"<" + ScormLoConstant.ENDLEARN_TAG_NAME_ROOT + ">");
		responseWriter.print(status);
		responseWriter.print(
			"</" + ScormLoConstant.ENDLEARN_TAG_NAME_ROOT + ">");
		responseWriter.close();
		////////////////////////////////////////////////////////////////////////
		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL doRequest(reqeust,response)",
			DebugLog.ROW);
	}

	/**
	 * 学習終了のメイン処理を実行します。<br>
	 * Assignmentsテーブルのカウントアップ、
	 * 中断情報、トレース情報のログデータからの抽出・ファイル保存、
	 * AICCログのログデータからの抽出・ＤＢ登録を実行します。<br>
	 * これらの処理が失敗した場合は、共通ログの登録のみを試みます。<br>
	 *
	 * @param endLearnData 学習終了情報オブジェクト。
	 * @param requestStream HTTPリクエストの入力ストリーム。
	 * @param targetLOID Assignmentsテーブルの学習回数カウントアップ対象のLOID。
	 * @throws LOException 処理に失敗した場合。
	 */
	private void processEndLearnMain(
			ScormLoEndLearnData endLearnData,
			InputStream requestStream,
			String targetLOID) throws LOException {

		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD processEndLearnMain("
			+ "endLearnData,requestStream," + targetLOID + ")",
			DebugLog.ROW);
		try {
			////////////////////////////////////////////////////////////////////
			// 終了理由が中止の場合、SCORM LO対して終了の通知だけ行う。
			String reason = endLearnData.getReason();
			if (reason.equals(ScormLoConstant.PARAM_REASON_ABANDON) == false) {
				
				////////////////////////////////////////////////////////////////
				// LOアサイン上での学習回数のカウントアップ
				countupAssignmentStudyCount(endLearnData, targetLOID);
				////////////////////////////////////////////////////////////////
				// requestStreamからデータを取得し、保存する
				BufferedReader reader = null ;
			    String line = null ;
			    int lineCount = 0 ; 
				try
				{
					////////////////////////////////////////////////////////////
					// BufferedReader生成 
					reader = new BufferedReader(new InputStreamReader(requestStream));
				    // 1行目のデータは各ブロックごとの行数とする
				    String[] lineCountString = reader.readLine().split(",") ;
					////////////////////////////////////////////////////////////
				    // 受信データの解析は以下の順に行う
				    // １．中断情報
				    // ２．学習ログ情報
				    // ３．トレース情報
				    // ４．navtrace.xml
					////////////////////////////////////////////////////////////
					// 中断情報の部分を取得・保存
					// =========================================================
					// 中断情報出力先ファイル
					File resumeFile = ScormLoUtil.getResumeFile(
						endLearnData.getUserID(),
						endLearnData.getLoID(),
						endLearnData.getStudySession());
					// 出力する
					lineCount = Integer.parseInt( lineCountString[0] ) ;
				    saveFile(reader, lineCount, resumeFile);
					////////////////////////////////////////////////////////////
					// 学習ログの取得・文字列に変換
					// =========================================================
					// 文字列に変換し変数へ格納する
					lineCount = Integer.parseInt( lineCountString[1] ) ;
					StringBuffer aiccLog = new StringBuffer() ;
				    for ( int i = 0 ; i < lineCount ; i++ )
					{
				    	line = reader.readLine() ;
				    	if ( line != null )
				    	{
				   			aiccLog.append(line);
				    		// メモリ上の文字列なので改行コードはCRLF固定とする
				    		aiccLog.append("\r\n");
				    	}
					} 
					endLearnData.setStudyLog( aiccLog.toString() );
				    ////////////////////////////////////////////////////////////
					// トレース情報の部分を取得・保存
					// =========================================================
					// トレース情報出力先ファイル
					File traceFile = ScormLoUtil.getTraceFile(
						endLearnData.getUserID(),
						endLearnData.getLoID(),
						endLearnData.getLoginSession());
					// 出力する
					lineCount = Integer.parseInt( lineCountString[2] ) ;
				    saveFile(reader, lineCount, traceFile);
					// =========================================================
					// navtrace.xmlトレース情報出力先ファイル
					File navtraceFile = new File(traceFile.getParentFile(), "navtrace.xml");
					// 出力する
					lineCount = Integer.parseInt( lineCountString[3] ) ;
				    saveFile(reader, lineCount, navtraceFile);
				}
				catch (Exception e) {
					DebugLog.write(getClass(), e, DebugLog.HIGHT);
					throw new LOException(e.getMessage());
				}
				finally
				{
					// 入力ファイルを閉じる
					if ( reader != null )
					{
						try
						{
							reader.close();
						}
						catch ( IOException e ) {} 
					}
				}				
			}
			////////////////////////////////////////////////////////////////////
			// SCORM LOへログを送信します
			writeStudyLog(endLearnData);
		}
		catch (Exception e) {
			DebugLog.write(getClass(), e, DebugLog.HIGHT);
			// 何かしら例外が発生しても、共通ログの挿入だけは試みる。
			insertCommonLog(endLearnData);
			throw new LOException(e.getMessage());
		}
		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL processEndLearnMain("
			+ "endLearnData,requestStream," + targetLOID + ")",
			DebugLog.ROW);
	}

	/**
	 * 受信データからファイルを保存する
	 * @param reader リーダー
	 * @param lineCount 取得行数
	 * @param saveFile 保存先ファイル
	 * @return
	 * @throws IOException
	 * @throws NumberFormatException
	 */
	private BufferedWriter saveFile(BufferedReader reader, int lineCount, File saveFile) 
	throws IOException {
		
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD saveFile("
			+ "reader," + lineCount + ",resumeFile)",
			DebugLog.ROW);
	
		BufferedWriter writer = null;
		String line = null;
		File dirFile = null;
		
		// =========================================================
		// 出力先のディレクトリを作成する
		dirFile = saveFile.getParentFile();
		dirFile.mkdirs();
		// ファイルに保存する
		try
		{
			writer = new BufferedWriter(new FileWriter(saveFile)) ;
			for ( int i = 0 ; i < lineCount ; i++ )
			{
				line = reader.readLine() ;
				if ( line != null )
				{
					writer.write(line) ;
					writer.write("\r\n") ;
				}
			}
		}
		finally
		{
			// ファイルを閉じる
			if ( writer != null )
			{
				try {
					writer.flush();
					writer.close();
				}
				catch (IOException ex) {}
			}
		}
		
		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL saveFile("
			+ "reader," + lineCount + ",resumeFile)",
			DebugLog.ROW);

		return writer;
	}

	/**
	 * 共通ログデータの登録を試みます。<br>
	 *
	 * @param endLearnData 学習終了情報オブジェクト。
	 */
	private void insertCommonLog(ScormLoEndLearnData endLearnData) {
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD insertCommonLog(endLearnData)", DebugLog.ROW);
		try {
			// パラメータ設定
			Hashtable params = new Hashtable();
			params.put( ScormLoConstant.KEY_END_LEARN_DATA, endLearnData );
			// サーバ名称の取得
			Environment environment = new Environment();
			String serverName = environment.getLoServerName();
			// Loクライアントのインスタンスから学習終了呼び出します。
			LoTopClient loTopClient = new LoTopClient(
					serverName, LoConstant.LO_TYPE_SCORM);
			loTopClient.setParam( params );
			// 共通ログ登録
			boolean endLearnResult = loTopClient.execute(
							ScormLoConstant.METHOD3, params);
			if ( endLearnResult == false ) {
				// 学習終了処理でエラーが生じた場合は例外を投げる
				throw new LOException(loTopClient.getMessageData());
			}
		}
		catch ( Exception e ) {
			DebugLog.write(getClass(), e, DebugLog.HIGHT);
		}
		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL insertCommonLog(endLearnData)", DebugLog.ROW);
	}

	/**
	 * 学習ログを処理します。
	 * @param endLearnData 学習終了情報。
	 * @throws LOException 例外コメント
	 */
	private void writeStudyLog( ScormLoEndLearnData endLearnData )
		throws LOException {
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD writeStudyLog(endLearnData)",
			DebugLog.ROW);

		// パラメータ設定
		Hashtable params = new Hashtable();
		params.put( ScormLoConstant.KEY_END_LEARN_DATA, endLearnData );

		try {
			// サーバ名称の取得
			Environment environment = new Environment();
			String serverName = environment.getLoServerName();
			// Loクライアントのインスタンスから学習終了呼び出します。
			LoTopClient loTopClient = new LoTopClient(
					serverName, LoConstant.LO_TYPE_SCORM);
			loTopClient.setParam( params );
			boolean endLearnResult = loTopClient.endLearn();
			if ( endLearnResult == false ) {
				// 学習終了処理でエラーが生じた場合は例外を投げる
				throw new LOException(loTopClient.getMessageData());
			}
		}
		catch ( LOException e ) {
			throw e;
		}
		catch ( Exception e ) {
			throw new LOException(e.getMessage());
		}
		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL writeStudyLog(endLearnData)",
			DebugLog.ROW);
	}

	/**
	 * 学習開始時に生成した起動ファイルを削除します。<br>
	 * 学習開始時SCORM LOサーバには、学習するLOのRealLOIDがLOIDとして、
	 * 送信されており、起動ファイルには教材の（Realではない）LOIDが書き込まれています。
	 * LOアサイン上の学習回数のカウントアップでは（Realではない）LOIDが
	 * カウントアップ対象となるため、ファイルの削除に先立って、
	 * （Realではない）LOIDを取得し、メソッドの戻り値として戻します。<br>
	 *
	 * @param endLearnData 学習終了情報。
	 * @return 起動ファイルから読み取ったRealではないLOID。
	 * @throws LOException 処理中に例外が発生した場合。
	 */
	private String deleteSessionFile(ScormLoEndLearnData endLearnData)
			throws LOException {
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD deleteSessionFile(endLearnData)",
			DebugLog.ROW);

		// このメソッドの戻り値
		String targetLOID = null;
		// ユーザID
		String useID = endLearnData.getUserID();
		// LOID
		String loID = endLearnData.getLoID();
		// 終了理由
		String reason = endLearnData.getReason();
		// 削除対象ファイル
		LearnSession learnSession = new LearnSession();
		learnSession.setUserID(useID);
		learnSession.setSessionID(loID);
		learnSession.setLoType(LoConstant.LO_TYPE_SCORM);
		// 終了理由が中止の場合、削除だけを行う
		if (reason.equals(ScormLoConstant.PARAM_REASON_ABANDON) == false) {
			if (LearnSession.isExitSession(useID, loID) == false) {
				String message = "NO Session File(" + useID + "," + loID + ")";
				DebugLog.write(getClass(),
					message, DebugLog.MID);
				throw new LOException(message);
			}
			// 学習起動ファイルを読み込み、学習対象ＬＯのLOIDを取得
			learnSession.load();
			targetLOID = learnSession.getTargetLOID();
			if (targetLOID == null) {
				String message = "No non-real LOID"
					+ " from (userID:" + useID + ",loid:" + loID + ")";
				DebugLog.write(getClass(),
					message, DebugLog.MID);
				throw new LOException(message);
			}
		}
		// 起動ファイルを削除。
		learnSession.delete();

		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL deleteSessionFile(endLearnData)"
			 + " return " + targetLOID,
			DebugLog.ROW);

		return targetLOID;
	}

	/**
	 * リクエストパラメータから学習終了情報を生成します。<br>
	 * あわせて各パラメータのNULLチェックも行います。<br>
	 * @param request HTTPリクエスト。
	 * @return パラメータから取得可能な学習終了情報。
	 * @throws LOException パラメータが送信されなかったり、
	 *          数値パラメータが数値に変換できなかったりした場合。
	 */
	private ScormLoEndLearnData makeScormLoEndLearnData(
			HttpServletRequest request) throws LOException {
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD makeScormLoEndLearnData(" + request + ")",
			DebugLog.ROW);

		ScormLoEndLearnData endLearnData = new ScormLoEndLearnData();
		try {
			////////////////////////////////////////////////////////////////////
			// リクエスト・パラメータ取得
			// 各パラメータともに取得後、ログ出力、NULLチェック、データクラス格納。
			//==================================================================
			// ユーザID
			String userID = request.getParameter(
					ScormLoConstant.PARAM_NAME_USERID);
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_USERID + "]"
				+ "[" + userID + "]", DebugLog.MID);
			if (userID == null) {
				throw new NullPointerException(
					"userID is not found at End-Learn");
			}
			endLearnData.setUserID(userID);
			//==================================================================
			// 教材のLOID
			String loID = request.getParameter(
					ScormLoConstant.PARAM_NAME_LOID);
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_LOID + "]"
				+ "[" + loID + "]", DebugLog.MID);
			if (loID == null) {
				throw new NullPointerException(
					"loID is not found at End-Learn");
			}
			endLearnData.setLoID(loID);
			//==================================================================
			// 教材の教材ID
			String materialID = request.getParameter(
					ScormLoConstant.PARAM_NAME_MATERIALID);
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_MATERIALID + "]"
				+ "[" + materialID + "]", DebugLog.MID);
			if (materialID == null) {
				throw new NullPointerException(
					"materialID is not found at End-Learn");
			}
			endLearnData.setMaterialID(materialID);
			//==================================================================
			// 学習セッション回数（開始−終了を１単位とする回数）
			String studySession = request.getParameter(
					ScormLoConstant.PARAM_NAME_STUDY_SESSION);
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_STUDY_SESSION + "]"
				+ "[" + studySession + "]", DebugLog.MID);
			if (studySession == null) {
				throw new NullPointerException(
					"studySession is not found at End-Learn");
			}
			int intStudySession = Integer.parseInt(studySession);
			endLearnData.setStudySession(intStudySession);
			//==================================================================
			// ログインセッション回数（開始／再開−中断／終了を１単位とする回数）
			String loginSession = request.getParameter(
					ScormLoConstant.PARAM_NAME_LOGIN_SESSION);
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_LOGIN_SESSION + "]"
				+ "[" + loginSession + "]", DebugLog.MID);
			if (loginSession == null) {
				throw new NullPointerException(
					"loginSession is not found at End-Learn");
			}
			int intLoginSession = Integer.parseInt(loginSession);
			endLearnData.setLoginSession(intLoginSession);
			//==================================================================
			// 終了理由
			String reason = request.getParameter(
					ScormLoConstant.PARAM_NAME_REASON);
			if (reason == null) {
				throw new NullPointerException(
					"reason is not found at End-Learn");
			}
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_REASON + "]"
				+ "[" + reason + "]", DebugLog.MID);
			endLearnData.setReason(reason);
			//==================================================================
			// 学習開始日時
			String startdate = request.getParameter(
					ScormLoConstant.PARAM_NAME_START_DATE);
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_START_DATE + "]"
				+ "[" + startdate + "]", DebugLog.MID);
			if (startdate == null) {
				throw new NullPointerException(
					"startdate is not found at End-Learn");
			}
			endLearnData.setStartDate(startdate);
			//==================================================================
			// 完了状況の取得（終了理由が「終了」の場合に完了）
//MOD start 2005.08.08 NTTR-KO suspend
			boolean completion =
				reason.equals(ScormLoConstant.PARAM_REASON_EXIT);
			if(completion){
				endLearnData.setCompletionStatus(completion);
			}else{
				String completionStatus = request.getParameter(ScormLoConstant.PARAM_NAME_COMPLETION);
				DebugLog.write(getClass(), "[" + ScormLoConstant.PARAM_NAME_COMPLETION + "]" + "[" + completionStatus + "]", DebugLog.MID);
				completion = false;
				if (completionStatus != null) {
					completion = Boolean.valueOf(completionStatus).booleanValue();
				}
				endLearnData.setCompletionStatus(completion);
			}
//MOD end

			//==================================================================
			// 合格状況の取得
			String satisfiedStatus = request.getParameter(
					ScormLoConstant.PARAM_NAME_SATISFIED);
			DebugLog.write(getClass(),
				"[" + ScormLoConstant.PARAM_NAME_SATISFIED + "]"
				+ "[" + satisfiedStatus + "]", DebugLog.MID);
			boolean satisfied = false;
			if (satisfiedStatus != null) {
				satisfied = Boolean.valueOf(satisfiedStatus).booleanValue();
			}
			endLearnData.setSatisfiedStatus(satisfied);
		}
		catch (Exception e) {
			DebugLog.write(getClass(), e, DebugLog.HIGHT);
			throw new LOException(e.getMessage());
		}

		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL makeScormLoEndLearnData(" + request + ")",
			DebugLog.ROW);

		return endLearnData;
	}

	/**
	 * LOアサイン上での学習回数のカウントアップを実行します。
	 * @param endLearnData 学習終了情報。
	 * @param targetLoID  カウントアップする対象教材のLOID。
	 * @throws LOException カウントアップ処理中に例外が発生した場合。
	 */
	private void countupAssignmentStudyCount(
			ScormLoEndLearnData endLearnData, String targetLoID)
			throws LOException {
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD makeScormLoEndLearnData(endLearnData," + targetLoID + ")",
			DebugLog.ROW);

		try {
			// ユーザID
			String userID = endLearnData.getUserID();
			AssignmentsClient client = new AssignmentsClient();
			client.countupStudyCount(
				userID, targetLoID, LoConstant.LO_TYPE_SCORM);
		}
		catch (Exception e) {
			DebugLog.write(getClass(), e, DebugLog.HIGHT);
			throw new LOException(e.getMessage());
		}

		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL makeScormLoEndLearnData(endLearnData," + targetLoID + ")",
			DebugLog.ROW);
	}

	/**
	 * 学習終了情報として含まれるログインセッション回数がすでに存在するか
	 * @param endLearnData 学習終了情報。
	 * @return 指定したユーザID、LOIDで、ログインセッション回数が存在する場合、
	 *          trueをそうでない場合はfalseを戻します。
	 * @throws LOException 処理中に例外が発生した場合。
	 */
	private boolean isExistLoginSession(ScormLoEndLearnData endLearnData)
			throws LOException {
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD isExistLoginSession(endLearnData)", DebugLog.ROW);

		Boolean loginSessionUsed = null;
		try {
			////////////////////////////////////////////////////////////////////
			// 検索する条件を設定
			Hashtable params = new Hashtable();
			// ユーザID
			params.put(ScormLoConstant.KEY_USERID, endLearnData.getUserID());
			// LOID
			params.put(ScormLoConstant.KEY_LOID, endLearnData.getLoID());
			// ログインセッション回数
			params.put(ScormLoConstant.KEY_LOGIN_SESSION,
					Integer.toString(endLearnData.getLoginSession()));
			////////////////////////////////////////////////////////////////////
			// チェックメソッドをLOクライアントを通じて呼び出す。
			// サーバ名称の取得
			Environment environment = new Environment();
			String serverName = environment.getLoServerName();
			// チェックメソッドをLOクライアントを通じて呼び出す。
			LoTopClient loTopClient = new LoTopClient(
					serverName, LoConstant.LO_TYPE_SCORM);
			boolean endLearnResult = loTopClient.execute(
							ScormLoConstant.METHOD2, params);
			if ( endLearnResult == false ) {
				// 学習終了処理でエラーが生じた場合は例外を投げる
				throw new LOException(loTopClient.getMessageData());
			}
			////////////////////////////////////////////////////////////////////
			// 検索結果を取得します。
			loginSessionUsed = (Boolean)loTopClient.getExecute();
		}
		catch ( LOException e ) {
			throw e;
		}
		catch ( Exception e ) {
			DebugLog.write(getClass(), e, DebugLog.HIGHT);
			throw new LOException(e.getMessage());
		}
		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL isExistLoginSession(endLearnData)"
				+ " return " + loginSessionUsed.booleanValue(), DebugLog.ROW);
		return loginSessionUsed.booleanValue();
	}
}
