/*
 * Copyright (c) 2009 The openGion Project.
 *
 * 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.
 */
package org.opengion.hayabusa.servlet;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.HashMap;

import java.util.regex.Pattern;													// 7.2.9.4 (2020/11/20)
import java.util.regex.Matcher;													// 7.2.9.4 (2020/11/20)

import jakarta.servlet.ServletException;
import jakarta.servlet.ServletConfig;											// 7.2.9.4 (2020/11/20)
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.opengion.fukurou.system.Closer;
import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.remote.RemoteControllable;

// import jakarta.servlet.annotation.WebInitParam;								// 7.3.0.0 (2021/01/06)
// import jakarta.servlet.annotation.WebServlet;								// 7.3.0.0 (2021/01/06)

/**
 * 外部からキーと値を投げて処理をさせるサーブレットです。
 * Post,Get両方に対応しています。
 * classキーが必須です。(値はhayabusa/remote/内のクラス名)
 *
 * @og.rev 4.1.0.0 (2007/12/20) 新規作成
 * @version  4.1
 * @author   Masakazu Takahashi
 * @since    JDK6.0,
 *
 */
/*
@WebServlet(
	urlPatterns = "/servlet/remoteControl" ,
	initParams  = {
		@WebInitParam(name="filePattern", value=".*opengionV8.*|c:[/\\]temp[/\\].*")
	}
)
*/
public class RemoteControlServlet extends HttpServlet {
	private static final long serialVersionUID	= 542020111201L ;
	private static final String REMOTE_PKG		= "org.opengion.hayabusa.remote.";

	private Pattern filePattern ;												// 7.2.9.4 (2020/11/20) ﾌｧｲﾙｱｸｾｽの制約(指定がない場合は、処理しない)。

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
	 */
	public RemoteControlServlet() { super(); }		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。

	/**
	 * Postメソッドで与えられたrequestをcallClassメソッドに渡します。
	 * callClassメソッドではclassパラメータの値を利用してクラスをロードし、処理を行います。
	 * 具体的な処理はcallClassメソッドをご覧下さい。
	 *
	 * @param request HttpServletRequestリクエスト
	 * @param response HttpServletResponseレスポンス
	 * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。
	 * @throws IOException 入出力エラーが発生したとき
	 */
	@Override
	public void doPost( final HttpServletRequest request, final HttpServletResponse response )	throws ServletException, IOException {
		callClass( request, response );
	}

	/**
	 * Getメソッドで与えられたrequestをcallClassメソッドに渡します。
	 * callClassメソッドではclassパラメータの値を利用してクラスをロードし、処理を行います。
	 * 具体的な処理はcallClassメソッドをご覧下さい。
	 *
	 * @param request HttpServletRequestリクエスト
	 * @param response HttpServletResponseレスポンス
	 * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。
	 * @throws IOException 入出力エラーが発生したとき
	 */
	@Override
	public void doGet( final HttpServletRequest request, final HttpServletResponse response )	throws ServletException, IOException {
		callClass( request, response );
	}

	/**
	 * Servlet の 初期値設定を行います。
	 *
	 * WEB-INF/web.xml ファイルで、&lt;servlet&gt; タグ内で初期値設定を行います。
	 * &lt;init-param&gt;
	 *     &lt;param-name&gt;filePattern&lt;/param-name&gt;
	 *     &lt;param-value&gt;c:/opengionV8|c:/temp&lt;/param-value&gt;
	 * &lt;/init-param&gt;
	 *
	 * @og.rev 7.2.9.4 (2020/11/20) ﾌｧｲﾙｱｸｾｽの制約(指定がない場合は、処理しない)。
	 *
	 * @param	config	ServletConfigオブジェクト
	 */
	@Override
	public void init( final ServletConfig config ) throws ServletException {
		super.init( config );

		final String filePtn = config.getInitParameter("filePattern");
		if( filePtn != null && !filePtn.isEmpty() ) {
			filePattern = Pattern.compile( filePtn );
		}
	}

	/**
	 * POSTとGETに対する実際の処理です
	 * 必須パラメータclassで与えられたクラス名でorg.opengion.hayabusa.remoteから
	 * クラスをロードし、MAPに格納したrequestパラメータをそのクラスに対して渡します。
	 * ロードするクラスはRemoteControllableを実装している必要があります。
	 * ロードしたクラスの処理が終了すると、返されたStringをresponseに出力して終了します。
	 * なお、classパラメータがnullの場合は何もせずに終了します。
	 *
	 * @og.rev 5.4.2.0 (2011/12/01) フォワード対応
	 * @og.rev 7.2.9.4 (2020/11/20) ﾌｧｲﾙｱｸｾｽの制約(指定がない場合は、処理しない)。
	 *
	 * @param request リクエスト
	 * @param response レスポンス
	 * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。
	 * @throws IOException 入出力エラーが発生したとき
	 */
	private void callClass( final HttpServletRequest request, final HttpServletResponse response ) throws ServletException, IOException {
		// 5.4.2.0 (2011/12/01) リクエストのエンコードを指定
		try {
			request.setCharacterEncoding( "UTF-8" );
		}
		catch( final UnsupportedEncodingException ex ) {
			throw new HybsSystemException( ex );
		}

		// 5.4.2.0 (2011/12/01) フォワード対応
		// 7.2.9.4 (2020/11/20) ﾌｧｲﾙｱｸｾｽの制約(指定がない場合は、処理しない)。
		final String file = request.getParameter( "file" );
//		if( file != null && file.length() > 0 ) {
		if( file != null && !file.isEmpty() && filePattern != null ) {			// 7.2.9.4 (2020/11/20)
			final Matcher match = filePattern.matcher( file );					// 7.2.9.4 (2020/11/20)
			if( match.matches() ) {												// 7.2.9.4 (2020/11/20)
				response.setContentType( "application/octet-stream" );
				// ファイル内容の出力
				FileInputStream     fin = null;
				ServletOutputStream out = null;
				try {
					// 対応済み：spotbugs:サーブレットの絶対パストラバーサル
					fin = new FileInputStream( file );
					out = response.getOutputStream();

					// ファイル読み込み用バッファ
					final byte buffer[]  = new byte[4096];
					int size;
					while((size = fin.read(buffer))!=-1) {
						out.write(buffer,0, size);
						out.flush();
					}
				}
				finally {
					Closer.ioClose( fin );
					Closer.ioClose( out );
				}
			}
		}
		else {
			final String clazz = request.getParameter( "class" ); // パラメータ"class"だけは必ず必要

			if( clazz == null ) {
				return; // 暫定処理
			}

			final Enumeration<?> paramEnm = request.getParameterNames();		// 4.3.3.6 (2008/11/15) Generics警告対応
			final HashMap<String,String> paramMap = new HashMap<>();
			while( paramEnm.hasMoreElements() ) {
				final String key = ( String )( paramEnm.nextElement() );
				paramMap.put( key, request.getParameter( key ) );
			}

			final RemoteControllable rmtC = HybsSystem.newInstance( REMOTE_PKG + clazz );
			final String rtnString = rmtC.remoteControl( paramMap );
			response.setContentType( "text/xml; charset=UTF-8" );
			final PrintWriter out = response.getWriter();
			out.println( rtnString );
			out.flush();
			out.close();
		}
	}
}
