Plumbing additional RPC options

MultiPart: 2/3
Change-Id: I7becc9b76f8b0b0c3ccc9990468794ec4dd6491c
diff --git a/impl/google/namespace/jni.go b/impl/google/namespace/jni.go
index d315f27..35b542e 100644
--- a/impl/google/namespace/jni.go
+++ b/impl/google/namespace/jni.go
@@ -30,7 +30,7 @@
 	// Global reference for io.v.v23.naming.GlobReply class.
 	jGlobReplyClass jutil.Class
 	// Global reference for io.v.v23.naming.MountEntry class.
-	jMountEntryClass jutil.Class
+	JMountEntryClass jutil.Class
 	// Global reference for io.v.v23.security.access.Permissions
 	jPermissionsClass jutil.Class
 )
@@ -47,7 +47,7 @@
 	if err != nil {
 		return err
 	}
-	jMountEntryClass, err = jutil.JFindClass(env, "io/v/v23/naming/MountEntry")
+	JMountEntryClass, err = jutil.JFindClass(env, "io/v/v23/naming/MountEntry")
 	if err != nil {
 		return err
 	}
@@ -219,7 +219,7 @@
 	}
 	env, freeFunc := jutil.GetEnv()
 	defer freeFunc()
-	jEntry, err := jutil.JVomCopy(env, entry, jMountEntryClass)
+	jEntry, err := jutil.JVomCopy(env, entry, JMountEntryClass)
 	if err != nil {
 		return jutil.NullObject, err
 	}
@@ -263,7 +263,7 @@
 	}
 	env, freeFunc := jutil.GetEnv()
 	defer freeFunc()
-	jEntry, err := jutil.JVomCopy(env, entry, jMountEntryClass)
+	jEntry, err := jutil.JVomCopy(env, entry, JMountEntryClass)
 	if err != nil {
 		return jutil.NullObject, err
 	}
diff --git a/impl/google/rpc/jni.go b/impl/google/rpc/jni.go
index 549b4de..05b43a6 100644
--- a/impl/google/rpc/jni.go
+++ b/impl/google/rpc/jni.go
@@ -11,7 +11,6 @@
 	"unsafe"
 
 	"v.io/v23/context"
-	"v.io/v23/options"
 	"v.io/v23/rpc"
 	"v.io/v23/vdl"
 	"v.io/v23/verror"
@@ -23,6 +22,7 @@
 	jutil "v.io/x/jni/util"
 	jcontext "v.io/x/jni/v23/context"
 	jnaming "v.io/x/jni/v23/naming"
+	jopts "v.io/x/jni/v23/options"
 	jsecurity "v.io/x/jni/v23/security"
 )
 
