| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // +build java android |
| |
| package rpc |
| |
| import ( |
| "fmt" |
| "runtime" |
| |
| "v.io/v23/context" |
| "v.io/v23/naming" |
| "v.io/v23/rpc" |
| "v.io/v23/vdl" |
| "v.io/v23/vdlroot/signature" |
| "v.io/v23/vom" |
| jchannel "v.io/x/jni/impl/google/channel" |
| jutil "v.io/x/jni/util" |
| jcontext "v.io/x/jni/v23/context" |
| ) |
| |
| // #include "jni.h" |
| import "C" |
| |
| func goInvoker(env *C.JNIEnv, jObj C.jobject) (rpc.Invoker, error) { |
| // Create a new Java VDLInvoker object. |
| jInvokerObj, err := jutil.NewObject(env, jVDLInvokerClass, []jutil.Sign{jutil.ObjectSign}, jObj) |
| if err != nil { |
| return nil, fmt.Errorf("error creating Java VDLInvoker object: %v", err) |
| } |
| // Reference Java invoker; it will be de-referenced when the go invoker |
| // created below is garbage-collected (through the finalizer callback we |
| // setup just below). |
| jInvoker := C.jobject(jutil.NewGlobalRef(env, jInvokerObj)) |
| i := &invoker{ |
| jInvoker: jInvoker, |
| } |
| runtime.SetFinalizer(i, func(i *invoker) { |
| jEnv, freeFunc := jutil.GetEnv() |
| env := (*C.JNIEnv)(jEnv) |
| defer freeFunc() |
| jutil.DeleteGlobalRef(env, i.jInvoker) |
| }) |
| return i, nil |
| } |
| |
| type invoker struct { |
| jInvoker C.jobject |
| } |
| |
| func (i *invoker) Prepare(method string, numArgs int) (argptrs []interface{}, tags []*vdl.Value, err error) { |
| env, freeFunc := jutil.GetEnv() |
| defer freeFunc() |
| |
| // Have all input arguments be decoded into *vdl.Value. |
| argptrs = make([]interface{}, numArgs) |
| for i := 0; i < numArgs; i++ { |
| value := new(vdl.Value) |
| argptrs[i] = &value |
| } |
| // Get the method tags. |
| jTags, err := jutil.CallObjectMethod(env, i.jInvoker, "getMethodTags", []jutil.Sign{jutil.StringSign}, jutil.ArraySign(jutil.VdlValueSign), jutil.CamelCase(method)) |
| if err != nil { |
| return nil, nil, err |
| } |
| tags, err = jutil.GoVDLValueArray(env, jTags) |
| if err != nil { |
| return nil, nil, err |
| } |
| return |
| } |
| |
| func (i *invoker) Invoke(ctx *context.T, call rpc.StreamServerCall, method string, argptrs []interface{}) (results []interface{}, err error) { |
| jEnv, freeFunc := jutil.GetEnv() |
| env := (*C.JNIEnv)(jEnv) |
| defer freeFunc() |
| |
| jContext, err := jcontext.JavaContext(env, ctx, nil) |
| if err != nil { |
| return nil, err |
| } |
| |
| jStreamServerCall, err := javaStreamServerCall(env, call) |
| if err != nil { |
| return nil, err |
| } |
| |
| // VOM-encode the input arguments. |
| jVomArgs, err := encodeArgs(env, argptrs) |
| if err != nil { |
| return nil, err |
| } |
| // Invoke the method. |
| contextSign := jutil.ClassSign("io.v.v23.context.VContext") |
| callSign := jutil.ClassSign("io.v.v23.rpc.StreamServerCall") |
| replySign := jutil.ClassSign("io.v.impl.google.rpc.VDLInvoker$InvokeReply") |
| jReply, err := jutil.CallObjectMethod(env, i.jInvoker, "invoke", []jutil.Sign{contextSign, callSign, jutil.StringSign, jutil.ArraySign(jutil.ArraySign(jutil.ByteSign))}, replySign, jContext, jStreamServerCall, jutil.CamelCase(method), jVomArgs) |
| if err != nil { |
| return nil, fmt.Errorf("error invoking Java method %q: %v", method, err) |
| } |
| // Decode and return results. |
| return decodeResults(env, C.jobject(jReply)) |
| } |
| |
| type javaGlobber struct { |
| i *invoker |
| } |
| |
| func (j javaGlobber) Glob__(ctx *context.T, call rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) { |
| jEnv, freeFunc := jutil.GetEnv() |
| defer freeFunc() |
| env := (*C.JNIEnv)(jEnv) |
| |
| jServerCall, err := JavaServerCall(env, call) |
| if err != nil { |
| return nil, err |
| } |
| |
| actualChannel := make(chan naming.GlobReply) |
| readFunc := func(input interface{}) error { |
| jEnv, freeFunc := jutil.GetEnv() |
| defer freeFunc() |
| env := (*C.JNIEnv)(jEnv) |
| |
| defer jutil.DeleteGlobalRef(env, input) |
| var reply naming.GlobReply |
| err := jutil.GoVomCopy(env, input, jGlobReplyClass, &reply) |
| if err != nil { |
| return err |
| } |
| actualChannel <- reply |
| return nil |
| } |
| closeFunc := func() error { |
| close(actualChannel) |
| return nil |
| } |
| |
| jOutputChannel, err := jchannel.JavaOutputChannel(env, readFunc, closeFunc) |
| if err != nil { |
| return nil, err |
| } |
| |
| callSign := jutil.ClassSign("io.v.v23.rpc.ServerCall") |
| channelSign := jutil.ClassSign("io.v.v23.OutputChannel") |
| // Invoke the VDLInvoker's glob method. |
| go func(jServerCallRef C.jobject, jOutputChannelRef C.jobject) { |
| jEnv, freeFunc := jutil.GetEnv() |
| defer freeFunc() |
| env := (*C.JNIEnv)(jEnv) |
| jutil.CallVoidMethod(env, j.i.jInvoker, "glob", []jutil.Sign{callSign, jutil.StringSign, channelSign}, jServerCallRef, pattern, jOutputChannelRef) |
| jutil.DeleteGlobalRef(env, jServerCallRef) |
| jutil.DeleteGlobalRef(env, jOutputChannelRef) |
| }(C.jobject(jutil.NewGlobalRef(env, jServerCall)), C.jobject(jutil.NewGlobalRef(env, jOutputChannel))) |
| |
| return actualChannel, nil |
| } |
| |
| func (i *invoker) Globber() *rpc.GlobState { |
| return &rpc.GlobState{AllGlobber: javaGlobber{i}} |
| } |
| |
| func (i *invoker) Signature(ctx *context.T, call rpc.ServerCall) ([]signature.Interface, error) { |
| jEnv, freeFunc := jutil.GetEnv() |
| env := (*C.JNIEnv)(jEnv) |
| defer freeFunc() |
| |
| replySign := jutil.ClassSign("io.v.v23.vdlroot.signature.Interface") |
| |
| interfacesArr, err := jutil.CallObjectArrayMethod(env, i.jInvoker, "getSignature", nil, replySign) |
| if err != nil { |
| return nil, err |
| } |
| |
| result := make([]signature.Interface, len(interfacesArr)) |
| for i, jInterface := range interfacesArr { |
| err = jutil.GoVomCopy(jEnv, jInterface, jInterfaceClass, &result[i]) |
| if err != nil { |
| return nil, err |
| } |
| } |
| return result, nil |
| } |
| |
| func (i *invoker) MethodSignature(ctx *context.T, call rpc.ServerCall, method string) (signature.Method, error) { |
| jEnv, freeFunc := jutil.GetEnv() |
| env := (*C.JNIEnv)(jEnv) |
| defer freeFunc() |
| |
| replySign := jutil.ClassSign("io.v.v23.vdlroot.signature.Method") |
| |
| jMethod, err := jutil.CallObjectMethod(env, i.jInvoker, "getMethodSignature", []jutil.Sign{jutil.StringSign}, replySign, method) |
| if err != nil { |
| return signature.Method{}, err |
| } |
| |
| var result signature.Method |
| err = jutil.GoVomCopy(jEnv, jMethod, jMethodClass, &result) |
| if err != nil { |
| return signature.Method{}, err |
| } |
| return result, nil |
| } |
| |
| // encodeArgs VOM-encodes the provided arguments pointers and returns them as a |
| // Java array of byte arrays. |
| func encodeArgs(env *C.JNIEnv, argptrs []interface{}) (C.jobjectArray, error) { |
| vomArgs := make([][]byte, len(argptrs)) |
| for i, argptr := range argptrs { |
| arg := interface{}(jutil.DerefOrDie(argptr)) |
| var err error |
| if vomArgs[i], err = vom.Encode(arg); err != nil { |
| return nil, err |
| } |
| } |
| return C.jobjectArray(jutil.JByteArrayArray(env, vomArgs)), nil |
| } |
| |
| // decodeResults VOM-decodes replies stored in the Java reply object into |
| // an array *vdl.Value. |
| func decodeResults(env *C.JNIEnv, jReply C.jobject) ([]interface{}, error) { |
| // Unpack the replies. |
| results, err := jutil.JByteArrayArrayField(env, jReply, "results") |
| if err != nil { |
| return nil, err |
| } |
| vomAppErr, err := jutil.JByteArrayField(env, jReply, "vomAppError") |
| if err != nil { |
| return nil, err |
| } |
| // Check for app error. |
| if vomAppErr != nil { |
| var appErr error |
| if err := vom.Decode(vomAppErr, &appErr); err != nil { |
| return nil, err |
| } |
| return nil, appErr |
| } |
| // VOM-decode results into *vdl.Value instances. |
| ret := make([]interface{}, len(results)) |
| for i, result := range results { |
| var err error |
| if ret[i], err = jutil.VomDecodeToValue(result); err != nil { |
| return nil, err |
| } |
| } |
| return ret, nil |
| } |