changeset 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 24b05963ba62
children 8bd98da6991a
files core/src/luan/modules/IoLuan.java core/src/luan/modules/Rpc.luan core/src/luan/modules/RpcLuan.java core/src/luan/modules/Time.luan core/src/luan/modules/host/Hosting.luan
diffstat 5 files changed, 316 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/core/src/luan/modules/IoLuan.java	Wed Dec 09 18:12:16 2015 -0700
+++ b/core/src/luan/modules/IoLuan.java	Fri Dec 11 00:13:13 2015 -0700
@@ -206,6 +206,7 @@
 		public LuanTable table() {
 			LuanTable tbl = new LuanTable();
 			try {
+				tbl.rawPut( "java", this );
 				tbl.rawPut( "to_string", new LuanJavaFunction(
 					LuanIn.class.getMethod( "to_string" ), this
 				) );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/Rpc.luan	Fri Dec 11 00:13:13 2015 -0700
@@ -0,0 +1,38 @@
+java()
+local RpcLuan = require "java:luan.modules.RpcLuan"
+local Luan = require "luan:Luan"
+local error = Luan.error
+local set_metatable = Luan.set_metatable or error()
+local Io = require "luan:Io"
+local Thread = require "luan:Thread"
+
+
+local M = {}
+
+M.call = RpcLuan.call  -- Rpc.call(socket,fn_name,...)
+M.respond = RpcLuan.respond  -- Rpc.respond(socket,fns)
+
+function M.remote(socket_uri)
+	local mt = {}
+	function mt.__index(_,key)
+		return function(...)
+			local socket = Io.uri(socket_uri)
+			return M.call(socket,key,...)
+		end
+	end
+	local t = {}
+	set_metatable(t,mt)
+	return t
+end
+
+--[[
+function M.serve(port,fns)
+	local server = Io.socket_server(port)
+	while true do
+		local socket = server()
+		Thread.fork(function() M.respond(socket,fns) end)
+	end
+end
+]]
+
+return M
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/RpcLuan.java	Fri Dec 11 00:13:13 2015 -0700
@@ -0,0 +1,228 @@
+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() );
+	}
+
+}
--- a/core/src/luan/modules/Time.luan	Wed Dec 09 18:12:16 2015 -0700
+++ b/core/src/luan/modules/Time.luan	Fri Dec 11 00:13:13 2015 -0700
@@ -2,8 +2,8 @@
 
 java()
 local Luan = require "luan:Luan"
-local ipairs = Luan.ipairs
 local error = Luan.error
+local ipairs = Luan.ipairs or error()
 local Table = require "luan:Table"
 local System = require "java:java.lang.System"
 local Calendar = require "java:java.util.Calendar"
--- a/core/src/luan/modules/host/Hosting.luan	Wed Dec 09 18:12:16 2015 -0700
+++ b/core/src/luan/modules/host/Hosting.luan	Fri Dec 11 00:13:13 2015 -0700
@@ -1,36 +1,66 @@
 -- Hosting
 
-local Io = require "luan:Io"
 local Luan = require "luan:Luan"
 local error = Luan.error
+local ipairs = Luan.ipairs or error()
+local pairs = Luan.pairs or error()
+local Io = require "luan:Io"
+local print = Io.print or error()
+local Rpc = require "luan:Rpc"
+
 
 local M = {}
 
 M.port = 9101
 
 function M.push(domain,password,dir)
-	local f = Io.uri("file:"..dir)
-	f.exists() or error("directory '"..dir.."' not found")
-	f.is_directory() or error("'"..dir.."' is not a directory")
+	local my_dir = Io.uri("file:"..dir)
+	my_dir.exists() or error("directory '"..dir.."' not found")
+	my_dir.is_directory() or error("'"..dir.."' is not a directory")
 	local socket = "socket:" .. domain .. ":" .. M.port
-	local pc = Io.uri(socket).pickle_client()
-	local pickle = pc.pickle
-	pc.call(%>
-		local Hosting = require "luan:host/Hosting"
-		Hosting.do_push(<%=pickle(domain)%>,<%=pickle(password)%>,<%=pickle(dir)%>)
-	<%)
-	pc.close()
+	local host = Rpc.remote(socket)
+	local tree = host.get(domain,password)
+	if tree == nil then
+		print("creating "..domain)
+		tree = host.create(domain,password)
+	end
+
+	local function process(there_parent,there,here)
+		if here.is_file() then
+			if there == nil or there.last_modified < here.last_modified() then
+				print("copying "..here.to_string())
+				host.copy_file(domain,password,there_parent.path,here.name(),here.read_binary())
+			end
+		elseif here.is_directory() then
+			if here.name() == "local" then
+				return
+			end
+			if there == nil then
+				there = host.mkdir(domain,password,there_parent.path,here.name())
+			end
+			for _, here_child in ipairs(here.children()) do
+				local name = here_child.name()
+				process(there,there.children[name],here_child)
+				there.children[name] = nil
+			end
+			for _, there_child in pairs(there.children) do
+				print("deleting "..there_child.name)
+				host.delete_unused(domain,password,there_child.path)
+			end
+		else
+			error "not file or dir"
+		end
+	end
+
+	process( nil, tree, my_dir )
+
+	host.remove_handler(domain,password)
 end
 
 function M.delete(domain,password)
 	local socket = "socket:" .. domain .. ":" .. M.port
-	local pc = Io.uri(socket).pickle_client()
-	local pickle = pc.pickle
-	pc.call(%>
-		local Hosting = require "luan:host/Hosting"
-		Hosting.do_delete(<%=pickle(domain)%>,<%=pickle(password)%>)
-	<%)
-	pc.close()
+	local host = Rpc.remote(socket)
+	host.delete(domain,password)
 end
 
 return M