v.io/x/jni: Support for the ref fix

MultiPart: 2/2

Change-Id: I5cc7838fd4912fc7f79420ca8a7b4b26f23e091d
diff --git a/impl/google/channel/jni.go b/impl/google/channel/jni.go
index b9b3bee..4daff97 100644
--- a/impl/google/channel/jni.go
+++ b/impl/google/channel/jni.go
@@ -10,6 +10,7 @@
 	"fmt"
 	"unsafe"
 
+	"v.io/v23/verror"
 	jutil "v.io/x/jni/util"
 )
 
@@ -46,30 +47,31 @@
 }
 
 //export Java_io_v_impl_google_channel_ChannelIterable_nativeReadValue
-func Java_io_v_impl_google_channel_ChannelIterable_nativeReadValue(jenv *C.JNIEnv, jChannelIterable C.jobject, goValChanPtr C.jlong, goErrChanPtr C.jlong) C.jobject {
+func Java_io_v_impl_google_channel_ChannelIterable_nativeReadValue(jenv *C.JNIEnv, jChannelIterable C.jobject, goChanPtr C.jlong, goConvertPtr C.jlong) C.jobject {
 	env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
-	valCh := *(*chan jutil.Object)(jutil.NativePtr(goValChanPtr))
-	errCh := *(*chan error)(jutil.NativePtr(goErrChanPtr))
-	if jObj, ok := <-valCh; ok {
-		jObjLocal := jutil.NewLocalRef(env, jObj)
-		jutil.DeleteGlobalRef(env, jObj)
-		return C.jobject(unsafe.Pointer(jObjLocal))
+	c := *(*chan interface{})(jutil.NativePtr(goChanPtr))
+	convert := *(*func(jutil.Env, interface{})(jutil.Object, error))(jutil.NativePtr(goConvertPtr))
+	val, ok := <-c
+	if !ok {  // channel closed
+		jutil.JThrow(env, jEOFExceptionClass, "Reached end of input channel.")
+		return nil
 	}
-	// No more results, figure out if gracefully or due to an error.
-	err, ok := <-errCh
-	if !ok {  // gracefully
-		jutil.JThrow(env, jEOFExceptionClass, "Channel closed.")
-	} else {  // error
-		jutil.JThrowV(env, err)
+	jVal, err := convert(env, val)
+	if err == nil {
+		return C.jobject(unsafe.Pointer(jVal))
 	}
+	if verr, ok := err.(verror.E); ok && verr.ID == verror.ErrCanceled.ID {  // EOF
+		jutil.JThrow(env, jEOFExceptionClass, "User canceled the operation.")
+		return nil
+	}
+	jutil.JThrowV(env, err)
 	return nil
 }
 
 //export Java_io_v_impl_google_channel_ChannelIterable_nativeFinalize
-func Java_io_v_impl_google_channel_ChannelIterable_nativeFinalize(jenv *C.JNIEnv, jChannelIterable C.jobject, goValChanPtr C.jlong, goErrChanPtr C.jlong, goSourceChanPtr C.jlong) {
-	jutil.GoUnref(jutil.NativePtr(goValChanPtr))
-	jutil.GoUnref(jutil.NativePtr(goErrChanPtr))
-	jutil.GoUnref(jutil.NativePtr(goSourceChanPtr))
+func Java_io_v_impl_google_channel_ChannelIterable_nativeFinalize(jenv *C.JNIEnv, jChannelIterable C.jobject, goChanPtr C.jlong, goConvertPtr C.jlong) {
+	jutil.GoUnref(jutil.NativePtr(goChanPtr))
+	jutil.GoUnref(jutil.NativePtr(goConvertPtr))
 }
 
 //export Java_io_v_impl_google_channel_OutputChannelImpl_nativeWriteValue
diff --git a/impl/google/channel/util.go b/impl/google/channel/util.go
index 4a4d007..331d542 100644
--- a/impl/google/channel/util.go
+++ b/impl/google/channel/util.go
@@ -13,16 +13,15 @@
 // #include "jni.h"
 import "C"
 
-// JavaIterable converts the provided Go channel of jutil.Object values into a Java
-// VIterable object.
-func JavaIterable(env jutil.Env, valChPtr, errChPtr, sourceChPtr interface{}) (jutil.Object, error) {
-	jIterable, err := jutil.NewObject(env, jChannelIterableClass, []jutil.Sign{jutil.LongSign, jutil.LongSign, jutil.LongSign}, int64(jutil.PtrValue(valChPtr)), int64(jutil.PtrValue(errChPtr)), int64(jutil.PtrValue(sourceChPtr)))
+// JavaIterable converts the provided Go channel into a Java VIterable object, using the
+// given convert function.
+func JavaIterable(env jutil.Env, ch chan interface{}, convert func(jutil.Env, interface{}) (jutil.Object, error)) (jutil.Object, error) {
+	jIterable, err := jutil.NewObject(env, jChannelIterableClass, []jutil.Sign{jutil.LongSign, jutil.LongSign}, int64(jutil.PtrValue(&ch)), int64(jutil.PtrValue(&convert)))
 	if err != nil {
 		return jutil.NullObject, err
 	}
-	jutil.GoRef(valChPtr)     // Un-refed when ChannelIterable is finalized.
-	jutil.GoRef(errChPtr)     // Un-refed when ChannelIterable is finalized.
-	jutil.GoRef(sourceChPtr)  // Un-refed when ChannelIterable is finalized.
+	jutil.GoRef(&ch)      // Un-refed when ChannelIterable is finalized.
+	jutil.GoRef(&convert) // Un-refed when ChannelIterable is finalized.
 	return jIterable, nil
 }
 
diff --git a/impl/google/namespace/jni.go b/impl/google/namespace/jni.go
index 2eeaf66..dad54ce 100644
--- a/impl/google/namespace/jni.go
+++ b/impl/google/namespace/jni.go
@@ -72,44 +72,30 @@
 }
 
 func doGlob(env jutil.Env, n namespace.T, context *context.T, pattern string, opts []naming.NamespaceOpt) (jutil.Object, error) {
-	entryChan, err := n.Glob(context, pattern, opts...)
+	c, err := n.Glob(context, pattern, opts...)
 	if err != nil {
 		return jutil.NullObject, err
 	}
-
-	valChan := make(chan jutil.Object, 5)
-	errChan := make(chan error, 1)
+	valChan := make(chan interface{}, 100)
 	go func() {
-		env, freeFunc := jutil.GetEnv()
-		defer freeFunc()
-
-		for globReply := range entryChan {
-			// Check for a canceled context error, we surface these as EOF.
-			if errorEntry, ok := globReply.(*naming.GlobReplyError); ok {
-				if verr, ok := errorEntry.Value.Error.(verror.E); ok && verr.ID == verror.ErrCanceled.ID {
-					break
-				}
-			}
-			jGlobReply, err := jutil.JVomCopy(env, globReply, jGlobReplyClass)
-			if err != nil {
-				errChan <- fmt.Errorf("Couldn't convert Go glob result %v to Java: %v\n", globReply, err)
-				break
-			}
-			// The other side of the channel is responsible for deleting this
-			// global reference.
-			valChan <- jutil.NewGlobalRef(env, jGlobReply)
-			// Free up the local reference as it'll be auto-freed only when
-			// freeFunc() gets executed, which can burn us for big globs.
-			jutil.DeleteLocalRef(env, jGlobReply)
+		for val := range c {
+			valChan <- val
 		}
 		close(valChan)
-		close(errChan)
 	}()
-	jIterable, err := jchannel.JavaIterable(env, &valChan, &errChan, &entryChan)
-	if err != nil {
-		return jutil.NullObject, err
-	}
-	return jIterable, nil
+	return jchannel.JavaIterable(env, valChan, func(env jutil.Env, val interface{}) (jutil.Object, error) {
+		globReply, ok := val.(naming.GlobReply)
+		if !ok {
+			return jutil.NullObject, fmt.Errorf("Expected value of GlobReply type, got type %T: %v", val, val)
+		}
+		// Check for a canceled context error, we surface these as EOF.
+		if errorEntry, ok := globReply.(*naming.GlobReplyError); ok {
+			if verr, ok := errorEntry.Value.Error.(verror.E); ok && verr.ID == verror.ErrCanceled.ID {
+				return jutil.NullObject, verr
+			}
+		}
+		return jutil.JVomCopy(env, val, jGlobReplyClass)
+	})
 }
 
 //export Java_io_v_impl_google_namespace_NamespaceImpl_nativeGlob