@@ -295,11 +295,11 @@
 
 //export Java_io_v_impl_google_rpc_ClientImpl_nativeStartCall
 func Java_io_v_impl_google_rpc_ClientImpl_nativeStartCall(jenv *C.JNIEnv, jClientObj C.jobject, goRef C.jlong,
-	jContext C.jobject, jName C.jstring, jMethod C.jstring, jVomArgs C.jobjectArray, jNameResolutionAuthorizerObj,
-	jServerAuthorizerObj C.jobject, jCallbackObj C.jobject) {
+	jContext C.jobject, jName C.jstring, jMethod C.jstring, jVomArgs C.jobjectArray, jOptionsObj C.jobject, jCallbackObj C.jobject) {
 	env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
 	name := jutil.GoString(env, jutil.Object(uintptr(unsafe.Pointer(jName))))
 	method := jutil.GoString(env, jutil.Object(uintptr(unsafe.Pointer(jMethod))))
+	jOptions := jutil.Object(uintptr(unsafe.Pointer(jOptionsObj)))
 	jCallback := jutil.Object(uintptr(unsafe.Pointer(jCallbackObj)))
 	ctx, cancel, err := jcontext.GoContext(env, jutil.Object(uintptr(unsafe.Pointer(jContext))))
 	if err != nil {
@@ -311,25 +311,13 @@
 		jutil.CallbackOnFailure(env, jCallback, err)
 		return
 	}
-	var opts []rpc.CallOpt
-	jNameResolutionAuthorizer := jutil.Object(uintptr(unsafe.Pointer(jNameResolutionAuthorizerObj)))
-	if !jNameResolutionAuthorizer.IsNull() {
-		auth, err := jsecurity.GoAuthorizer(env, jNameResolutionAuthorizer)
-		if err != nil {
-			jutil.CallbackOnFailure(env, jCallback, err)
-			return
-		}
-		opts = append(opts, options.NameResolutionAuthorizer{auth})
+
+	opts, err := jopts.GoRpcOpts(env, jOptions)
+	if err != nil {
+		jutil.CallbackOnFailure(env, jCallback, err)
+		return
 	}
-	jServerAuthorizer := jutil.Object(uintptr(unsafe.Pointer(jServerAuthorizerObj)))
-	if !jServerAuthorizer.IsNull() {
-		auth, err := jsecurity.GoAuthorizer(env, jServerAuthorizer)
-		if err != nil {
-			jutil.CallbackOnFailure(env, jCallback, err)
-			return
-		}
-		opts = append(opts, options.ServerAuthorizer{auth})
-	}
+
 	// Create a global reference to the client object for the duration of the call
 	// so that the java object doesn't get garbage collected while the async call
 	// is happening. This is an issue because if the java object is garbage collected,
diff --git a/impl/google/rt/jni.go b/impl/google/rt/jni.go
index d6732f4..0429ed0 100644
--- a/impl/google/rt/jni.go
+++ b/impl/google/rt/jni.go
@@ -11,13 +11,13 @@
 
 	"v.io/v23"
 	"v.io/v23/context"
-	"v.io/v23/options"
 
 	jdiscovery "v.io/x/jni/impl/google/discovery"
 	jns "v.io/x/jni/impl/google/namespace"
 	jrpc "v.io/x/jni/impl/google/rpc"
 	jutil "v.io/x/jni/util"
 	jcontext "v.io/x/jni/v23/context"
+	jopts "v.io/x/jni/v23/options"
 	jsecurity "v.io/x/jni/v23/security"
 )
 
@@ -119,7 +119,7 @@
 }
 
 //export Java_io_v_impl_google_rt_VRuntimeImpl_nativeWithNewServer
