/**
* Kandata
* Copyright (C) 2003. Yoshinori Watanabe
* 
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* 
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
* 
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package kandata.client.http;

import java.io.*;
import java.net.*;
import java.lang.reflect.*;
import kandata.client.*;
import kandata.KandataServerException;

/**
 * HTTPを使ってサーバ側に接続する.
 * クライアントアプリはこのクラスによって起動され、その後は
 * このクラスによってサーバ側と通信を行う。
 */
public class HttpConnector implements KandataConnector {
	static final boolean TRACE_ENABLE = true;
	static final void TRACE(String message) { if (TRACE_ENABLE) System.out.println(message); }

	private URL url = null;
	private HttpURLConnection conn = null;
	HttpClassLoader httpClassLoader;
	private String id = null;

	/**
	 * HttpConnectorを初期化する.
	 * このクラスは main()からでしか生成できない.
	 */
	private HttpConnector() {
		httpClassLoader = new HttpClassLoader(this);
	}

	/**
	 * 接続する.
	 */
	public void connect(URL url) throws KandataClientException, KandataServerException {
		TRACE("HttpConnector.connect("+url+")");
		this.url = url;
		try {
			this.conn = (HttpURLConnection) url.openConnection();
			this.conn.setDoOutput(true);
		} catch (IOException ex) {
			throw new KandataClientException("connect failed.", ex);
		}
		id = (String) invoke("@connect", null);
	}

	/**
	 * 切断する.
	 * Connectorインターフェースの実装
	 */
	public void disconnect() throws KandataClientException, KandataServerException {
		TRACE("HttpConnector.disconnect()");
		// 切断を通知
		invoke("@disconnect", null);
		// 切断
		if (conn != null) {
			conn.disconnect();
			conn = null;
		}
	}

	/**
	 * サーバ側のメソッドを呼び出す.
	 * Connectorインターフェースの実装
	 */
	public Object invoke(String command, Object[] args) throws KandataClientException, KandataServerException {
		TRACE("HttpConnector.invoke("+command+", args);");
		if (args != null) {
			for (int i=0; i<args.length; i++) {
				TRACE("\targs["+i+"]="+args[i]);
			}
		}
		try {
			conn = (HttpURLConnection) url.openConnection();
			conn.setDoOutput(true);
			// POSTメソッドを使う
			ObjectOutputStream out = new ObjectOutputStream(conn.getOutputStream());
			out.writeObject(id);
			out.writeObject(command);
			out.writeInt(((args!=null) ? args.length : 0));
			if (args != null) {
				for (int i=0; i<args.length; i++) {
					out.writeObject(args[i]);
				}
			}
			out.close();

			conn.connect();

			ObjectInputStream in = new HttpObjectInputStream(conn.getInputStream(), httpClassLoader);
			Object ret = in.readObject();
			KandataServerException ex = (KandataServerException) in.readObject();
			in.close();
			TRACE("\treturn "+ret);
			conn.disconnect();
			// もしサーバ側でKandataServerExceptionが発生していたら、例外を発生させる。
			if (ex != null) throw ex;
			return ret;
		} catch (IOException ex) {
			throw new KandataClientException("invoke failed.", ex);
		} catch (ClassNotFoundException ex) {
			throw new KandataClientException("invoke failed.", ex);
		}
	}

	/**
	 * サーバ側のリソースを取得する.
	 * Connectorインターフェースの実装
	 */
	public byte[] getResource(String path) throws KandataClientException, KandataServerException {
		TRACE("HttpConnector.getResource("+url+")");
		return (byte[]) invoke("@getResource", new Object[] { path });
	}

	/**
	 * サーバ側にあるクライアントのクラスファイルを取得する.
	 */
	public byte[] getClass(String classname) throws KandataClientException, KandataServerException {
		TRACE("HttpConnector.getClass("+url+")");
		return (byte[]) invoke("@getClass", new Object[] { classname });
	}

	/**
	 * エントリのクラスを取得する.
	 */
	public String getEntryClassName() throws KandataClientException, KandataServerException {
		return (String) invoke("@getEntryClassName", null);
	}

	/**
	 * Kandataアプリケーションを実行する.
	 */
	public static void main(String[] args) throws Exception {
		// 引数がないときには使用方法を表示する.
		if (args.length < 1) {
			System.err.println("usage: java kandata.client.http.HttpConnector <url> [<client-type> [<version>]]");
			System.exit(-1);
		}
		String url = args[0];
		String clientType = args.length > 1 ? args[1] : null;	// TODO:どうやって使う？
		String version    = args.length > 2 ? args[2] : null;	// TODO:どうやって使う？
		// サーバに接続する
		HttpConnector connector = new HttpConnector();
		connector.connect(new URL(args[0]));

		// エントリクラスをもらう
		String entry_classname = connector.getEntryClassName();

		// セキュリティマネージャを設定する。TODO:どうやってアプリごとにセキュリティを設定する？
//		System.out.println("SecurityManager="+System.getSecurityManager());
//		System.setSecurityManager(new java.rmi.RMISecurityManager());
//		System.out.println("SecurityManager="+System.getSecurityManager());

		// クライアントアプリを、HttpClassLoaderを介して読み込み、実行する。
		Class entry_class = Class.forName(entry_classname, true, connector.httpClassLoader);
		KandataClient client = (KandataClient) entry_class.newInstance();
		client.init(connector);
	}
}
