blob: c9d4325db614b6ed323476ca0170991336d6d367 [file] [log] [blame]
// 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
}