blob: 8b4fd10f890606ff1b13e812d3818e44e2743341 [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"
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(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
}
// Convert Go arguments to Java.
jArgs, err := i.prepareArgs(env, method, argptrs)
if err != nil {
return nil, err
}
// Invoke the method.
resultarr, err := jutil.CallObjectArrayMethod(env, i.jInvoker, "invoke", []jutil.Sign{contextSign, streamServerCallSign, jutil.StringSign, jutil.ArraySign(jutil.ObjectSign)}, jutil.ObjectSign, jContext, jStreamServerCall, jutil.CamelCase(method), jArgs)
if err != nil {
return nil, err
}
// Convert Java results into Go.
return i.prepareResults(env, method, resultarr)
}
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()
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")
// 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
}