view core/src/luan/modules/RpcLuan.java @ 610:b4f3dbe1c6e3

add Rpc and change Hosting to use Rpc
author Franklin Schmidt <fschmidt@gmail.com>
date Fri, 11 Dec 2015 00:13:13 -0700
parents
children cdc70de628b5
line wrap: on
line source

package luan.modules;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.EOFException;
import java.nio.charset.StandardCharsets;
import java.util.Set;
import java.util.IdentityHashMap;
import java.util.Collections;
import java.util.Map;
import luan.Luan;
import luan.LuanState;
import luan.LuanTable;
import luan.LuanFunction;
import luan.LuanException;
import luan.LuanMethod;


public final class RpcLuan {
	private static final int NIL = 0;
	private static final int STRING = 1;
	private static final int BOOLEAN = 2;
	private static final int NUMBER = 3;
	private static final int BINARY = 4;
	private static final int TABLE = 5;

	@LuanMethod public static Object[] call(LuanState luan,LuanTable socketTbl,String fnName,Object... args)
		throws LuanException, IOException
	{
		IoLuan.LuanSocket socket = (IoLuan.LuanSocket)socketTbl.rawGet("java");
		InputStream in = new BufferedInputStream(socket.inputStream());
		OutputStream out = new BufferedOutputStream(socket.outputStream());
		try {
			writeString(out,fnName);
			writeInt(out,args.length);
			for( Object arg : args ) {
				writeObj(out,luan,arg);
			}
			out.flush();
			boolean ok = readBoolean(in);
			if( ok ) {
				int n = readInt(in);
				Object[] rtn = new Object[n];
				for( int i=0; i<n; i++ ) {
					rtn[i] = readObj(in,luan);
				}
				return rtn;
			} else {
				String msg = readString(in);
				throw new LuanException(luan,msg);
			}
		} finally {
			out.close();
			in.close();
		}
	}

	public static void respond(LuanState luan,LuanTable socketTbl,LuanTable fns)
		throws IOException, LuanException
	{
		IoLuan.LuanSocket socket = (IoLuan.LuanSocket)socketTbl.rawGet("java");
		InputStream in = new BufferedInputStream(socket.inputStream());
		OutputStream out = new BufferedOutputStream(socket.outputStream());
		try {
			Object[] rtn;
			try {
				String fnName = readString(in);
				int nArgs = readInt(in);
				Object[] args = new Object[nArgs];
				for( int i=0; i<nArgs; i++ ) {
					args[i] = readObj(in,luan);
				}
				LuanFunction fn = (LuanFunction)fns.rawGet(fnName);
				if( fn == null )
					throw new LuanException(luan, "function not found: " + fnName );
				rtn = Luan.array(fn.call(luan,args));
			} catch(LuanException e) {
				writeBoolean(out,false);
				writeString(out,e.getFullMessage());
				return;
			}
			writeBoolean(out,true);
			writeInt(out,rtn.length);
			for( Object obj : rtn ) {
				writeObj(out,luan,obj);
			}
		} finally {
			out.close();
			in.close();
		}
	}

	static void writeObj(OutputStream out,LuanState luan,Object obj) throws IOException, LuanException {
		if( obj == null ) {
			out.write(NIL);
		}
		else if( obj instanceof String ) {
			out.write(STRING);
			writeString(out,(String)obj);
		}
		else if( obj instanceof Boolean ) {
			out.write(BOOLEAN);
			writeBoolean(out,(Boolean)obj);
		}
		else if( obj instanceof Number ) {
			out.write(NUMBER);
			writeString(out,obj.toString());
		}
		else if( obj instanceof byte[] ) {
			byte[] a = (byte[])obj;
			out.write(BINARY);
			writeInt(out,a.length);
			out.write(a);
		}
		else if( obj instanceof LuanTable ) {
			out.write(TABLE);
			String s = pickle( luan, obj, Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) );
			writeString(out,s);
		}
		else
			throw new LuanException(luan, "invalid type: " + obj.getClass() );
	}

	static Object readObj(InputStream in,LuanState luan) throws IOException, LuanException {
		int type = in.read();
		switch(type) {
		case NIL:
			return null;
		case STRING:
			return readString(in);
		case BOOLEAN:
			return readBoolean(in);
		case NUMBER:
			return Double.valueOf(readString(in));
		case BINARY:
			return readBinary(in,readInt(in));
		case TABLE:
			String s = readString(in);
			LuanFunction fn = BasicLuan.load(luan,s,"rpc-reader",null,true);
			return fn.call(luan);
		default:
			throw new LuanException(luan, "invalid type: " + type );
		}
	}

	static Boolean readBoolean(InputStream in) throws IOException {
		return Boolean.valueOf(readString(in));
	}

	static String readString(InputStream in) throws IOException {
		int len = readInt(in);
		byte[] a = readBinary(in,len);
		return new String(a,StandardCharsets.UTF_8);
	}

	static int readInt(InputStream in) throws IOException {
		int ch1 = in.read();
		int ch2 = in.read();
		int ch3 = in.read();
		int ch4 = in.read();
		if ((ch1 | ch2 | ch3 | ch4) < 0)
			throw new EOFException();
		return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
	}

	static byte[] readBinary(InputStream in,int size) throws IOException {
		byte[] a = new byte[size];
		int i = 0;
		while( i < size ) {
			int n = in.read(a,i,size-i);
			if( n == -1 )
				throw new EOFException();
			i += n;
		}
		return a;
	}

	static void writeBoolean(OutputStream out,Boolean b) throws IOException {
		writeString(out,b.toString());
	}

	static void writeString(OutputStream out,String s) throws IOException {
		byte[] a = s.getBytes(StandardCharsets.UTF_8);
		writeInt(out,a.length);
		out.write(a);
	}

	static void writeInt(OutputStream out,int v) throws IOException {
        out.write((v >>> 24) & 0xFF);
        out.write((v >>> 16) & 0xFF);
        out.write((v >>>  8) & 0xFF);
        out.write((v >>>  0) & 0xFF);
	}


	private static String pickle(LuanState luan,Object obj,Set<LuanTable> set) throws LuanException {
		if( obj == null )
			return "nil";
		if( obj instanceof Boolean )
			return obj.toString();
		if( obj instanceof Number )
			return Luan.toString((Number)obj);
		if( obj instanceof String )
			return "\"" + Luan.stringEncode((String)obj) + "\"";
		if( obj instanceof LuanTable ) {
			LuanTable tbl = (LuanTable)obj;
			if( !set.add(tbl) ) {
				throw new LuanException(luan, "circular reference in table" );
			}
			StringBuilder sb = new StringBuilder();
			sb.append( "{" );
			for( Map.Entry<Object,Object> entry : tbl.iterable(luan) ) {
				sb.append( "[" );
				sb.append( pickle(luan,entry.getKey(),set) );
				sb.append( "]=" );
				sb.append( pickle(luan,entry.getValue(),set) );
				sb.append( ", " );
			}
			sb.append( "}" );
			return sb.toString();
		}
		throw new LuanException(luan, "invalid type: " + obj.getClass() );
	}

}