blob: 9aa0f88243cca0515d76839d3839c21868fe69c2 [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"
"unsafe"
"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) {
// See if the Java object is an invoker.
var jInvoker C.jobject
if jutil.IsInstanceOf(env, jObj, jInvokerClass) {
jInvoker = jObj
} else {
// Create a new Java ReflectInvoker object.
jReflectInvoker, err := jutil.NewObject(env, jReflectInvokerClass, []jutil.Sign{jutil.ObjectSign}, jObj)
if err != nil {
return nil, fmt.Errorf("error creating Java ReflectInvoker object: %v", err)
}
jInvoker = C.jobject(jReflectInvoker)
}
// 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).
jInvokerRef := C.jobject(jutil.NewGlobalRef(env, jInvoker))
i := &invoker{
jInvoker: jInvokerRef,
}
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(ctx *context.T, 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
}
jVomTags, err := jutil.CallStaticObjectMethod(env, jUtilClass, "getMethodTags", []jutil.Sign{invokerSign, jutil.StringSign}, jutil.ArraySign(jutil.ByteArraySign), i.jInvoker, jutil.CamelCase(method))
if err != nil {
return nil, nil, err
}
vomTags := jutil.GoByteArrayArray(env, jVomTags)
tags = make([]*vdl.Value, len(vomTags))
for i, vomTag := range vomTags {
var err error
if tags[i], err = jutil.VomDecodeToValue(vomTag); 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
}
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
}
}
jVomArgs := jutil.JByteArrayArray(env, vomArgs)
jVomResults, err := jutil.CallStaticObjectMethod(env, jUtilClass, "invoke", []jutil.Sign{invokerSign, contextSign, streamServerCallSign, jutil.StringSign, jutil.ArraySign(jutil.ByteArraySign)}, jutil.ArraySign(jutil.ByteArraySign), i.jInvoker, jContext, jStreamServerCall, jutil.CamelCase(method), jVomArgs)
if err != nil {
return nil, err
}
vomResults := jutil.GoByteArrayArray(env, jVomResults)
results = make([]interface{}, len(vomResults))
for i, vomResult := range vomResults {
var err error
if results[i], err = jutil.VomDecodeToValue(vomResult); err != nil {
return nil, err
}
}
return
}
func (i *invoker) Signature(ctx *context.T, call rpc.ServerCall) ([]signature.Interface, error) {
jEnv, freeFunc := jutil.GetEnv()
env := (*C.JNIEnv)(jEnv)
defer freeFunc()
jContext, err := jcontext.JavaContext(env, ctx, nil)
if err != nil {
return nil, err
}
jServerCall, err := JavaServerCall(env, call)
if err != nil {
return nil, err
}
ifaceSign := jutil.ClassSign("io.v.v23.vdlroot.signature.Interface")
interfacesArr, err := jutil.CallObjectArrayMethod(env, i.jInvoker, "getSignature", []jutil.Sign{contextSign, serverCallSign}, ifaceSign, jContext, jServerCall)
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()
jContext, err := jcontext.JavaContext(env, ctx, nil)
if err != nil {
return signature.Method{}, err
}
jServerCall, err := JavaServerCall(env, call)
if err != nil {
return signature.Method{}, err
}
methodSign := jutil.ClassSign("io.v.v23.vdlroot.signature.Method")
jMethod, err := jutil.CallObjectMethod(env, i.jInvoker, "getMethodSignature", []jutil.Sign{contextSign, serverCallSign, jutil.StringSign}, methodSign, jContext, jServerCall, 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
}
func (i *invoker) Globber() *rpc.GlobState {
return &rpc.GlobState{AllGlobber: javaGlobber{i}}
}
// prepareArgs converts the provided arguments pointers into a Java Object array.
func (i *invoker) prepareArgs(env *C.JNIEnv, method string, argptrs []interface{}) (unsafe.Pointer, error) {
args := make([]interface{}, len(argptrs))
for i, argptr := range argptrs {
args[i] = interface{}(jutil.DerefOrDie(argptr))
}
// Get Java argument types.
typesarr, err := jutil.CallObjectArrayMethod(env, i.jInvoker, "getArgumentTypes", []jutil.Sign{jutil.StringSign}, jutil.TypeSign, jutil.CamelCase(method))
if err != nil {
return nil, err
}
if len(typesarr) != len(args) {
return nil, fmt.Errorf("wrong number of arguments for method %s, want %d, have %d", method, len(typesarr), len(args))
}
arr := make([]interface{}, len(args))
for i, arg := range args {
var err error
if arr[i], err = jutil.JVomCopy(env, arg, typesarr[i]); err != nil {
return nil, err
}
}
return jutil.JObjectArray(env, arr, jObjectClass), nil
}
// prepareResults converts the provided Java result array into a Go slice of *vdl.Value.
func (i *invoker) prepareResults(env *C.JNIEnv, method string, jResults []unsafe.Pointer) ([]interface{}, error) {
// Get Java result types.
typesarr, err := jutil.CallObjectArrayMethod(env, i.jInvoker, "getResultTypes", []jutil.Sign{jutil.StringSign}, jutil.TypeSign, jutil.CamelCase(method))
if err != nil {
return nil, err
}
if len(typesarr) != len(jResults) {
return nil, fmt.Errorf("wrong number of results for method %s, want %d, have %d", method, len(typesarr), len(jResults))
}
// VOM-encode Java results and decode into []*vdl.Value.
ret := make([]interface{}, len(jResults))
for i, jResult := range jResults {
data, err := jutil.JVomEncode(env, jResult, typesarr[i])
if err != nil {
return nil, err
}
if ret[i], err = jutil.VomDecodeToValue(data); err != nil {
return nil, err
}
}
return ret, nil
}
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()
env := (*C.JNIEnv)(jEnv)
defer jutil.DeleteGlobalRef(env, input)
var reply naming.GlobReply
err := jutil.GoVomCopy(env, input, jGlobReplyClass, &reply)
if err != nil {
freeFunc()
return err
}
freeFunc()
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")
// Calls Java invoker'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
}