-func Java_io_v_impl_google_rt_VRuntimeImpl_nativeWithNewServer(jenv *C.JNIEnv, jRuntime C.jclass, jContext C.jobject, jName C.jstring, jDispatcher C.jobject, jLameDuck C.jobject) C.jobject {
+func Java_io_v_impl_google_rt_VRuntimeImpl_nativeWithNewServer(jenv *C.JNIEnv, jRuntime C.jclass, jContext C.jobject, jName C.jstring, jDispatcher C.jobject, jOptions C.jobject) C.jobject {
 	env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
 	ctx, cancel, err := jcontext.GoContext(env, jutil.Object(uintptr(unsafe.Pointer(jContext))))
 	if err != nil {
@@ -132,12 +132,12 @@
 		jutil.JThrowV(env, err)
 		return nil
 	}
-	timeout, err := jutil.GoDuration(env, jutil.Object(uintptr(unsafe.Pointer(jLameDuck))))
+	opts, err := jopts.GoRpcServerOpts(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
 	if err != nil {
 		jutil.JThrowV(env, err)
 		return nil
 	}
-	newCtx, server, err := v23.WithNewDispatchingServer(ctx, name, d, options.LameDuckTimeout(timeout))
+	newCtx, server, err := v23.WithNewDispatchingServer(ctx, name, d, opts...)
 	if err != nil {
 		jutil.JThrowV(env, err)
 		return nil
diff --git a/util/util.go b/util/util.go
index 264c7eb..649625e 100644
--- a/util/util.go
+++ b/util/util.go
@@ -732,6 +732,9 @@
 
 // jFieldID returns the Java field ID for the given object (i.e., non-static)
 // field, or an error if the field couldn't be found.
+// TODO(rosswang): Make these exported/cacheable (likely warrants refactor of corresponding
+// GetXField methods)
+// https://rkennke.wordpress.com/2007/07/24/efficient-jni-programming-ii-field-and-method-access/
 func jFieldID(env Env, class Class, name string, sign Sign) (C.jfieldID, error) {
 	cName := C.CString(name)
 	defer C.free(unsafe.Pointer(cName))
diff --git a/v23/options/rpc_options.go b/v23/options/rpc_options.go
new file mode 100644
index 0000000..aa7b9b9
--- /dev/null
+++ b/v23/options/rpc_options.go
@@ -0,0 +1,148 @@
+// Copyright 2016 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 options
+
+import (
+	"time"
+
+	"v.io/v23/naming"
+	"v.io/v23/options"
+	"v.io/v23/rpc"
+	"v.io/v23/security"
+
+	jnamespace "v.io/x/jni/impl/google/namespace"
+	jutil "v.io/x/jni/util"
+	jsecurity "v.io/x/jni/v23/security"
+)
+
+// #include "jni.h"
+import "C"
+
+var (
+	authorizerSign = jutil.ClassSign("io.v.v23.security.Authorizer")
+	mountEntrySign = jutil.ClassSign("io.v.v23.naming.MountEntry")
+)
+
+func getAuthorizer(env jutil.Env, obj jutil.Object, field string) (security.Authorizer, error) {
+	jAuthorizer, err := jutil.JObjectField(env, obj, field, authorizerSign)
+	if err != nil {
+		return nil, err
+	}
+
+	if !jAuthorizer.IsNull() {
+		auth, err := jsecurity.GoAuthorizer(env, jAuthorizer)
+		if err != nil {
+			return nil, err
+		}
+		return auth, nil
+	}
+	return nil, nil
+}
+
+func getPreresolved(env jutil.Env, obj jutil.Object) (*naming.MountEntry, error) {
+	jMountEntry, err := jutil.JObjectField(env, obj, "preresolved", mountEntrySign)
+	if err != nil {
+		return nil, err
+	}
+
+	if !jMountEntry.IsNull() {
+		var mountEntry naming.MountEntry
+		if err := jutil.GoVomCopy(env, obj, jnamespace.JMountEntryClass, &mountEntry); err != nil {
+			return nil, err
+		}
+		return &mountEntry, nil
+	}
+	return nil, nil
+}
+
+func getDuration(env jutil.Env, obj jutil.Object, field string) (*time.Duration, error) {
+	jDuration, err := jutil.JObjectField(env, obj, field, jutil.DurationSign)
+	if err != nil {
+		return nil, err
+	}
+
+	if !jDuration.IsNull() {
+		duration, err := jutil.GoDuration(env, jDuration)
+		if err != nil {
+			return nil, err
+		}
+		return &duration, nil
+	}
+	return nil, nil
+}
+
+func GoRpcOpts(env jutil.Env, obj jutil.Object) ([]rpc.CallOpt, error) {
+	var opts []rpc.CallOpt
+
+	if opt, err := getAuthorizer(env, obj, "nameResolutionAuthorizer"); err != nil {
+		return nil, err
+	} else if opt != nil {
+		opts = append(opts, options.NameResolutionAuthorizer{opt})
+	}
+
+	if opt, err := getAuthorizer(env, obj, "serverAuthorizer"); err != nil {
+		return nil, err
+	} else if opt != nil {
+		opts = append(opts, options.ServerAuthorizer{opt})
+	}
+
+	if opt, err := getPreresolved(env, obj); err != nil {
+		return nil, err
+	} else if opt != nil {
+		opts = append(opts, options.Preresolved{opt})
+	}
+
+	if opt, err := jutil.JBoolField(env, obj, "noRetry"); err != nil {
+		return nil, err
+	} else if opt {
+		opts = append(opts, options.NoRetry{})
+	}
+
+	if opt, err := getDuration(env, obj, "connectionTimeout"); err != nil {
+		return nil, err
+	} else if opt != nil {
+		opts = append(opts, options.ConnectionTimeout(*opt))
+	}
+
+	if opt, err := getDuration(env, obj, "channelTimeout"); err != nil {
+		return nil, err
+	} else if opt != nil {
+		opts = append(opts, options.ChannelTimeout(*opt))
+	}
+
+	return opts, nil
+}
+
+func GoRpcServerOpts(env jutil.Env, obj jutil.Object) ([]rpc.ServerOpt, error) {
+	var opts []rpc.ServerOpt
+
+	if opt, err := jutil.JBoolField(env, obj, "servesMountTable"); err != nil {
+		return nil, err
+	} else {
+		opts = append(opts, options.ServesMountTable(opt))
+	}
+
+	if opt, err := getDuration(env, obj, "lameDuckTimeout"); err != nil {
+		return nil, err
+	} else if opt != nil {
+		opts = append(opts, options.LameDuckTimeout(*opt))
+	}
+
+	if opt, err := jutil.JBoolField(env, obj, "isLeaf"); err != nil {
+		return nil, err
+	} else {
+		opts = append(opts, options.IsLeaf(opt))
+	}
+
+	if opt, err := getDuration(env, obj, "channelTimeout"); err != nil {
+		return nil, err
+	} else if opt != nil {
+		opts = append(opts, options.ChannelTimeout(*opt))
+	}
+
+	return opts, nil
+}