veyron/runtimes/google/ipc/jni: Add streaming support to Java Veyron Runtime.
Change-Id: I6cf0058da5c6f44209d397e49c4e12c4776dc790
diff --git a/runtimes/google/ipc/jni/arg_getter.go b/runtimes/google/ipc/jni/arg_getter.go
index 25c2003..7bd27c5 100644
--- a/runtimes/google/ipc/jni/arg_getter.go
+++ b/runtimes/google/ipc/jni/arg_getter.go
@@ -1,14 +1,11 @@
-// +build android
-
package main
import (
"fmt"
"path"
"reflect"
- "strings"
- // Imported IDLs. Please add a link to all IDLs you care about here,
+ // Imported VDLs. Please add a link to all VDLs you care about here,
// and add all interfaces you care about to the init() function below.
"veyron/examples/fortune"
"veyron2/ipc"
@@ -22,7 +19,7 @@
// A list of all registered argGetter-s.
var register map[string]*argGetter = make(map[string]*argGetter)
-// registerInterface registers the provided IDL client or server interface
+// registerInterface registers the provided VDL client or server interface
// so that its methods' arguments can be created on-the-fly.
func registerInterface(ifacePtr interface{}) {
t := reflect.TypeOf(ifacePtr)
@@ -34,137 +31,207 @@
panic(fmt.Sprintf("expected interface type for %q, got: %v", ifacePtr, t.Kind()))
}
+ contextType := reflect.TypeOf((*ipc.Context)(nil)).Elem()
+ optType := reflect.TypeOf(([]ipc.ClientOpt)(nil))
// Create a new arg getter.
- methods := make(map[string][]methodInfo)
+ methods := make(map[string][]*methodArgs)
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
- in := make([]reflect.Type, m.Type.NumIn()-1)
- idx := 0
- contextType := reflect.TypeOf((*ipc.ServerContext)(nil)).Elem()
- optType := reflect.TypeOf((*ipc.CallOpt)(nil)).Elem()
+ var mArgs methodArgs
for j := 0; j < m.Type.NumIn(); j++ {
argType := m.Type.In(j)
- if j == 0 && argType == contextType { // skip the Context argument.
+ if j == 0 && argType == contextType { // (service) Context argument - ignore it.
continue
}
- if j == m.Type.NumIn()-1 && argType == optType { // skip the CallOption argument.
- continue
+ if j == m.Type.NumIn()-1 {
+ if argType.Kind() == reflect.Interface { // (service) stream argument.
+ if err := fillStreamArgs(argType, &mArgs); err != nil {
+ panic(err.Error())
+ }
+ continue
+ }
+ if argType == optType { // (client) CallOption argument - ignore it.
+ continue
+ }
}
- in[idx] = argType
- idx++
+ mArgs.inTypes = append(mArgs.inTypes, argType)
}
- out := make([]reflect.Type, m.Type.NumOut()-1) // skip error argument
for j := 0; j < m.Type.NumOut()-1; j++ {
- out[j] = m.Type.Out(j)
+ argType := m.Type.Out(j)
+ if j == 0 && argType.Kind() == reflect.Interface { // (client) stream argument
+ if err := fillStreamArgs(argType, &mArgs); err != nil {
+ panic(err.Error())
+ }
+ continue
+ }
+ mArgs.outTypes = append(mArgs.outTypes, argType)
}
- mis := methods[m.Name]
- mis = append(mis, methodInfo{
- inTypes: in,
- outTypes: out,
- })
- methods[m.Name] = mis
+ methods[m.Name] = append(methods[m.Name], &mArgs)
}
path := path.Join(t.PkgPath(), t.Name())
register[path] = &argGetter{
methods: methods,
- idlPath: path,
+ vdlPath: path,
}
}
-// newPtrInstance returns the pointer to the new instance of the provided type.
-func newPtrInstance(t reflect.Type) interface{} {
- return reflect.New(t).Interface()
+// fillStreamArgs fills in stream argument types for the provided stream.
+func fillStreamArgs(stream reflect.Type, mArgs *methodArgs) error {
+ // Get the stream send type.
+ if mSend, ok := stream.MethodByName("Send"); ok {
+ if mSend.Type.NumIn() != 1 {
+ return fmt.Errorf("Illegal number of arguments for Send method in stream %v", stream)
+ }
+ mArgs.streamSendType = mSend.Type.In(0)
+ }
+ // Get the stream recv type.
+ if mRecv, ok := stream.MethodByName("Recv"); ok {
+ if mRecv.Type.NumOut() != 2 {
+ return fmt.Errorf("Illegal number of arguments for Recv method in stream %v", stream)
+ }
+ mArgs.streamRecvType = mRecv.Type.Out(0)
+ }
+ if mArgs.streamSendType == nil && mArgs.streamRecvType == nil {
+ return fmt.Errorf("Both stream in and out arguments cannot be nil in stream %v", stream)
+ }
+ // Get the stream finish types.
+ if mFinish, ok := stream.MethodByName("Finish"); ok && mFinish.Type.NumOut() > 1 {
+ for i := 0; i < mFinish.Type.NumOut()-1; i++ {
+ mArgs.streamFinishTypes = append(mArgs.streamFinishTypes, mFinish.Type.Out(i))
+ }
+ }
+ return nil
}
-// newArgGetter returns the argument getter for the provided IDL interface.
-func newArgGetter(javaIdlIfacePath string) *argGetter {
- return register[strings.Join(strings.Split(javaIdlIfacePath, ".")[1:], "/")]
+// newArgGetter returns the argument getter for the provided VDL interface.
+func newArgGetter(vdlIfacePath string) *argGetter {
+ return register[vdlIfacePath]
}
// argGetter serves method arguments for a specific interface.
type argGetter struct {
- methods map[string][]methodInfo
- idlPath string
+ methods map[string][]*methodArgs
+ vdlPath string
}
-// methodInfo contains argument type information for a method belonging to an interface.
-type methodInfo struct {
- inTypes []reflect.Type
- outTypes []reflect.Type
-}
-
-func (m methodInfo) String() string {
- in := fmt.Sprintf("[%d]", len(m.inTypes))
- out := fmt.Sprintf("[%d]", len(m.outTypes))
- for _, t := range m.inTypes {
- in = in + ", " + t.Name()
+func (ag *argGetter) String() (ret string) {
+ ret = "VDLPath: " + ag.vdlPath
+ for k, v := range ag.methods {
+ for _, m := range v {
+ ret += "; "
+ ret += fmt.Sprintf("Method: %s, Args: %v", k, m)
+ }
}
- for _, t := range m.outTypes {
- out = out + ", " + t.Name()
- }
- return fmt.Sprintf("(%s; %s)", in, out)
+ return
}
-// findMethod returns the method type information for the given method, or nil if
+// FindMethod returns the method type information for the given method, or nil if
// the method doesn't exist.
-func (ag *argGetter) findMethod(method string, numInArgs int) *methodInfo {
+func (ag *argGetter) FindMethod(method string, numInArgs int) *methodArgs {
ms, ok := ag.methods[method]
if !ok {
return nil
}
- var m *methodInfo
+ var m *methodArgs
for _, mi := range ms {
if len(mi.inTypes) == numInArgs {
- m = &mi
+ m = mi
break
}
}
return m
}
-// GetInArgTypes returns types of all input arguments for the given method.
-func (ag *argGetter) GetInArgTypes(method string, numInArgs int) ([]reflect.Type, error) {
- m := ag.findMethod(method, numInArgs)
- if m == nil {
- return nil, fmt.Errorf("couldn't find method %q with %d args in path %s", method, numInArgs, ag.idlPath)
- }
- return m.inTypes, nil
+// argGetters is a cache of created argument getters, keyed by VDL interface path.
+var argGetters map[string]*argGetter = make(map[string]*argGetter)
+
+// method contains argument type information for a method belonging to an interface.
+type methodArgs struct {
+ inTypes []reflect.Type
+ outTypes []reflect.Type
+ streamSendType reflect.Type
+ streamRecvType reflect.Type
+ streamFinishTypes []reflect.Type
}
-// GenInArgPtrs returns pointers to instances of all input arguments for the given method.
-func (ag *argGetter) GetInArgPtrs(method string, numInArgs int) (argptrs []interface{}, err error) {
- m := ag.findMethod(method, numInArgs)
- if m == nil {
- return nil, fmt.Errorf("couldn't find method %q with %d args in path %s", method, numInArgs, ag.idlPath)
+func (m *methodArgs) String() string {
+ in := fmt.Sprintf("[%d]", len(m.inTypes))
+ out := fmt.Sprintf("[%d]", len(m.outTypes))
+ streamFinish := fmt.Sprintf("[%d]", len(m.streamFinishTypes))
+ streamSend := "<nil>"
+ streamRecv := "<nil>"
+ for idx, t := range m.inTypes {
+ if idx > 0 {
+ in += ", "
+ }
+ in += t.Name()
}
- argptrs = make([]interface{}, len(m.inTypes))
+ for idx, t := range m.outTypes {
+ if idx > 0 {
+ out += ", "
+ }
+ out += t.Name()
+ }
+ if m.streamSendType != nil {
+ streamSend = m.streamSendType.Name()
+ }
+ if m.streamRecvType != nil {
+ streamRecv = m.streamRecvType.Name()
+ }
+ for idx, t := range m.streamFinishTypes {
+ if idx > 0 {
+ streamFinish += ", "
+ }
+ streamFinish += t.Name()
+ }
+ return fmt.Sprintf("(In: %s; Out: %s; streamSend: %s; StreamRecv: %s; StreamFinish: %s)", in, out, streamSend, streamRecv, streamFinish)
+}
+
+func (m *methodArgs) IsStreaming() bool {
+ return m.streamSendType != nil || m.streamRecvType != nil
+}
+
+// GenInPtrs returns pointers to instances of all input arguments.
+func (m *methodArgs) InPtrs() []interface{} {
+ argptrs := make([]interface{}, len(m.inTypes))
for i, arg := range m.inTypes {
argptrs[i] = newPtrInstance(arg)
}
- return
+ return argptrs
}
-// GetOurArgTypes returns types of all output arguments for the given method.
-func (ag *argGetter) GetOutArgTypes(method string, numInArgs int) ([]reflect.Type, error) {
- m := ag.findMethod(method, numInArgs)
- if m == nil {
- return nil, fmt.Errorf("couldn't find method %q with %d args in path %s", method, numInArgs, ag.idlPath)
- }
- return m.outTypes, nil
-}
-
-// GetOutArgPtrs returns pointers to instances of all output arguments for the given method.
-func (ag *argGetter) GetOutArgPtrs(method string, numInArgs int) (argptrs []interface{}, err error) {
- m := ag.findMethod(method, numInArgs)
- if m == nil {
- return nil, fmt.Errorf("couldn't find method %q with %d args in path %s", method, numInArgs, ag.idlPath)
- }
- argptrs = make([]interface{}, len(m.outTypes))
+// OutPtrs returns pointers to instances of all output arguments.
+func (m *methodArgs) OutPtrs() []interface{} {
+ argptrs := make([]interface{}, len(m.outTypes))
for i, arg := range m.outTypes {
argptrs[i] = newPtrInstance(arg)
}
- return
+ return argptrs
}
-// argGetters is a cache of created argument getters, keyed by IDL interface path.
-var argGetters map[string]*argGetter = make(map[string]*argGetter)
+// StreamSendPtr returns a pointer to an instance of a stream send type.
+func (m *methodArgs) StreamSendPtr() interface{} {
+ return newPtrInstance(m.streamSendType)
+}
+
+// StreamRecvPtr returns a pointer to an instance of a stream recv type.
+func (m *methodArgs) StreamRecvPtr() interface{} {
+ return newPtrInstance(m.streamRecvType)
+}
+
+// StreamFinishPtrs returns pointers to instances of stream finish types.
+func (m *methodArgs) StreamFinishPtrs() []interface{} {
+ argptrs := make([]interface{}, len(m.streamFinishTypes))
+ for i, arg := range m.streamFinishTypes {
+ argptrs[i] = newPtrInstance(arg)
+ }
+ return argptrs
+}
+
+// newPtrInstance returns the pointer to the new instance of the provided type.
+func newPtrInstance(t reflect.Type) interface{} {
+ if t == nil {
+ return nil
+ }
+ return reflect.New(t).Interface()
+}
diff --git a/runtimes/google/ipc/jni/arg_getter_test.go b/runtimes/google/ipc/jni/arg_getter_test.go
new file mode 100644
index 0000000..4694ab6
--- /dev/null
+++ b/runtimes/google/ipc/jni/arg_getter_test.go
@@ -0,0 +1,64 @@
+package main
+
+import (
+ "reflect"
+ "testing"
+
+ "veyron2/vdl/test_base"
+)
+
+func init() {
+ registerInterface((*test_base.ServiceB)(nil))
+}
+
+func compareType(t *testing.T, method string, got, want interface{}, argKind string) {
+ if gotT, wantT := reflect.TypeOf(got), reflect.TypeOf(want); gotT != wantT {
+ t.Errorf("type mismatch in %q's %s argument: got %v , want %v ", method, argKind, gotT, wantT)
+ }
+}
+
+func compareTypes(t *testing.T, method string, got, want []interface{}, argKind string) {
+ if len(got) != len(want) {
+ t.Errorf("mismatch in input arguments: got %v , want %v ", got, want)
+ }
+ for i, _ := range got {
+ compareType(t, method, got[i], want[i], argKind)
+ }
+}
+
+func TestGetter(t *testing.T) {
+ iface := "veyron2/vdl/test_base/ServiceB"
+ getter := newArgGetter(iface)
+ if getter == nil {
+ t.Fatalf("no getter for interface: %v ", iface)
+ }
+ if got, want := getter.vdlPath, iface; got != want {
+ t.Errorf("invalid pathname: got %v , want %v ", got, want)
+ }
+ data := []struct{
+ Method string
+ NumInArgs int
+ in, out []interface{}
+ sSend, sRecv interface{}
+ sFinish []interface{}
+ }{
+ {"MethodA1", 0, nil, nil, nil, nil, nil},
+ {"MethodA2", 2, []interface{}{(*int32)(nil), (*string)(nil)}, []interface{}{(*string)(nil)}, nil, nil, nil},
+ {"MethodA3", 1, []interface{}{(*int32)(nil)}, nil, nil, (*test_base.Scalars)(nil), []interface{}{(*string)(nil)}},
+ {"MethodA4", 1, []interface{}{(*int32)(nil)}, nil, (*int32)(nil), (*string)(nil), nil},
+ {"MethodB1", 2, []interface{}{(*test_base.Scalars)(nil), (*test_base.Composites)(nil)}, []interface{}{(*test_base.CompComp)(nil)}, nil, nil, nil},
+ }
+ for _, d := range data {
+ m := getter.FindMethod(d.Method, d.NumInArgs)
+ if m == nil {
+ t.Errorf("couldn't find method %q with %d args", d.Method, d.NumInArgs)
+ continue
+ }
+ // Compare arguments.
+ compareTypes(t, d.Method, m.InPtrs(), d.in, "input")
+ compareTypes(t, d.Method, m.OutPtrs(), d.out, "output")
+ compareType(t, d.Method, m.StreamSendPtr(), d.sSend, "stream send")
+ compareType(t, d.Method, m.StreamRecvPtr(), d.sRecv, "stream recv")
+ compareTypes(t, d.Method, m.StreamFinishPtrs(), d.sFinish, "stream finish")
+ }
+}
\ No newline at end of file
diff --git a/runtimes/google/ipc/jni/client.go b/runtimes/google/ipc/jni/client.go
index 10e4474..571f8ae 100644
--- a/runtimes/google/ipc/jni/client.go
+++ b/runtimes/google/ipc/jni/client.go
@@ -5,11 +5,11 @@
import (
"encoding/json"
"fmt"
+ "strings"
"time"
"veyron2"
"veyron2/ipc"
- "veyron2/rt"
)
// #include <stdlib.h>
@@ -20,17 +20,13 @@
client ipc.Client
}
-func newClient() (*client, error) {
- c, err := rt.R().NewClient()
- if err != nil {
- return nil, err
- }
+func newClient(c ipc.Client) *client {
return &client{
client: c,
- }, nil
+ }
}
-func (c *client) StartCall(env *C.JNIEnv, name, method string, jArgs C.jobjectArray, jPath C.jstring, jTimeout C.jlong) (*clientCall, error) {
+func (c *client) StartCall(env *C.JNIEnv, jContext C.jobject, name, method string, jArgs C.jobjectArray, jPath C.jstring, jTimeout C.jlong) (*clientCall, error) {
// NOTE(spetrovic): In the long-term, we will decode JSON arguments into an
// array of vom.Value instances and send this array across the wire.
@@ -40,11 +36,15 @@
argStrs[i] = goString(env, C.jstring(C.GetObjectArrayElement(env, jArgs, C.jsize(i))))
}
// Get argument instances that correspond to the provided method.
- getter := newArgGetter(goString(env, jPath))
+ getter := newArgGetter(strings.Join(strings.Split(goString(env, jPath), ".")[1:], "/"))
if getter == nil {
return nil, fmt.Errorf("couldn't find IDL interface corresponding to path %q", goString(env, jPath))
}
- argptrs, err := getter.GetInArgPtrs(method, len(argStrs))
+ mArgs := getter.FindMethod(method, len(argStrs))
+ if mArgs == nil {
+ return nil, fmt.Errorf("couldn't find method %s with %d args in IDL interface at path %q, getter: %v", method, len(argStrs), goString(env, jPath), getter)
+ }
+ argptrs := mArgs.InPtrs()
if len(argptrs) != len(argStrs) {
return nil, fmt.Errorf("invalid number of arguments for method %s, want %d, have %d", method, len(argStrs), len(argptrs))
}
@@ -63,18 +63,18 @@
if int(jTimeout) >= 0 {
options = append(options, veyron2.CallTimeout(time.Duration(int(jTimeout))*time.Millisecond))
}
- // Invoke StartCall
- call, err := c.client.StartCall(name, method, args, options...)
+ context, err := newContext(env, jContext)
if err != nil {
return nil, err
}
- resultptrs, err := getter.GetOutArgPtrs(method, len(argStrs))
+ // Invoke StartCall
+ call, err := c.client.StartCall(context, name, method, args, options...)
if err != nil {
return nil, err
}
return &clientCall{
- call: call,
- resultptrs: resultptrs,
+ stream: newStream(call, mArgs),
+ call: call,
}, nil
}
@@ -83,24 +83,29 @@
}
type clientCall struct {
- call ipc.Call
- resultptrs []interface{}
+ stream
+ call ipc.Call
}
func (c *clientCall) Finish(env *C.JNIEnv) (C.jobjectArray, error) {
+ var resultptrs []interface{}
+ if c.mArgs.IsStreaming() {
+ resultptrs = c.mArgs.StreamFinishPtrs()
+ } else {
+ resultptrs = c.mArgs.OutPtrs()
+ }
// argGetter doesn't store the (mandatory) error result, so we add it here.
var appErr error
- if err := c.call.Finish(append(c.resultptrs, &appErr)...); err != nil {
+ if err := c.call.Finish(append(resultptrs, &appErr)...); err != nil {
// invocation error
return nil, fmt.Errorf("Invocation error: %v", err)
}
if appErr != nil { // application error
return nil, appErr
}
-
- // JSON encode results.
- jsonResults := make([][]byte, len(c.resultptrs))
- for i, resultptr := range c.resultptrs {
+ // JSON encode the results.
+ jsonResults := make([][]byte, len(resultptrs))
+ for i, resultptr := range resultptrs {
// Remove the pointer from the result. Simply *resultptr doesn't work
// as resultptr is of type interface{}.
result := derefOrDie(resultptr)
@@ -110,7 +115,6 @@
return nil, fmt.Errorf("error marshalling %q into JSON", resultptr)
}
}
-
// Convert to Java array of C.jstring.
ret := C.NewObjectArray(env, C.jsize(len(jsonResults)), jStringClass, nil)
for i, result := range jsonResults {
diff --git a/runtimes/google/ipc/jni/context.go b/runtimes/google/ipc/jni/context.go
new file mode 100644
index 0000000..cecb04e
--- /dev/null
+++ b/runtimes/google/ipc/jni/context.go
@@ -0,0 +1,38 @@
+// +build android
+
+package main
+
+import (
+ "fmt"
+ "runtime"
+)
+
+// #include "jni_wrapper.h"
+import "C"
+
+func newContext(env *C.JNIEnv, jContext C.jobject) (*context, error) {
+ var jVM *C.JavaVM
+ if status := C.GetJavaVM(env, &jVM); status != 0 {
+ return nil, fmt.Errorf("couldn't get Java VM from the (Java) environment")
+ }
+ // Reference Java context; it will be de-referenced when the go context
+ // created below is garbage-collected (through the finalizer callback we
+ // setup just below).
+ jContext = C.NewGlobalRef(env, jContext)
+ c := &context{
+ jVM: jVM,
+ jContext: jContext,
+ }
+ runtime.SetFinalizer(c, func(c *context) {
+ var env *C.JNIEnv
+ C.AttachCurrentThread(c.jVM, &env, nil)
+ defer C.DetachCurrentThread(c.jVM)
+ C.DeleteGlobalRef(env, c.jContext)
+ })
+ return c, nil
+}
+
+type context struct {
+ jVM *C.JavaVM
+ jContext C.jobject
+}
diff --git a/runtimes/google/ipc/jni/invoker.go b/runtimes/google/ipc/jni/invoker.go
index 4a8954c..74a1c90 100644
--- a/runtimes/google/ipc/jni/invoker.go
+++ b/runtimes/google/ipc/jni/invoker.go
@@ -6,6 +6,7 @@
"encoding/json"
"fmt"
"runtime"
+ "strings"
"veyron2/ipc"
"veyron2/security"
@@ -28,6 +29,9 @@
// static jobject CallGetInterfacePath(JNIEnv* env, jobject obj, jmethodID id) {
// return (*env)->CallObjectMethod(env, obj, id);
// }
+// static jobject CallNewServerCallObject(JNIEnv* env, jclass class, jmethodID id, jlong ref) {
+// return (*env)->NewObject(env, class, id, ref);
+// }
import "C"
func newJNIInvoker(env *C.JNIEnv, jVM *C.JavaVM, jObj C.jobject) (ipc.Invoker, error) {
@@ -40,13 +44,13 @@
// Fetch the argGetter for the object.
pid := jMethodID(env, jIDLInvokerClass, "getInterfacePath", fmt.Sprintf("()%s", stringSign))
jPath := C.jstring(C.CallGetInterfacePath(env, jInvoker, pid))
- getter := newArgGetter(goString(env, jPath))
+ getter := newArgGetter(strings.Join(strings.Split(goString(env, jPath), ".")[1:], "/"))
if getter == nil {
return nil, fmt.Errorf("couldn't find IDL interface corresponding to path %q", goString(env, jPath))
}
// Reference Java invoker; it will be de-referenced when the go invoker
// created below is garbage-collected (through the finalizer callback we
- // also setup below).
+ // setup just below).
jInvoker = C.NewGlobalRef(env, jInvoker)
i := &jniInvoker{
jVM: jVM,
@@ -74,7 +78,12 @@
// arguments into vom.Value objects, which we shall then de-serialize into
// Java objects (see Invoke comments below). This approach is blocked on
// pending VOM encoder/decoder changes as well as Java (de)serializer.
- argptrs, err = i.argGetter.GetInArgPtrs(method, numArgs)
+ mArgs := i.argGetter.FindMethod(method, numArgs)
+ if mArgs == nil {
+ err = fmt.Errorf("couldn't find IDL method %q with %d args", method, numArgs)
+ return
+ }
+ argptrs = mArgs.InPtrs()
// TODO(spetrovic): ask the Java object to give us the label.
label = security.AdminLabel
return
@@ -92,6 +101,16 @@
C.AttachCurrentThread(i.jVM, &env, nil)
defer C.DetachCurrentThread(i.jVM)
+ // Create a new Java server call instance.
+ mArgs := i.argGetter.FindMethod(method, len(argptrs))
+ if mArgs == nil {
+ err = fmt.Errorf("couldn't find IDL method %q with %d args", method, len(argptrs))
+ }
+ sCall := newServerCall(call, mArgs)
+ cid := jMethodID(env, jServerCallClass, "<init>", fmt.Sprintf("(%s)%s", longSign, voidSign))
+ jServerCall := C.CallNewServerCallObject(env, jServerCallClass, cid, ptrValue(sCall))
+ goRef(sCall) // unref-ed when jServerCall is garbage-collected
+
// Translate input args to JSON.
jArgs, err := i.encodeArgs(env, argptrs)
if err != nil {
@@ -101,7 +120,7 @@
const callSign = "Lcom/veyron2/ipc/ServerCall;"
const replySign = "Lcom/veyron/runtimes/google/ipc/IDLInvoker$InvokeReply;"
mid := jMethodID(env, C.GetObjectClass(env, i.jInvoker), "invoke", fmt.Sprintf("(%s%s[%s)%s", stringSign, callSign, stringSign, replySign))
- jReply := C.CallInvokeMethod(env, i.jInvoker, mid, jString(env, camelCase(method)), nil, jArgs)
+ jReply := C.CallInvokeMethod(env, i.jInvoker, mid, jString(env, camelCase(method)), jServerCall, jArgs)
if err := jExceptionMsg(env); err != nil {
return nil, fmt.Errorf("error invoking Java method %q: %v", method, err)
}
@@ -142,11 +161,13 @@
errorID := jStringField(env, jReply, "errorID")
errorMsg := jStringField(env, jReply, "errorMsg")
- // Get Go result instances.
- argptrs, err := i.argGetter.GetOutArgPtrs(method, numArgs)
- if err != nil {
- return nil, fmt.Errorf("couldn't get arguments for method %q with %d input args: %v", method, numArgs, err)
+ // Get result instances.
+ mArgs := i.argGetter.FindMethod(method, numArgs)
+ if mArgs == nil {
+ return nil, fmt.Errorf("couldn't find method %q with %d input args: %v", method, numArgs)
}
+ argptrs := mArgs.OutPtrs()
+
// Check for app error.
if hasAppErr {
return resultsWithError(argptrs, verror.Make(verror.ID(errorID), errorMsg)), nil
diff --git a/runtimes/google/ipc/jni/jni.go b/runtimes/google/ipc/jni/jni.go
index 1fb6a61..c010140 100644
--- a/runtimes/google/ipc/jni/jni.go
+++ b/runtimes/google/ipc/jni/jni.go
@@ -5,11 +5,13 @@
import (
"flag"
"fmt"
+ "io"
+ "time"
"unsafe"
+ "veyron2"
"veyron2/ipc"
"veyron2/rt"
- "veyron2/verror"
)
// #include <stdlib.h>
@@ -19,122 +21,142 @@
var (
// Global reference for com.veyron2.ipc.VeyronException class.
jVeyronExceptionClass C.jclass
+ // Global reference for com.veyron.runtimes.google.ipc.Runtime$ServerCall class.
+ jServerCallClass C.jclass
// Global reference for com.veyron.runtimes.google.ipc.IDLInvoker class.
jIDLInvokerClass C.jclass
// Global reference for java.lang.Throwable class.
jThrowableClass C.jclass
// Global reference for java.lang.String class.
jStringClass C.jclass
+ // Global reference for java.io.EOFException class.
+ jEOFExceptionClass C.jclass
)
-// refs stores references to instances of various Go types, namely instances
-// that are referenced only by the Java code. The only purpose of this store
-// is to prevent Go runtime from garbage collecting those instances.
-var refs = newSafeSet()
-
-// serverPtr returns the pointer to the provided server instance, as a Java's
-// C.jlong type.
-func serverPtr(s ipc.Server) C.jlong {
- return C.jlong(uintptr(unsafe.Pointer(&s)))
-}
-
-// getServer returns the server referenced by the provided pointer, or nil if
-// the pointer is 0.
-func getServer(env *C.JNIEnv, ptr C.jlong) ipc.Server {
- if ptr == C.jlong(0) {
- jThrow(env, "Go server pointer is nil")
- return nil
- }
- return *(*ipc.Server)(unsafe.Pointer(uintptr(ptr)))
-}
-
-// serverPtr returns the pointer to the provided client instance, as a Java's
-// C.jlong type.
-func clientPtr(c *client) C.jlong {
- return C.jlong(uintptr(unsafe.Pointer(c)))
-}
-
-// getClient returns the client referenced by the provided pointer, or nil if
-// the pointer is 0.
-func getClient(env *C.JNIEnv, ptr C.jlong) *client {
- if ptr == C.jlong(0) {
- jThrow(env, "Go client pointer is nil")
- return nil
- }
- return (*client)(unsafe.Pointer(uintptr(ptr)))
-}
-
-// serverPtr returns the pointer to the provided clientCall instance, as a
-// Java's C.jlong type.
-func clientCallPtr(c *clientCall) C.jlong {
- return C.jlong(uintptr(unsafe.Pointer(c)))
-}
-
-// getCall returns the clientCall referenced by the provided pointer,
-// or nil if the pointer is 0.
-func getCall(env *C.JNIEnv, ptr C.jlong) *clientCall {
- if ptr == C.jlong(0) {
- jThrow(env, "Go client call pointer is nil")
- return nil
- }
- return (*clientCall)(unsafe.Pointer(uintptr(ptr)))
-}
-
//export JNI_OnLoad
func JNI_OnLoad(jVM *C.JavaVM, reserved unsafe.Pointer) C.jint {
return C.JNI_VERSION_1_6
}
-//export Java_com_veyron_runtimes_google_ipc_Runtime_nativeInit
-func Java_com_veyron_runtimes_google_ipc_Runtime_nativeInit(env *C.JNIEnv, jRuntime C.jclass) {
+//export Java_com_veyron_runtimes_google_ipc_Runtime_nativeGlobalInit
+func Java_com_veyron_runtimes_google_ipc_Runtime_nativeGlobalInit(env *C.JNIEnv, jRuntime C.jclass) {
// Cache global references to all Java classes used by the package. This is
// necessary because JNI gets access to the class loader only in the system
// thread, so we aren't able to invoke FindClass in other threads.
jVeyronExceptionClass = jFindClassOrDie(env, "com/veyron2/ipc/VeyronException")
+ jServerCallClass = jFindClassOrDie(env, "com/veyron/runtimes/google/ipc/Runtime$ServerCall")
jIDLInvokerClass = jFindClassOrDie(env, "com/veyron/runtimes/google/ipc/IDLInvoker")
jThrowableClass = jFindClassOrDie(env, "java/lang/Throwable")
jStringClass = jFindClassOrDie(env, "java/lang/String")
+ jEOFExceptionClass = jFindClassOrDie(env, "java/io/EOFException")
}
-//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativeInit
-func Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativeInit(env *C.JNIEnv, jServer C.jobject) C.jlong {
- s, err := rt.R().NewServer()
- if err != nil {
- jThrow(env, fmt.Sprintf("Couldn't get new server from go runtime: %v", err))
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Runtime_nativeInit
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024Runtime_nativeInit(env *C.JNIEnv, jRuntime C.jobject, create C.jboolean) C.jlong {
+ r := rt.Init()
+ if create == C.JNI_TRUE {
+ var err error
+ r, err = rt.New()
+ if err != nil {
+ jThrowV(env, err)
+ }
+ }
+ goRef(&r)
+ return ptrValue(&r)
+}
+
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Runtime_nativeNewClient
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024Runtime_nativeNewClient(env *C.JNIEnv, jRuntime C.jobject, goRuntimePtr C.jlong) C.jlong {
+ r, ok := ptr(goRuntimePtr).(*veyron2.Runtime)
+ if !ok || r == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go runtime with pointer: %d", int(goRuntimePtr)))
return C.jlong(0)
}
+ rc, err := (*r).NewClient()
+ if err != nil {
+ jThrowV(env, err)
+ return C.jlong(0)
+ }
+ c := newClient(rc)
+ goRef(c)
+ return ptrValue(c)
+}
- // Ref.
- refs.insert(s)
- return serverPtr(s)
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Runtime_nativeNewServer
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024Runtime_nativeNewServer(env *C.JNIEnv, jRuntime C.jobject, goRuntimePtr C.jlong) C.jlong {
+ r, ok := ptr(goRuntimePtr).(*veyron2.Runtime)
+ if !ok || r == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go runtime with pointer: %d", int(goRuntimePtr)))
+ return C.jlong(0)
+ }
+ s, err := (*r).NewServer()
+ if err != nil {
+ jThrowV(env, err)
+ return C.jlong(0)
+ }
+ goRef(&s)
+ return ptrValue(&s)
+}
+
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Runtime_nativeGetClient
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024Runtime_nativeGetClient(env *C.JNIEnv, jRuntime C.jobject, goRuntimePtr C.jlong) C.jlong {
+ r, ok := ptr(goRuntimePtr).(*veyron2.Runtime)
+ if !ok || r == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go runtime with pointer: %d", int(goRuntimePtr)))
+ return C.jlong(0)
+ }
+ rc := (*r).Client()
+ c := newClient(rc)
+ goRef(c)
+ return ptrValue(c)
+}
+
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Runtime_nativeNewContext
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024Runtime_nativeNewContext(env *C.JNIEnv, jRuntime C.jobject, goRuntimePtr C.jlong) C.jlong {
+ r, ok := ptr(goRuntimePtr).(*veyron2.Runtime)
+ if !ok || r == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go runtime with pointer: %d", int(goRuntimePtr)))
+ return C.jlong(0)
+ }
+ c := (*r).NewContext()
+ goRef(&c)
+ return ptrValue(&c)
+}
+
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Runtime_nativeFinalize
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024Runtime_nativeFinalize(env *C.JNIEnv, jRuntime C.jobject, goRuntimePtr C.jlong) {
+ r, ok := ptr(goRuntimePtr).(*veyron2.Runtime)
+ if ok && r != nil {
+ goUnref(r)
+ }
}
//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativeRegister
func Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativeRegister(env *C.JNIEnv, jServer C.jobject, goServerPtr C.jlong, prefix C.jstring, dispatcher C.jobject) {
- s := getServer(env, goServerPtr)
- if s == nil {
- jThrow(env, fmt.Sprintf("Couldn't find Go server with pointer: %d", int(goServerPtr)))
+ s, ok := ptr(goServerPtr).(*ipc.Server)
+ if !ok || s == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go server with pointer: %d", int(goServerPtr)))
return
}
// Create a new Dispatcher
d, err := newJNIDispatcher(env, dispatcher)
if err != nil {
- jThrow(env, err.Error())
+ jThrowV(env, err)
return
}
- s.Register(goString(env, prefix), d)
+ (*s).Register(goString(env, prefix), d)
}
//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativeListen
func Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativeListen(env *C.JNIEnv, server C.jobject, goServerPtr C.jlong, protocol C.jstring, address C.jstring) C.jstring {
- s := getServer(env, goServerPtr)
- if s == nil {
- jThrow(env, fmt.Sprintf("Couldn't find Go server with pointer: %d", int(goServerPtr)))
+ s, ok := ptr(goServerPtr).(*ipc.Server)
+ if !ok || s == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go server with pointer: %d", int(goServerPtr)))
return nil
}
- ep, err := s.Listen(goString(env, protocol), goString(env, address))
+ ep, err := (*s).Listen(goString(env, protocol), goString(env, address))
if err != nil {
- jThrow(env, err.Error())
+ jThrowV(env, err)
return nil
}
return jString(env, ep.String())
@@ -142,114 +164,177 @@
//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativePublish
func Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativePublish(env *C.JNIEnv, server C.jobject, goServerPtr C.jlong, name C.jstring) {
- s := getServer(env, goServerPtr)
- if s == nil {
- jThrow(env, fmt.Sprintf("Couldn't find Go server with pointer: %d", int(goServerPtr)))
+ s, ok := ptr(goServerPtr).(*ipc.Server)
+ if !ok || s == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go server with pointer: %d", int(goServerPtr)))
return
}
- if err := s.Publish(goString(env, name)); err != nil {
- jThrow(env, err.Error())
+ if err := (*s).Publish(goString(env, name)); err != nil {
+ jThrowV(env, err)
return
}
}
//export Java_com_veyron_runtimes_google_ipc_jni_Runtime_00024Server_nativeStop
func Java_com_veyron_runtimes_google_ipc_jni_Runtime_00024Server_nativeStop(env *C.JNIEnv, server C.jobject, goServerPtr C.jlong) {
- s := getServer(env, goServerPtr)
- if s == nil {
- jThrow(env, fmt.Sprintf("Couldn't find Go server with pointer: %d", int(goServerPtr)))
+ s, ok := ptr(goServerPtr).(*ipc.Server)
+ if !ok || s == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go server with pointer: %d", int(goServerPtr)))
return
}
- if err := s.Stop(); err != nil {
- jThrow(env, err.Error())
+ if err := (*s).Stop(); err != nil {
+ jThrowV(env, err)
return
}
}
//export Java_com_veyron_runtimes_google_ipc_jni_Runtime_00024Server_nativeFinalize
func Java_com_veyron_runtimes_google_ipc_jni_Runtime_00024Server_nativeFinalize(env *C.JNIEnv, server C.jobject, goServerPtr C.jlong) {
- s := getServer(env, goServerPtr)
- if s != nil {
- // Unref.
- refs.delete(s)
+ s, ok := ptr(goServerPtr).(*ipc.Server)
+ if ok && s != nil {
+ goUnref(s)
}
}
-//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeInit
-func Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeInit(env *C.JNIEnv, jClient C.jobject) C.jlong {
- c, err := newClient()
- if err != nil {
- jThrow(env, fmt.Sprintf("Couldn't get new client from go runtime: %v", err))
- return C.jlong(0)
- }
- // Ref.
- refs.insert(c)
- return clientPtr(c)
-}
-
//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeStartCall
-func Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeStartCall(env *C.JNIEnv, jClient C.jobject, goClientPtr C.jlong, name C.jstring, method C.jstring, jsonArgs C.jobjectArray, jPath C.jstring, timeoutMillis C.jlong) C.jlong {
- c := getClient(env, goClientPtr)
- if c == nil {
- jThrow(env, fmt.Sprintf("Couldn't find Go client with pointer: %d", int(goClientPtr)))
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeStartCall(env *C.JNIEnv, jClient C.jobject, goClientPtr C.jlong, jContext C.jobject, name C.jstring, method C.jstring, jsonArgs C.jobjectArray, jPath C.jstring, timeoutMillis C.jlong) C.jlong {
+ c, ok := ptr(goClientPtr).(*client)
+ if !ok || c == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go client with pointer: %d", int(goClientPtr)))
return C.jlong(0)
}
- call, err := c.StartCall(env, goString(env, name), goString(env, method), jsonArgs, jPath, timeoutMillis)
+ call, err := c.StartCall(env, jContext, goString(env, name), goString(env, method), jsonArgs, jPath, timeoutMillis)
if err != nil {
- jThrow(env, fmt.Sprintf("Couldn't start Go call: %v", err))
+ jThrowV(env, err)
return C.jlong(0)
}
- return clientCallPtr(call)
+ goRef(call)
+ return ptrValue(call)
}
//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeClose
func Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeClose(env *C.JNIEnv, jClient C.jobject, goClientPtr C.jlong) {
- c := getClient(env, goClientPtr)
- if c != nil {
- c.Close()
+ c, ok := ptr(goClientPtr).(*client)
+ if !ok || c == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go client with pointer: %d", int(goClientPtr)))
+ return
}
+ c.Close()
}
//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeFinalize
func Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeFinalize(env *C.JNIEnv, jClient C.jobject, goClientPtr C.jlong) {
- c := getClient(env, goClientPtr)
- if c != nil {
- // Unref.
- refs.delete(c)
+ c, ok := ptr(goClientPtr).(*client)
+ if ok && c != nil {
+ goUnref(c)
}
}
-//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Call_nativeFinish
-func Java_com_veyron_runtimes_google_ipc_Runtime_00024Call_nativeFinish(env *C.JNIEnv, jClient C.jobject, goCallPtr C.jlong) C.jobjectArray {
- c := getCall(env, goCallPtr)
- if c == nil {
- jThrow(env, fmt.Sprintf("Couldn't find Go client with pointer: %d", int(goCallPtr)))
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Context_nativeFinalize
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024Context_nativeFinalize(env *C.JNIEnv, jClient C.jobject, goContextPtr C.jlong) {
+ c, ok := ptr(goContextPtr).(*ipc.Context)
+ if ok && c != nil {
+ goUnref(c)
+ }
+}
+
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Stream_nativeSend
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024Stream_nativeSend(env *C.JNIEnv, jStream C.jobject, goStreamPtr C.jlong, jItem C.jstring) {
+ s, ok := ptr(goStreamPtr).(*stream)
+ if !ok || s == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go stream with pointer: %d", int(goStreamPtr)))
+ return
+ }
+ s.Send(env, jItem)
+}
+
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Stream_nativeRecv
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024Stream_nativeRecv(env *C.JNIEnv, jStream C.jobject, goStreamPtr C.jlong) C.jstring {
+ s, ok := ptr(goStreamPtr).(*stream)
+ if !ok || s == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go stream with pointer: %d", int(goStreamPtr)))
+ return nil
+ }
+ ret, err := s.Recv(env)
+ if err != nil {
+ if err == io.EOF {
+ jThrow(env, jEOFExceptionClass, err.Error())
+ return nil
+ }
+ jThrowV(env, err)
+ return nil
+ }
+ return ret
+}
+
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024ClientCall_nativeFinish
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024ClientCall_nativeFinish(env *C.JNIEnv, jClientCall C.jobject, goClientCallPtr C.jlong) C.jobjectArray {
+ c, ok := ptr(goClientCallPtr).(*clientCall)
+ if !ok || c == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go client call with pointer: %d", int(goClientCallPtr)))
return nil
}
ret, err := c.Finish(env)
if err != nil {
- // Could be an application error, so we throw it with jThrowV.
- jThrowV(env, verror.Convert(err))
+ jThrowV(env, err)
return nil
}
- // Unref.
- refs.delete(c)
return ret
}
-//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Call_nativeCancel
-func Java_com_veyron_runtimes_google_ipc_Runtime_00024Call_nativeCancel(env *C.JNIEnv, jClient C.jobject, goCallPtr C.jlong) {
- c := getCall(env, goCallPtr)
- if c != nil {
- c.Cancel()
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024ClientCall_nativeCancel
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024ClientCall_nativeCancel(env *C.JNIEnv, jClientCall C.jobject, goClientCallPtr C.jlong) {
+ c, ok := ptr(goClientCallPtr).(*clientCall)
+ if !ok || c == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go client call with pointer: %d", int(goClientCallPtr)))
+ return
+ }
+ c.Cancel()
+}
+
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024ClientCall_nativeFinalize
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024ClientCall_nativeFinalize(env *C.JNIEnv, jClientCall C.jobject, goClientCallPtr C.jlong) {
+ c, ok := ptr(goClientCallPtr).(*clientCall)
+ if ok && c != nil {
+ goUnref(c)
}
}
-//export Java_com_veyron_runtimes_google_ipc_Runtime_00024Call_nativeFinalize
-func Java_com_veyron_runtimes_google_ipc_Runtime_00024Call_nativeFinalize(env *C.JNIEnv, jClient C.jobject, goCallPtr C.jlong) {
- c := getCall(env, goCallPtr)
- if c != nil {
- refs.delete(c)
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024ServerCall_nativeDeadline
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024ServerCall_nativeDeadline(env *C.JNIEnv, jServerCall C.jobject, goServerCallPtr C.jlong) C.jlong {
+ s, ok := ptr(goServerCallPtr).(*serverCall)
+ if !ok || s == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go server call with pointer: %d", int(goServerCallPtr)))
+ return C.jlong(0)
+ }
+ var d time.Time
+ if s == nil {
+ // Error, return current time as deadline.
+ d = time.Now()
+ } else {
+ d = s.Deadline()
+ }
+ return C.jlong(d.UnixNano() / 1000)
+}
+
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024ServerCall_nativeClosed
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024ServerCall_nativeClosed(env *C.JNIEnv, jServerCall C.jobject, goServerCallPtr C.jlong) C.jboolean {
+ s, ok := ptr(goServerCallPtr).(*serverCall)
+ if !ok || s == nil {
+ jThrowV(env, fmt.Errorf("Couldn't find Go server call with pointer: %d", int(goServerCallPtr)))
+ return C.JNI_FALSE
+ }
+ if s.IsClosed() {
+ return C.JNI_TRUE
+ }
+ return C.JNI_FALSE
+}
+
+//export Java_com_veyron_runtimes_google_ipc_Runtime_00024ServerCall_nativeFinalize
+func Java_com_veyron_runtimes_google_ipc_Runtime_00024ServerCall_nativeFinalize(env *C.JNIEnv, jServerCall C.jobject, goServerCallPtr C.jlong) {
+ s, ok := ptr(goServerCallPtr).(*serverCall)
+ if ok && s != nil {
+ goUnref(s)
}
}
@@ -258,5 +343,4 @@
// flag is removed, the process will likely crash as android requires that all logs are written
// into a specific directory.
flag.Set("logtostderr", "true")
- rt.Init()
}
diff --git a/runtimes/google/ipc/jni/server_call.go b/runtimes/google/ipc/jni/server_call.go
new file mode 100644
index 0000000..ffab2ff
--- /dev/null
+++ b/runtimes/google/ipc/jni/server_call.go
@@ -0,0 +1,19 @@
+// +build android
+
+package main
+
+import (
+ "veyron2/ipc"
+)
+
+func newServerCall(call ipc.ServerCall, mArgs *methodArgs) *serverCall {
+ return &serverCall{
+ stream: newStream(call, mArgs),
+ ServerCall: call,
+ }
+}
+
+type serverCall struct {
+ stream
+ ipc.ServerCall
+}
diff --git a/runtimes/google/ipc/jni/stream.go b/runtimes/google/ipc/jni/stream.go
new file mode 100644
index 0000000..dc4a2fa
--- /dev/null
+++ b/runtimes/google/ipc/jni/stream.go
@@ -0,0 +1,54 @@
+// +build android
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "veyron2/ipc"
+)
+
+// #include <stdlib.h>
+// #include "jni_wrapper.h"
+import "C"
+
+func newStream(s ipc.Stream, mArgs *methodArgs) stream {
+ return stream{
+ stream: s,
+ mArgs: mArgs,
+ }
+}
+
+type stream struct {
+ stream ipc.Stream
+ mArgs *methodArgs
+}
+
+func (s *stream) Send(env *C.JNIEnv, jItem C.jstring) error {
+ argStr := goString(env, jItem)
+ argptr := s.mArgs.StreamSendPtr()
+ if argptr == nil {
+ return fmt.Errorf("nil stream input argument, expected a non-nil type for argument %q", argStr)
+ }
+ if err := json.Unmarshal([]byte(argStr), argptr); err != nil {
+ return err
+ }
+ return s.stream.Send(derefOrDie(argptr))
+}
+
+func (s *stream) Recv(env *C.JNIEnv) (C.jstring, error) {
+ argptr := s.mArgs.StreamRecvPtr()
+ if argptr == nil {
+ return nil, fmt.Errorf("nil stream output argument")
+ }
+ if err := s.stream.Recv(argptr); err != nil {
+ return nil, err
+ }
+ // JSON encode the result.
+ result, err := json.Marshal(derefOrDie(argptr))
+ if err != nil {
+ return nil, err
+ }
+ return jString(env, string(result)), nil
+}
diff --git a/runtimes/google/ipc/jni/util.go b/runtimes/google/ipc/jni/util.go
index 0455c9f..50041ef 100644
--- a/runtimes/google/ipc/jni/util.go
+++ b/runtimes/google/ipc/jni/util.go
@@ -29,32 +29,54 @@
const (
voidSign = "V"
boolSign = "Z"
+ longSign = "J"
stringSign = "Ljava/lang/String;"
objectSign = "Ljava/lang/Object;"
)
-// safeSet is a thread-safe set.
-type safeSet struct {
- lock sync.Mutex
- items map[interface{}]bool
-}
+// refs stores references to instances of various Go types, namely instances
+// that are referenced only by the Java code. The only purpose of this store
+// is to prevent Go runtime from garbage collecting those instances.
+var refs = newSafeSet()
-func newSafeSet() *safeSet {
- return &safeSet{
- items: make(map[interface{}]bool),
+// goRef creates a new reference to the value addressed by the provided pointer.
+// The value will remain referenced until it is explicitly unreferenced using
+// goUnref().
+func goRef(valptr interface{}) {
+ if !isPointer(valptr) {
+ panic("must pass pointer value to goRef")
}
+ refs.insert(valptr)
}
-func (s *safeSet) insert(item interface{}) {
- s.lock.Lock()
- defer s.lock.Unlock()
- s.items[item] = true
+// goUnref removes a previously added reference to the value addressed by the
+// provided pointer. If the value hasn't been ref-ed (a bug?), this unref will
+// be a no-op.
+func goUnref(valptr interface{}) {
+ if !isPointer(valptr) {
+ panic("must pass pointer value to goUnref")
+ }
+ refs.delete(valptr)
}
-func (s *safeSet) delete(item interface{}) {
- s.lock.Lock()
- defer s.lock.Unlock()
- delete(s.items, item)
+// ptrValue returns the value of the pointer as a Java C.jlong type.
+func ptrValue(ptr interface{}) C.jlong {
+ v := reflect.ValueOf(ptr)
+ if v.Kind() != reflect.Ptr {
+ panic("must pass pointer value to ptrValue")
+ }
+ return C.jlong(v.Pointer())
+}
+
+// ptr returns the pointer represented by the provided (Java C.jlong) value.
+func ptr(ptrValue C.jlong) (ptr interface{}) {
+ ptr = unsafe.Pointer(uintptr(ptrValue))
+ return
+}
+
+// isPointer returns true iff the provided value is a pointer.
+func isPointer(val interface{}) bool {
+ return reflect.ValueOf(ptr).Kind() == reflect.Ptr
}
// goString returns a Go string given the Java string.
@@ -74,17 +96,18 @@
return C.NewStringUTF(env, cString)
}
-// jThrow throws a new Java VeyronException with the given message.
-func jThrow(env *C.JNIEnv, msg string) {
+// jThrow throws a new Java exception of the provided type with the given message.
+func jThrow(env *C.JNIEnv, class C.jclass, msg string) {
s := C.CString(msg)
defer C.free(unsafe.Pointer(s))
- C.ThrowNew(env, jVeyronExceptionClass, s)
+ C.ThrowNew(env, class, s)
}
-// jThrowV throws a new Java VeyronException corresponding to the given verror.
-func jThrowV(env *C.JNIEnv, err verror.E) {
+// jThrowV throws a new Java VeyronException corresponding to the given error.
+func jThrowV(env *C.JNIEnv, err error) {
+ verr := verror.Convert(err)
id := jMethodID(env, jVeyronExceptionClass, "<init>", fmt.Sprintf("(%s%s)%s", stringSign, stringSign, voidSign))
- obj := C.jthrowable(C.CallNewVeyronExceptionObject(env, jVeyronExceptionClass, id, jString(env, err.Error()), jString(env, string(err.ErrorID()))))
+ obj := C.jthrowable(C.CallNewVeyronExceptionObject(env, jVeyronExceptionClass, id, jString(env, verr.Error()), jString(env, string(verr.ErrorID()))))
C.Throw(env, obj)
}
@@ -183,3 +206,27 @@
}
return v.Elem().Interface()
}
+
+func newSafeSet() *safeSet {
+ return &safeSet{
+ items: make(map[interface{}]bool),
+ }
+}
+
+// safeSet is a thread-safe set.
+type safeSet struct {
+ lock sync.Mutex
+ items map[interface{}]bool
+}
+
+func (s *safeSet) insert(item interface{}) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ s.items[item] = true
+}
+
+func (s *safeSet) delete(item interface{}) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ delete(s.items, item)
+}