java: Implement 23 more JNI syncbase internal functions

The functions added in this CL are:

  Database.Destroy
  Database.Exists
  Database.BeginBatch
  Database.Commit
  Database.Abort
  Database.ListCollections
  Database.GetResumeMarker
  Database.ListSyncgroups
  Database.CreateSyncgroup
  Collection.Create
  Collection.Destroy
  Collection.Exists
  Collection.DeleteRange
  Row.Put
  Row.Get
  Row.Exists
  Row.Delete
  Blessings.DebugString
  Blessings.AppBlessingFromContext
  Blessings.UserBlessingFromContext
  Util.Encode
  Util.EncodeId
  Util.NamingJoin

This CL adds idempotent "x.free" methods to types.go and changes the
"x.toFoo" functions to leave the "x" in the same state as "x.free".

This CL also fixes the cExists argument for v23_syncbase_RowExists
to be *C.v23_syncbase_Bool instead of *bool.

Change-Id: I2e9fb2d0e5af2550fe041c14f26a7570cd809757
diff --git a/services/syncbase/bridge/cgo/impl.go b/services/syncbase/bridge/cgo/impl.go
index 1c936cf..99d55fd 100644
--- a/services/syncbase/bridge/cgo/impl.go
+++ b/services/syncbase/bridge/cgo/impl.go
@@ -716,7 +716,7 @@
 // Row
 
 //export v23_syncbase_RowExists
-func v23_syncbase_RowExists(cName, cBatchHandle C.v23_syncbase_String, cExists *bool, cErr *C.v23_syncbase_VError) {
+func v23_syncbase_RowExists(cName, cBatchHandle C.v23_syncbase_String, cExists *C.v23_syncbase_Bool, cErr *C.v23_syncbase_VError) {
 	name := cName.toString()
 	batchHandle := wire.BatchHandle(cBatchHandle.toString())
 	ctx, call := b.NewCtxCall(name, bridge.MethodDesc(wire.RowDesc, "Exists"))
@@ -730,7 +730,7 @@
 		cErr.init(err)
 		return
 	}
-	*cExists = exists
+	cExists.init(exists)
 }
 
 //export v23_syncbase_RowGet
diff --git a/services/syncbase/bridge/cgo/jni.go b/services/syncbase/bridge/cgo/jni.go
index c55c593..f6e800c 100644
--- a/services/syncbase/bridge/cgo/jni.go
+++ b/services/syncbase/bridge/cgo/jni.go
@@ -8,21 +8,22 @@
 package main
 
 import (
-	"fmt"
-	"os"
 	"unsafe"
 )
 
 // #include <stdlib.h>
+// #include <string.h>
 // #include "jni_wrapper.h"
 // #include "lib.h"
 import "C"
 
 var (
-	jVM            *C.JavaVM
-	arrayListClass jArrayListClass
-	idClass        jIdClass
-	verrorClass    jVErrorClass
+	jVM                      *C.JavaVM
+	arrayListClass           jArrayListClass
+	idClass                  jIdClass
+	syncgroupMemberInfoClass jSyncgroupMemberInfo
+	syncgroupSpecClass       jSyncgroupSpec
+	verrorClass              jVErrorClass
 )
 
 // JNI_OnLoad is called when System.loadLibrary is called. We need to cache the
@@ -39,24 +40,12 @@
 	}
 	jVM = vm
 
-	v23_syncbase_Init()
-
-	// We don't bother throwing errors in here because attempting to create
-	// an exception can also fail.
-	if arrayListClass.Init(env) != nil {
-		fmt.Fprintln(os.Stderr, "Error caching the ArrayList class")
-		return C.JNI_ERR
-	}
-
-	if idClass.Init(env) != nil {
-		fmt.Fprintln(os.Stderr, "Error caching the ID class")
-		return C.JNI_ERR
-	}
-
-	if verrorClass.Init(env) != nil {
-		fmt.Fprintln(os.Stderr, "Error caching the VError class")
-		return C.JNI_ERR
-	}
+	v23_syncbase_Init(C.v23_syncbase_Bool(1))
+	arrayListClass = newJArrayListClass(env)
+	idClass = newJIdClass(env)
+	syncgroupMemberInfoClass = newJSyncgroupMemberInfo(env)
+	syncgroupSpecClass = newJSyncgroupSpec(env)
+	verrorClass = newJVErrorClass(env)
 
 	return C.JNI_VERSION_1_6
 }
@@ -82,57 +71,125 @@
 func Java_io_v_syncbase_internal_Database_SetPermissions(env *C.JNIEnv, cls C.jclass, name C.jstring, perms C.jobject) {
 }
 
-func throwException(env *C.JNIEnv, cErr *C.v23_syncbase_VError) {
-	obj := C.NewObjectA(env, verrorClass.class, verrorClass.init, nil)
-	if s, err := V23SStringToJString(env, cErr.id); err == nil {
-		C.SetObjectField(env, obj, verrorClass.id, s)
+// maybeThrowException takes ownership of cErr and throws a Java exception if
+// cErr represents a non-nil error. Returns a boolean indicating whether an
+// exception was thrown.
+func maybeThrowException(env *C.JNIEnv, cErr *C.v23_syncbase_VError) bool {
+	if obj := cErr.extractToJava(env); obj != nil {
+		C.Throw(env, obj)
+		return true
 	}
-	C.SetLongField(env, obj, verrorClass.actionCode, C.jlong(cErr.actionCode))
-	if s, err := V23SStringToJString(env, cErr.msg); err == nil {
-		C.SetObjectField(env, obj, verrorClass.message, s)
-	}
-	if s, err := V23SStringToJString(env, cErr.stack); err == nil {
-		C.SetObjectField(env, obj, verrorClass.stack, s)
-	}
-	C.Throw(env, obj)
+	return false
 }
 
 //export Java_io_v_syncbase_internal_Database_Create
 func Java_io_v_syncbase_internal_Database_Create(env *C.JNIEnv, cls C.jclass, name C.jstring, perms C.jobject) {
-	cName, err := JStringToV23SString(env, name)
-	if err != nil {
-		return
-	}
+	cName := newVStringFromJava(env, name)
 	var cErr C.v23_syncbase_VError
 	// TODO(razvanm): construct a proper C.v23_syncbase_Permissions based on
 	// the perms object.
 	v23_syncbase_DbCreate(cName, C.v23_syncbase_Permissions{}, &cErr)
-	if cErr.id.p != nil {
-		throwException(env, &cErr)
-	}
+	maybeThrowException(env, &cErr)
 }
-func Java_io_v_syncbase_internal_Database_Destroy(env *C.JNIEnv, cls C.jclass, name C.jstring) {}
+
+//export Java_io_v_syncbase_internal_Database_Destroy
+func Java_io_v_syncbase_internal_Database_Destroy(env *C.JNIEnv, cls C.jclass, name C.jstring) {
+	cName := newVStringFromJava(env, name)
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_DbDestroy(cName, &cErr)
+	maybeThrowException(env, &cErr)
+}
+
+//export Java_io_v_syncbase_internal_Database_Exists
 func Java_io_v_syncbase_internal_Database_Exists(env *C.JNIEnv, cls C.jclass, name C.jstring) C.jboolean {
-	return 0
+	cName := newVStringFromJava(env, name)
+	var r C.v23_syncbase_Bool
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_DbExists(cName, &r, &cErr)
+	maybeThrowException(env, &cErr)
+	return C.jboolean(r)
 }
+
+//export Java_io_v_syncbase_internal_Database_BeginBatch
 func Java_io_v_syncbase_internal_Database_BeginBatch(env *C.JNIEnv, cls C.jclass, name C.jstring, opts C.jobject) C.jstring {
-	return nil
+	cName := newVStringFromJava(env, name)
+	var cHandle C.v23_syncbase_String
+	var cErr C.v23_syncbase_VError
+	// TODO(razvanm): construct a C.v23_syncbase_BatchOptions from opts.
+	v23_syncbase_DbBeginBatch(cName, C.v23_syncbase_BatchOptions{}, &cHandle, &cErr)
+	if maybeThrowException(env, &cErr) {
+		return nil
+	}
+	return cHandle.extractToJava(env)
 }
+
+//export Java_io_v_syncbase_internal_Database_ListCollections
 func Java_io_v_syncbase_internal_Database_ListCollections(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring) C.jobject {
-	return nil
+	cName := newVStringFromJava(env, name)
+	cHandle := newVStringFromJava(env, handle)
+	var cIds C.v23_syncbase_Ids
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_DbListCollections(cName, cHandle, &cIds, &cErr)
+	if maybeThrowException(env, &cErr) {
+		return nil
+	}
+	return cIds.extractToJava(env)
 }
+
+//export Java_io_v_syncbase_internal_Database_Commit
 func Java_io_v_syncbase_internal_Database_Commit(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring) {
+	cName := newVStringFromJava(env, name)
+	cHandle := newVStringFromJava(env, handle)
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_DbCommit(cName, cHandle, &cErr)
+	maybeThrowException(env, &cErr)
 }
+
+//export Java_io_v_syncbase_internal_Database_Abort
 func Java_io_v_syncbase_internal_Database_Abort(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring) {
+	cName := newVStringFromJava(env, name)
+	cHandle := newVStringFromJava(env, handle)
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_DbAbort(cName, cHandle, &cErr)
+	maybeThrowException(env, &cErr)
 }
+
+//export Java_io_v_syncbase_internal_Database_GetResumeMarker
 func Java_io_v_syncbase_internal_Database_GetResumeMarker(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring) C.jbyteArray {
-	return nil
+	cName := newVStringFromJava(env, name)
+	cHandle := newVStringFromJava(env, handle)
+	var cMarker C.v23_syncbase_Bytes
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_DbGetResumeMarker(cName, cHandle, &cMarker, &cErr)
+	if maybeThrowException(env, &cErr) {
+		return nil
+	}
+	return cMarker.extractToJava(env)
 }
+
+//export Java_io_v_syncbase_internal_Database_ListSyncgroups
 func Java_io_v_syncbase_internal_Database_ListSyncgroups(env *C.JNIEnv, cls C.jclass, name C.jstring) C.jobject {
-	return nil
+	cName := newVStringFromJava(env, name)
+	var cIds C.v23_syncbase_Ids
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_DbListSyncgroups(cName, &cIds, &cErr)
+	if maybeThrowException(env, &cErr) {
+		return nil
+	}
+	return cIds.extractToJava(env)
 }
+
+//export Java_io_v_syncbase_internal_Database_CreateSyncgroup
 func Java_io_v_syncbase_internal_Database_CreateSyncgroup(env *C.JNIEnv, cls C.jclass, name C.jstring, sgId C.jobject, spec C.jobject, info C.jobject) {
+	cName := newVStringFromJava(env, name)
+	cSgId := newVIdFromJava(env, sgId)
+	cSpec := newVSyngroupSpecFromJava(env, spec)
+	cMyInfo := newVSyncgroupMemberInfoFromJava(env, info)
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_DbCreateSyncgroup(cName, cSgId, cSpec, cMyInfo, &cErr)
+	maybeThrowException(env, &cErr)
 }
+
 func Java_io_v_syncbase_internal_Database_JoinSyncgroup(env *C.JNIEnv, cls C.jclass, name C.jstring, sgId C.jobject, info C.jobject) C.jobject {
 	return nil
 }
@@ -156,25 +213,243 @@
 }
 func Java_io_v_syncbase_internal_Collection_SetPermissions(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring, perms C.jobject) {
 }
+
+//export Java_io_v_syncbase_internal_Collection_Create
 func Java_io_v_syncbase_internal_Collection_Create(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring, perms C.jobject) {
+	cName := newVStringFromJava(env, name)
+	cHandle := newVStringFromJava(env, handle)
+	var cErr C.v23_syncbase_VError
+	// TODO(razvanm): construct a proper C.v23_syncbase_Permissions based on
+	// the perms object.
+	v23_syncbase_CollectionCreate(cName, cHandle, C.v23_syncbase_Permissions{}, &cErr)
+	maybeThrowException(env, &cErr)
 }
+
+//export Java_io_v_syncbase_internal_Collection_Destroy
 func Java_io_v_syncbase_internal_Collection_Destroy(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring) {
+	cName := newVStringFromJava(env, name)
+	cHandle := newVStringFromJava(env, handle)
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_CollectionDestroy(cName, cHandle, &cErr)
+	maybeThrowException(env, &cErr)
 }
+
+//export Java_io_v_syncbase_internal_Collection_Exists
 func Java_io_v_syncbase_internal_Collection_Exists(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring) C.jboolean {
-	return 0
+	cName := newVStringFromJava(env, name)
+	cHandle := newVStringFromJava(env, handle)
+	var r C.v23_syncbase_Bool
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_CollectionExists(cName, cHandle, &r, &cErr)
+	maybeThrowException(env, &cErr)
+	return C.jboolean(r)
 }
+
+//export Java_io_v_syncbase_internal_Collection_DeleteRange
 func Java_io_v_syncbase_internal_Collection_DeleteRange(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring, start C.jbyteArray, limit C.jbyteArray) {
+	cName := newVStringFromJava(env, name)
+	cHandle := newVStringFromJava(env, handle)
+	cStart := newVBytesFromJava(env, start)
+	cLimit := newVBytesFromJava(env, limit)
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_CollectionDeleteRange(cName, cHandle, cStart, cLimit, &cErr)
+	maybeThrowException(env, &cErr)
 }
+
 func Java_io_v_syncbase_internal_Collection_Scan(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring, start C.jbyteArray, limit C.jbyteArray, callbacks C.jobject) {
 }
 
+//export Java_io_v_syncbase_internal_Row_Exists
 func Java_io_v_syncbase_internal_Row_Exists(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring) C.jboolean {
-	return 0
+	cName := newVStringFromJava(env, name)
+	cHandle := newVStringFromJava(env, handle)
+	var r C.v23_syncbase_Bool
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_RowExists(cName, cHandle, &r, &cErr)
+	maybeThrowException(env, &cErr)
+	return C.jboolean(r)
 }
+
+//export Java_io_v_syncbase_internal_Row_Get
 func Java_io_v_syncbase_internal_Row_Get(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring) C.jbyteArray {
-	return nil
+	cName := newVStringFromJava(env, name)
+	cHandle := newVStringFromJava(env, handle)
+	var r C.v23_syncbase_Bytes
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_RowGet(cName, cHandle, &r, &cErr)
+	maybeThrowException(env, &cErr)
+	return r.extractToJava(env)
 }
+
+//export Java_io_v_syncbase_internal_Row_Put
 func Java_io_v_syncbase_internal_Row_Put(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring, value C.jbyteArray) {
+	cName := newVStringFromJava(env, name)
+	cHandle := newVStringFromJava(env, handle)
+	var cErr C.v23_syncbase_VError
+	cValue := newVBytesFromJava(env, value)
+	v23_syncbase_RowPut(cName, cHandle, cValue, &cErr)
+	maybeThrowException(env, &cErr)
 }
+
+//export Java_io_v_syncbase_internal_Row_Delete
 func Java_io_v_syncbase_internal_Row_Delete(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring) {
+	cName := newVStringFromJava(env, name)
+	cHandle := newVStringFromJava(env, handle)
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_RowDelete(cName, cHandle, &cErr)
+	maybeThrowException(env, &cErr)
+}
+
+//export Java_io_v_syncbase_internal_Blessings_DebugString
+func Java_io_v_syncbase_internal_Blessings_DebugString(env *C.JNIEnv, cls C.jclass) C.jstring {
+	var cDebugString C.v23_syncbase_String
+	v23_syncbase_BlessingStoreDebugString(&cDebugString)
+	return cDebugString.extractToJava(env)
+}
+
+//export Java_io_v_syncbase_internal_Blessings_AppBlessingFromContext
+func Java_io_v_syncbase_internal_Blessings_AppBlessingFromContext(env *C.JNIEnv, cls C.jclass) C.jstring {
+	var cBlessing C.v23_syncbase_String
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_AppBlessingFromContext(&cBlessing, &cErr)
+	maybeThrowException(env, &cErr)
+	return cBlessing.extractToJava(env)
+}
+
+//export Java_io_v_syncbase_internal_Blessings_UserBlessingFromContext
+func Java_io_v_syncbase_internal_Blessings_UserBlessingFromContext(env *C.JNIEnv, cls C.jclass) C.jstring {
+	var cBlessing C.v23_syncbase_String
+	var cErr C.v23_syncbase_VError
+	v23_syncbase_UserBlessingFromContext(&cBlessing, &cErr)
+	maybeThrowException(env, &cErr)
+	return cBlessing.extractToJava(env)
+}
+
+//export Java_io_v_syncbase_internal_Util_Encode
+func Java_io_v_syncbase_internal_Util_Encode(env *C.JNIEnv, cls C.jclass, s C.jstring) C.jstring {
+	cPlainStr := newVStringFromJava(env, s)
+	var cEncodedStr C.v23_syncbase_String
+	v23_syncbase_Encode(cPlainStr, &cEncodedStr)
+	return cEncodedStr.extractToJava(env)
+}
+
+//export Java_io_v_syncbase_internal_Util_EncodeId
+func Java_io_v_syncbase_internal_Util_EncodeId(env *C.JNIEnv, cls C.jclass, obj C.jobject) C.jstring {
+	cId := newVIdFromJava(env, obj)
+	var cEncodedId C.v23_syncbase_String
+	v23_syncbase_EncodeId(cId, &cEncodedId)
+	return cEncodedId.extractToJava(env)
+}
+
+//export Java_io_v_syncbase_internal_Util_NamingJoin
+func Java_io_v_syncbase_internal_Util_NamingJoin(env *C.JNIEnv, cls C.jclass, obj C.jobject) C.jstring {
+	cElements := newVStringsFromJava(env, obj)
+	var cStr C.v23_syncbase_String
+	v23_syncbase_NamingJoin(cElements, &cStr)
+	return cStr.extractToJava(env)
+}
+
+// The functions below are defined in this file and not in jni_types.go due to
+// "inconsistent definitions" errors for various functions (C.NewObjectA and
+// C.SetObjectField for example).
+
+// extractToJava creates an Id object from a v23_syncbase_Id.
+func (x *C.v23_syncbase_Id) extractToJava(env *C.JNIEnv) C.jobject {
+	obj := C.NewObjectA(env, idClass.class, idClass.init, nil)
+	C.SetObjectField(env, obj, idClass.blessing, x.blessing.extractToJava(env))
+	C.SetObjectField(env, obj, idClass.name, x.name.extractToJava(env))
+	x.free()
+	return obj
+}
+
+// newVIds creates a v23_syncbase_Ids from a List<Id>.
+func newVIdsFromJava(env *C.JNIEnv, obj C.jobject) C.v23_syncbase_Ids {
+	if obj == nil {
+		return C.v23_syncbase_Ids{}
+	}
+	listInterface := newJListInterface(env, obj)
+	iterObj := C.CallObjectMethod(env, obj, listInterface.iterator)
+	if C.ExceptionOccurred(env) != nil {
+		panic("newVIds exception while trying to call List.iterator()")
+	}
+
+	iteratorInterface := newJIteratorInterface(env, iterObj)
+	tmp := []C.v23_syncbase_Id{}
+	for C.CallBooleanMethodA(env, iterObj, iteratorInterface.hasNext, nil) == C.JNI_TRUE {
+		idObj := C.CallObjectMethod(env, iterObj, iteratorInterface.next)
+		if C.ExceptionOccurred(env) != nil {
+			panic("newVIds exception while trying to call Iterator.next()")
+		}
+		tmp = append(tmp, newVIdFromJava(env, idObj))
+	}
+
+	size := C.size_t(len(tmp)) * C.size_t(C.sizeof_v23_syncbase_Id)
+	r := C.v23_syncbase_Ids{
+		p: (*C.v23_syncbase_Id)(unsafe.Pointer(C.malloc(size))),
+		n: C.int(len(tmp)),
+	}
+	for i := range tmp {
+		*r.at(i) = tmp[i]
+	}
+	return r
+}
+
+// extractToJava constructs a jobject from a v23_syncbase_Ids. The pointers
+// inside v23_syncbase_Ids will be freed.
+func (x *C.v23_syncbase_Ids) extractToJava(env *C.JNIEnv) C.jobject {
+	obj := C.NewObjectA(env, arrayListClass.class, arrayListClass.init, nil)
+	for i := 0; i < int(x.n); i++ {
+		idObj := x.at(i).extractToJava(env)
+		arg := *(*C.jvalue)(unsafe.Pointer(&idObj))
+		C.CallBooleanMethodA(env, obj, arrayListClass.add, &arg)
+	}
+	x.free()
+	return obj
+}
+
+// newVStringsFromJava creates a v23_syncbase_Strings from a List<String>.
+func newVStringsFromJava(env *C.JNIEnv, obj C.jobject) C.v23_syncbase_Strings {
+	if obj == nil {
+		return C.v23_syncbase_Strings{}
+	}
+	listInterface := newJListInterface(env, obj)
+	iterObj := C.CallObjectMethod(env, obj, listInterface.iterator)
+	if C.ExceptionOccurred(env) != nil {
+		panic("newVStringsFromJava exception while trying to call List.iterator()")
+	}
+
+	iteratorInterface := newJIteratorInterface(env, iterObj)
+	tmp := []C.v23_syncbase_String{}
+	for C.CallBooleanMethodA(env, iterObj, iteratorInterface.hasNext, nil) == C.JNI_TRUE {
+		stringObj := C.CallObjectMethod(env, iterObj, iteratorInterface.next)
+		if C.ExceptionOccurred(env) != nil {
+			panic("newVStringsFromJava exception while trying to call Iterator.next()")
+		}
+		tmp = append(tmp, newVStringFromJava(env, C.jstring(stringObj)))
+	}
+
+	size := C.size_t(len(tmp)) * C.size_t(C.sizeof_v23_syncbase_String)
+	r := C.v23_syncbase_Strings{
+		p: (*C.v23_syncbase_String)(unsafe.Pointer(C.malloc(size))),
+		n: C.int(len(tmp)),
+	}
+	for i := range tmp {
+		*r.at(i) = tmp[i]
+	}
+	return r
+}
+
+// extractToJava constructs a jobject from a v23_syncbase_VError. The pointers
+// from inside v23_syncbase_VError will be freed.
+func (x *C.v23_syncbase_VError) extractToJava(env *C.JNIEnv) C.jobject {
+	if x.id.p == nil {
+		return nil
+	}
+	obj := C.NewObjectA(env, verrorClass.class, verrorClass.init, nil)
+	C.SetObjectField(env, obj, verrorClass.id, x.id.extractToJava(env))
+	C.SetLongField(env, obj, verrorClass.actionCode, C.jlong(x.actionCode))
+	C.SetObjectField(env, obj, verrorClass.message, x.msg.extractToJava(env))
+	C.SetObjectField(env, obj, verrorClass.stack, x.stack.extractToJava(env))
+	x.free()
+	return obj
 }
diff --git a/services/syncbase/bridge/cgo/jni_lib.go b/services/syncbase/bridge/cgo/jni_lib.go
index 1c4e4c2..f06fc81 100644
--- a/services/syncbase/bridge/cgo/jni_lib.go
+++ b/services/syncbase/bridge/cgo/jni_lib.go
@@ -15,29 +15,101 @@
 type jArrayListClass struct {
 	class C.jclass
 	init  C.jmethodID
+	add   C.jmethodID
 }
 
-func (c *jArrayListClass) Init(env *C.JNIEnv) error {
-	var err error
-	c.class, c.init, err = initClass(env, "java/util/ArrayList")
-	if err != nil {
-		return err
+func newJArrayListClass(env *C.JNIEnv) jArrayListClass {
+	cls, init := initClass(env, "java/util/ArrayList")
+	return jArrayListClass{
+		class: cls,
+		init:  init,
+		add:   jGetMethodID(env, cls, "add", "(Ljava/lang/Object;)Z"),
 	}
-	return nil
+}
+
+type jIteratorInterface struct {
+	hasNext C.jmethodID
+	next    C.jmethodID
+}
+
+func newJIteratorInterface(env *C.JNIEnv, obj C.jobject) jIteratorInterface {
+	cls := C.GetObjectClass(env, obj)
+	return jIteratorInterface{
+		hasNext: jGetMethodID(env, cls, "hasNext", "()Z"),
+		next:    jGetMethodID(env, cls, "next", "()Ljava/lang/Object;"),
+	}
 }
 
 type jIdClass struct {
-	class C.jclass
-	init  C.jmethodID
+	class    C.jclass
+	init     C.jmethodID
+	blessing C.jfieldID
+	name     C.jfieldID
 }
 
-func (c *jIdClass) Init(env *C.JNIEnv) error {
-	var err error
-	c.class, c.init, err = initClass(env, "io/v/syncbase/internal/Id")
-	if err != nil {
-		return err
+func newJIdClass(env *C.JNIEnv) jIdClass {
+	cls, init := initClass(env, "io/v/syncbase/internal/Id")
+	return jIdClass{
+		class:    cls,
+		init:     init,
+		blessing: jGetFieldID(env, cls, "blessing", "Ljava/lang/String;"),
+		name:     jGetFieldID(env, cls, "name", "Ljava/lang/String;"),
 	}
-	return nil
+}
+
+type jListInterface struct {
+	iterator C.jmethodID
+	size     C.jmethodID
+}
+
+func newJListInterface(env *C.JNIEnv, obj C.jobject) jListInterface {
+	cls := C.GetObjectClass(env, obj)
+	return jListInterface{
+		size:     jGetMethodID(env, cls, "size", "()I"),
+		iterator: jGetMethodID(env, cls, "iterator", "()Ljava/util/Iterator;"),
+	}
+}
+
+type jSyncgroupMemberInfo struct {
+	class        C.jclass
+	init         C.jmethodID
+	syncPriority C.jfieldID
+	blobDevType  C.jfieldID
+}
+
+func newJSyncgroupMemberInfo(env *C.JNIEnv) jSyncgroupMemberInfo {
+	cls, init := initClass(env, "io/v/syncbase/internal/Database$SyncgroupMemberInfo")
+	return jSyncgroupMemberInfo{
+		class:        cls,
+		init:         init,
+		syncPriority: jGetFieldID(env, cls, "syncPriority", "I"),
+		blobDevType:  jGetFieldID(env, cls, "blobDevType", "I"),
+	}
+}
+
+type jSyncgroupSpec struct {
+	class               C.jclass
+	init                C.jmethodID
+	description         C.jfieldID
+	publishSyncbaseName C.jfieldID
+	permissions         C.jfieldID
+	collections         C.jfieldID
+	mountTables         C.jfieldID
+	isPrivate           C.jfieldID
+}
+
+func newJSyncgroupSpec(env *C.JNIEnv) jSyncgroupSpec {
+	cls, init := initClass(env, "io/v/syncbase/internal/Database$SyncgroupSpec")
+	return jSyncgroupSpec{
+		class:               cls,
+		init:                init,
+		description:         jGetFieldID(env, cls, "description", "Ljava/lang/String;"),
+		publishSyncbaseName: jGetFieldID(env, cls, "publishSyncbaseName", "Ljava/lang/String;"),
+		permissions:         jGetFieldID(env, cls, "permissions", "Lio/v/syncbase/internal/Permissions;"),
+		collections:         jGetFieldID(env, cls, "collections", "Ljava/util/List;"),
+		mountTables:         jGetFieldID(env, cls, "mountTables", "Ljava/util/List;"),
+		isPrivate:           jGetFieldID(env, cls, "isPrivate", "Z"),
+	}
 }
 
 type jVErrorClass struct {
@@ -49,41 +121,27 @@
 	stack      C.jfieldID
 }
 
-func (c *jVErrorClass) Init(env *C.JNIEnv) error {
-	var err error
-	c.class, c.init, err = initClass(env, "io/v/syncbase/internal/VError")
-	if err != nil {
-		return err
+func newJVErrorClass(env *C.JNIEnv) jVErrorClass {
+	cls, init := initClass(env, "io/v/syncbase/internal/VError")
+	return jVErrorClass{
+		class:      cls,
+		init:       init,
+		id:         jGetFieldID(env, cls, "id", "Ljava/lang/String;"),
+		actionCode: jGetFieldID(env, cls, "actionCode", "J"),
+		message:    jGetFieldID(env, cls, "message", "Ljava/lang/String;"),
+		stack:      jGetFieldID(env, cls, "stack", "Ljava/lang/String;"),
 	}
-	c.id, err = JGetFieldID(env, c.class, "id", "Ljava/lang/String;")
-	if err != nil {
-		return err
-	}
-	c.actionCode, err = JGetFieldID(env, c.class, "actionCode", "J")
-	if err != nil {
-		return err
-	}
-	c.message, err = JGetFieldID(env, c.class, "message", "Ljava/lang/String;")
-	if err != nil {
-		return err
-	}
-	c.stack, err = JGetFieldID(env, c.class, "stack", "Ljava/lang/String;")
-	if err != nil {
-		return err
-	}
-	return nil
 }
 
 // initClass returns the jclass and the jmethodID of the default constructor for
 // a class.
-func initClass(env *C.JNIEnv, name string) (C.jclass, C.jmethodID, error) {
-	cls, err := JFindClass(env, name)
+func initClass(env *C.JNIEnv, name string) (C.jclass, C.jmethodID) {
+	cls, err := jFindClass(env, name)
 	if err != nil {
-		return nil, nil, err
+		// The invariant is that we only deal with classes that must be
+		// known to the JVM. A panic indicates a bug in our code.
+		panic(err)
 	}
-	init, err := JGetMethodID(env, cls, "<init>", "()V")
-	if err != nil {
-		return nil, nil, err
-	}
-	return cls, init, nil
+	init := jGetMethodID(env, cls, "<init>", "()V")
+	return cls, init
 }
diff --git a/services/syncbase/bridge/cgo/jni_types.go b/services/syncbase/bridge/cgo/jni_types.go
new file mode 100644
index 0000000..173d79f
--- /dev/null
+++ b/services/syncbase/bridge/cgo/jni_types.go
@@ -0,0 +1,201 @@
+// 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.
+
+// This file contains JNI conversions to/from Java types for the types declared
+// in lib.h. The other file that contains functions related to the types in
+// lib.h is types.go. We need to place the JNI code in a separate file in order
+// to use build tags to restrict the compilation to Java and Android.
+//
+// All "x.extractToJava" methods leave "x" in the same state as "x.free".
+
+// +build java android
+// +build cgo
+
+package main
+
+import "unsafe"
+
+// #include <stdlib.h>
+// #include "jni_wrapper.h"
+// #include "lib.h"
+import "C"
+
+// newVBytesFromJava creates a v23_syncbase_Bytes from a jbyteArray.
+func newVBytesFromJava(env *C.JNIEnv, array C.jbyteArray) C.v23_syncbase_Bytes {
+	r := C.v23_syncbase_Bytes{}
+	n := C.GetArrayLength(env, array)
+	r.n = C.int(n)
+	r.p = (*C.uint8_t)(C.malloc(C.size_t(r.n)))
+	C.GetByteArrayRegion(env, array, 0, n, (*C.jbyte)(unsafe.Pointer(r.p)))
+	// We don't have to check for exceptions because GetByteArrayRegion can
+	// only throw ArrayIndexOutOfBoundsException and we know the requested
+	// amount of elements is valid.
+	return r
+}
+
+// extractToJava constructs a jbyteArray from a v23_syncbase_Bytes. The pointer
+// inside v23_syncbase_Bytes will be freed.
+func (x *C.v23_syncbase_Bytes) extractToJava(env *C.JNIEnv) C.jbyteArray {
+	obj := C.NewByteArray(env, C.jsize(x.n))
+	if C.ExceptionOccurred(env) != nil {
+		panic("NewByteArray OutOfMemoryError exception")
+	}
+	C.SetByteArrayRegion(env, obj, 0, C.jsize(x.n), (*C.jbyte)(unsafe.Pointer(x.p)))
+	// We don't have to check for exceptions because SetByteArrayRegion can
+	// only throw ArrayIndexOutOfBoundsException and we know the requested
+	// amount of elements is valid.
+	x.free()
+	return obj
+}
+
+// newVIdFromJava creates a v23_syncbase_Id from a jobject.
+func newVIdFromJava(env *C.JNIEnv, obj C.jobject) C.v23_syncbase_Id {
+	blessing := C.jstring(C.GetObjectField(env, obj, idClass.blessing))
+	if C.ExceptionOccurred(env) != nil {
+		panic("newVIdFromJava exception while retrieving Id.blessing")
+	}
+
+	name := C.jstring(C.GetObjectField(env, obj, idClass.name))
+	if C.ExceptionOccurred(env) != nil {
+		panic("newVIdFromJava exception while retrieving Id.name")
+	}
+
+	return C.v23_syncbase_Id{
+		blessing: newVStringFromJava(env, blessing),
+		name:     newVStringFromJava(env, name),
+	}
+}
+
+// extractToJava constructs a jstring from a v23_syncbase_String. The pointer
+// inside v23_syncbase_String will be freed. The code is somewhat complicated
+// and inefficient because the NewStringUTF from JNI only works with modified
+// UTF-8 strings (inner nulls are encoded as 0xC0, 0x80 and the string is
+// terminated with a null).
+func (x *C.v23_syncbase_String) extractToJava(env *C.JNIEnv) C.jstring {
+	n := int(x.n)
+	srcPtr := uintptr(unsafe.Pointer(x.p))
+	numNulls := 0
+	for i := 0; i < n; i++ {
+		if *(*byte)(unsafe.Pointer(srcPtr + uintptr(i))) == 0 {
+			numNulls++
+		}
+	}
+	tmp := C.malloc(C.size_t(n + numNulls + 1))
+	defer C.free(tmp)
+	tmpPtr := uintptr(tmp)
+	j := 0
+	for i := 0; i < n; i, j = i+1, j+1 {
+		if *(*byte)(unsafe.Pointer(srcPtr + uintptr(i))) != 0 {
+			*(*byte)(unsafe.Pointer(tmpPtr + uintptr(j))) = *(*byte)(unsafe.Pointer(srcPtr + uintptr(i)))
+			continue
+		}
+		*(*byte)(unsafe.Pointer(tmpPtr + uintptr(j))) = 0xC0
+		j++
+		*(*byte)(unsafe.Pointer(tmpPtr + uintptr(j))) = 0x80
+	}
+	*(*byte)(unsafe.Pointer(tmpPtr + uintptr(j))) = 0
+	r := C.NewStringUTF(env, (*C.char)(tmp))
+	if C.ExceptionOccurred(env) != nil {
+		panic("NewStringUTF OutOfMemoryError exception")
+	}
+	x.free()
+	return r
+}
+
+// newVStringFromJava creates a v23_syncbase_String from a jstring.
+func newVStringFromJava(env *C.JNIEnv, s C.jstring) C.v23_syncbase_String {
+	r := C.v23_syncbase_String{}
+	if s == nil {
+		return r
+	}
+	// Note that GetStringUTFLength does not include a trailing zero.
+	n := int(C.GetStringUTFLength(env, s))
+	r.n = C.int(n)
+	// TODO(razvanm): The JNI documentation doesn't clearly specify whether
+	// the string returned by GetStringUTFRegion is null-terminated. What
+	// I empirically found was that the heap gets corrupted if we allocate
+	// the exact amount of bytes and the string size is of certain sizes (to
+	// be more specific, size of the form 24 + 16 * x). Adding a single
+	// extra byte seems to avoid the issue. I only checked sizes up to 32K.
+	//
+	// Some interesting perspective on the JNI's brokenness:
+	//   http://www.club.cc.cmu.edu/~cmccabe/blog_jni_flaws.html
+	r.p = (*C.char)(C.malloc(C.size_t(r.n + 1)))
+	p := uintptr(unsafe.Pointer(r.p))
+	// Note that we need to use GetStringLength and not GetStringUTFLength
+	// because we need to indicate how many Unicode characters we want to be
+	// copied.
+	C.GetStringUTFRegion(env, s, 0, C.GetStringLength(env, s), r.p)
+	// We don't have to check for exceptions because GetStringUTFRegion can
+	// only throw StringIndexOutOfBoundsException and we know the requested
+	// amount of characters is valid.
+	j := 0
+	for i := 0; i < n; i, j = i+1, j+1 {
+		if i+1 < n && *(*byte)(unsafe.Pointer(p + uintptr(i))) == 0xC0 && *(*byte)(unsafe.Pointer(p + uintptr(i+1))) == 0x80 {
+			*(*byte)(unsafe.Pointer(p + uintptr(j))) = 0
+			i++
+			continue
+		}
+
+		if j == i {
+			continue
+		}
+
+		*(*byte)(unsafe.Pointer(p + uintptr(j))) = *(*byte)(unsafe.Pointer(p + uintptr(i)))
+	}
+	r.p = (*C.char)(C.realloc(unsafe.Pointer(r.p), (C.size_t)(j)))
+	return r
+}
+
+// newVSyncgroupMemberInfoFromJava creates a v23_syncbase_SyncgroupMemberInfo
+// from a jobject.
+func newVSyncgroupMemberInfoFromJava(env *C.JNIEnv, obj C.jobject) C.v23_syncbase_SyncgroupMemberInfo {
+	syncPriority := C.GetIntField(env, obj, syncgroupMemberInfoClass.syncPriority)
+	if C.ExceptionOccurred(env) != nil {
+		panic("newVSyncgroupMemberInfoFromJava exception while retrieving SyncgroupMemberInfo.syncPriority")
+	}
+	blobDevType := C.GetIntField(env, obj, syncgroupMemberInfoClass.blobDevType)
+	if C.ExceptionOccurred(env) != nil {
+		panic("newVSyncgroupMemberInfoFromJava exception while retrieving SyncgroupMemberInfo.blobDevType")
+	}
+	return C.v23_syncbase_SyncgroupMemberInfo{
+		syncPriority: C.uint8_t(syncPriority),
+		blobDevType:  C.uint8_t(blobDevType),
+	}
+}
+
+// newVSyngroupSpecFromJava creates a v23_syncbase_SyncgroupSpec from a jobject.
+func newVSyngroupSpecFromJava(env *C.JNIEnv, obj C.jobject) C.v23_syncbase_SyncgroupSpec {
+	description := C.jstring(C.GetObjectField(env, obj, syncgroupSpecClass.description))
+	if C.ExceptionOccurred(env) != nil {
+		panic("newVSyngroupSpecFromJava exception while retrieving SyncgroupSpec.description")
+	}
+
+	publishSyncbaseName := C.jstring(C.GetObjectField(env, obj, syncgroupSpecClass.publishSyncbaseName))
+	if C.ExceptionOccurred(env) != nil {
+		panic("newVSyngroupSpecFromJava exception while retrieving SyncgroupSpec.publishSyncbaseName")
+	}
+
+	// TODO(razvanm): construct a proper Permissions object based on the
+	// C.v23_syncbase_Permissions.
+
+	collections := C.GetObjectField(env, obj, syncgroupSpecClass.collections)
+	if C.ExceptionOccurred(env) != nil {
+		panic("newVSyngroupSpecFromJava exception while retrieving SyncgroupSpec.collections")
+	}
+	collectionsIds := newVIdsFromJava(env, collections)
+
+	mountTables := C.GetObjectField(env, obj, syncgroupSpecClass.mountTables)
+	if C.ExceptionOccurred(env) != nil {
+		panic("newVSyngroupSpecFromJava exception while retrieving SyncgroupSpec.mountTables")
+	}
+	mountTablesStrings := newVStringsFromJava(env, mountTables)
+
+	return C.v23_syncbase_SyncgroupSpec{
+		description:         newVStringFromJava(env, description),
+		publishSyncbaseName: newVStringFromJava(env, publishSyncbaseName),
+		collections:         collectionsIds,
+		mountTables:         mountTablesStrings,
+	}
+}
diff --git a/services/syncbase/bridge/cgo/jni_util.go b/services/syncbase/bridge/cgo/jni_util.go
index 8d9c68a..7385467 100644
--- a/services/syncbase/bridge/cgo/jni_util.go
+++ b/services/syncbase/bridge/cgo/jni_util.go
@@ -17,7 +17,7 @@
 // #include "lib.h"
 import "C"
 
-func JFindClass(env *C.JNIEnv, name string) (C.jclass, error) {
+func jFindClass(env *C.JNIEnv, name string) (C.jclass, error) {
 	cName := C.CString(name)
 	defer C.free(unsafe.Pointer(cName))
 
@@ -33,7 +33,7 @@
 	return globalRef, nil
 }
 
-func JGetMethodID(env *C.JNIEnv, cls C.jclass, name, sig string) (C.jmethodID, error) {
+func jGetMethodID(env *C.JNIEnv, cls C.jclass, name, sig string) C.jmethodID {
 	cName := C.CString(name)
 	defer C.free(unsafe.Pointer(cName))
 
@@ -42,15 +42,15 @@
 
 	method := C.GetMethodID(env, cls, cName, cSig)
 	if method == nil {
-		return nil, fmt.Errorf("couldn't get method %q with signature %s", name, sig)
+		panic(fmt.Sprintf("couldn't get method %q with signature %s", name, sig))
 	}
 
 	// Note: the validity of the method is bounded by the lifetime of the
 	// ClassLoader that did the loading of the class.
-	return method, nil
+	return method
 }
 
-func JGetFieldID(env *C.JNIEnv, cls C.jclass, name, sig string) (C.jfieldID, error) {
+func jGetFieldID(env *C.JNIEnv, cls C.jclass, name, sig string) C.jfieldID {
 	cName := C.CString(name)
 	defer C.free(unsafe.Pointer(cName))
 
@@ -59,75 +59,10 @@
 
 	field := C.GetFieldID(env, cls, cName, cSig)
 	if field == nil {
-		return nil, fmt.Errorf("couldn't get field %q with signature ", name, sig)
+		panic(fmt.Sprintf("couldn't get field %q with signature ", name, sig))
 	}
 
 	// Note: the validity of the field is bounded by the lifetime of the
 	// ClassLoader that did the loading of the class.
-	return field, nil
-}
-
-// V23SStringToJString constructs a C.jstring from a C.v23_syncbase_String. The
-// code is somewhat complicated and inefficient because the NewStringUTF from
-// JNI only works with modified UTF-8 strings (inner nulls are encoded as 0xC0,
-// 0x80 and the string is terminated with a null).
-func V23SStringToJString(env *C.JNIEnv, src C.v23_syncbase_String) (C.jstring, error) {
-	n := int(src.n)
-	srcPtr := uintptr(unsafe.Pointer(src.p))
-	numNulls := 0
-	for i := 0; i < n; i++ {
-		if *(*byte)(unsafe.Pointer(srcPtr + uintptr(i))) == 0 {
-			numNulls++
-		}
-	}
-	tmp := C.malloc(C.size_t(n + numNulls + 1))
-	defer C.free(tmp)
-	tmpPtr := uintptr(tmp)
-	j := 0
-	for i := 0; i < n; i, j = i+1, j+1 {
-		if *(*byte)(unsafe.Pointer(srcPtr + uintptr(i))) != 0 {
-			*(*byte)(unsafe.Pointer(tmpPtr + uintptr(j))) = *(*byte)(unsafe.Pointer(srcPtr + uintptr(i)))
-			continue
-		}
-		*(*byte)(unsafe.Pointer(tmpPtr + uintptr(j))) = 0xC0
-		j++
-		*(*byte)(unsafe.Pointer(tmpPtr + uintptr(j))) = 0x80
-	}
-	*(*byte)(unsafe.Pointer(tmpPtr + uintptr(j))) = 0
-	r := C.NewStringUTF(env, (*C.char)(tmp))
-	if C.ExceptionOccurred(env) != nil {
-		panic("NewStringUTF OutOfMemoryError exception")
-	}
-	return r, nil
-}
-
-// JStringToV23SString creates a v23_syncbase_String from a jstring. This is the
-// revert of the V23SStringToJString.
-func JStringToV23SString(env *C.JNIEnv, s C.jstring) (C.v23_syncbase_String, error) {
-	r := C.v23_syncbase_String{}
-	r.n = C.int(C.GetStringUTFLength(env, s))
-	r.p = (*C.char)(C.malloc(C.size_t(r.n)))
-	p := uintptr(unsafe.Pointer(r.p))
-	// Note that GetStringUTFLength does not include a trailing zero.
-	n := int(C.GetStringUTFLength(env, s))
-	C.GetStringUTFRegion(env, s, 0, C.GetStringLength(env, s), r.p)
-	// We don't have to check for exceptions because GetStringUTFRegion can
-	// only throw StringIndexOutOfBoundsException and we know the requested
-	// amount of characters is valid.
-	j := 0
-	for i := 0; i < n; i, j = i+1, j+1 {
-		if i+1 < n && *(*byte)(unsafe.Pointer(p + uintptr(i))) == 0xC0 && *(*byte)(unsafe.Pointer(p + uintptr(i+1))) == 0x80 {
-			*(*byte)(unsafe.Pointer(p + uintptr(j))) = 0
-			i++
-			continue
-		}
-
-		if j == i {
-			continue
-		}
-
-		*(*byte)(unsafe.Pointer(p + uintptr(j))) = *(*byte)(unsafe.Pointer(p + uintptr(i)))
-	}
-	r.p = (*C.char)(C.realloc(unsafe.Pointer(r.p), (C.size_t)(j)))
-	return r, nil
+	return field
 }
diff --git a/services/syncbase/bridge/cgo/jni_wrapper.c b/services/syncbase/bridge/cgo/jni_wrapper.c
index d3cb147..be8f139 100644
--- a/services/syncbase/bridge/cgo/jni_wrapper.c
+++ b/services/syncbase/bridge/cgo/jni_wrapper.c
@@ -6,6 +6,14 @@
 
 #include "jni_wrapper.h"
 
+jboolean CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID methodID, jvalue *args) {
+  return (*env)->CallBooleanMethodA(env, obj, methodID, args);
+}
+
+jobject CallObjectMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
+  return (*env)->CallObjectMethod(env,obj,methodID);
+}
+
 void ExceptionClear(JNIEnv *env) {
   (*env)->ExceptionClear(env);
 }
@@ -18,10 +26,22 @@
   return (*env)->FindClass(env, name);
 }
 
+jsize GetArrayLength(JNIEnv *env, jarray array) {
+  return (*env)->GetArrayLength(env, array);
+}
+
 jint GetEnv(JavaVM* jvm, JNIEnv** env, jint version) {
   return (*jvm)->GetEnv(jvm, (void**)env, version);
 }
 
+jint GetIntField(JNIEnv *env, jobject obj, jfieldID fieldID) {
+  return (*env)->GetIntField(env, obj, fieldID);
+}
+
+void GetByteArrayRegion(JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf) {
+  return (*env)->GetByteArrayRegion(env, array, start, len, buf);
+}
+
 jfieldID GetFieldID(JNIEnv *env, jclass cls, const char *name, const char *sig) {
   return (*env)->GetFieldID(env, cls, name, sig);
 }
@@ -30,12 +50,20 @@
   return (*env)->GetMethodID(env, cls, name, args);
 }
 
+jclass GetObjectClass(JNIEnv *env, jobject obj) {
+  return (*env)->GetObjectClass(env, obj);
+}
+
+jobject GetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID) {
+  return (*env)->GetObjectField(env, obj, fieldID);
+}
+
 jsize GetStringLength(JNIEnv *env, jstring string) {
   return (*env)->GetStringLength(env, string);
 }
 
 jsize GetStringUTFLength(JNIEnv *env, jstring string) {
-  return (*env)->GetStringLength(env, string);
+  return (*env)->GetStringUTFLength(env, string);
 }
 
 void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf) {
@@ -50,10 +78,18 @@
   return (*env)->NewObjectA(env, cls, methodID, args);
 }
 
+void SetByteArrayRegion(JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf) {
+  return (*env)->SetByteArrayRegion(env, array, start, len, buf);
+}
+
 void SetLongField(JNIEnv *env, jobject obj, jfieldID fieldID, jlong value) {
   (*env)->SetLongField(env, obj, fieldID, value);
 }
 
+jbyteArray NewByteArray(JNIEnv *env, jsize length) {
+  return (*env)->NewByteArray(env, length);
+}
+
 jstring NewStringUTF(JNIEnv *env, const char *bytes) {
   return (*env)->NewStringUTF(env, bytes);
 }
diff --git a/services/syncbase/bridge/cgo/jni_wrapper.h b/services/syncbase/bridge/cgo/jni_wrapper.h
index ab15784..a4ba834 100644
--- a/services/syncbase/bridge/cgo/jni_wrapper.h
+++ b/services/syncbase/bridge/cgo/jni_wrapper.h
@@ -10,18 +10,28 @@
 
 #include "jni.h"
 
+jboolean CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID methodID, jvalue *args);
+jobject CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID);
+jobject CallObjectMethod(JNIEnv *env, jobject obj, jmethodID methodID);
 void ExceptionClear(JNIEnv *env);
 jthrowable ExceptionOccurred(JNIEnv* env);
 jclass FindClass(JNIEnv* env, const char* name);
+jsize GetArrayLength(JNIEnv *env, jarray array);
 jint GetEnv(JavaVM* jvm, JNIEnv** env, jint version);
+jint GetIntField(JNIEnv *env, jobject obj, jfieldID fieldID);
+void GetByteArrayRegion(JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf);
 jfieldID GetFieldID(JNIEnv *env, jclass cls, const char *name, const char *sig);
 jmethodID GetMethodID(JNIEnv* env, jclass cls, const char* name, const char* sig);
+jclass GetObjectClass(JNIEnv *env, jobject obj);
+jobject GetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID);
 jsize GetStringLength(JNIEnv *env, jstring string);
 jsize GetStringUTFLength(JNIEnv *env, jstring string);
 void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf);
+jbyteArray NewByteArray(JNIEnv *env, jsize length);
 jobject NewGlobalRef(JNIEnv* env, jobject obj);
 jobject NewObjectA(JNIEnv *env, jclass cls, jmethodID methodID, jvalue *args);
 jstring NewStringUTF(JNIEnv *env, const char *bytes);
+void SetByteArrayRegion(JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf);
 void SetLongField(JNIEnv *env, jobject obj, jfieldID fieldID, jlong value);
 void SetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID, jobject value);
 jint Throw(JNIEnv *env, jthrowable obj);
diff --git a/services/syncbase/bridge/cgo/types.go b/services/syncbase/bridge/cgo/types.go
index 4b45351..298b289 100644
--- a/services/syncbase/bridge/cgo/types.go
+++ b/services/syncbase/bridge/cgo/types.go
@@ -15,7 +15,10 @@
 	"v.io/v23/vom"
 )
 
-// All "x.toFoo" methods free the memory associated with x.
+// All "x.toFoo" methods leave x in the same state as "x.free".
+// The "x.free" methods are idempotent.
+//
+// TODO(razvanm): Change from "x.toFoo" to "x.extract".
 
 /*
 #include <stdlib.h>
@@ -82,8 +85,19 @@
 	if x.p == nil {
 		return nil
 	}
-	defer C.free(unsafe.Pointer(x.p))
-	return C.GoBytes(unsafe.Pointer(x.p), x.n)
+	res := C.GoBytes(unsafe.Pointer(x.p), x.n)
+	C.free(unsafe.Pointer(x.p))
+	x.p = nil
+	x.n = 0
+	return res
+}
+
+func (x *C.v23_syncbase_Bytes) free() {
+	if x.p != nil {
+		C.free(unsafe.Pointer(x.p))
+		x.p = nil
+	}
+	x.n = 0
 }
 
 ////////////////////////////////////////////////////////////
@@ -122,11 +136,13 @@
 	if x.p == nil {
 		return nil
 	}
-	defer C.free(unsafe.Pointer(x.p))
 	res := make([]wire.CollectionRowPattern, x.n)
 	for i := 0; i < int(x.n); i++ {
 		res[i] = x.at(i).toCollectionRowPattern()
 	}
+	C.free(unsafe.Pointer(x.p))
+	x.p = nil
+	x.n = 0
 	return res
 }
 
@@ -145,6 +161,11 @@
 	}
 }
 
+func (x *C.v23_syncbase_Id) free() {
+	x.blessing.free()
+	x.name.free()
+}
+
 ////////////////////////////////////////////////////////////
 // C.v23_syncbase_Ids
 
@@ -164,14 +185,28 @@
 	if x.p == nil {
 		return nil
 	}
-	defer C.free(unsafe.Pointer(x.p))
 	res := make([]wire.Id, x.n)
 	for i := 0; i < int(x.n); i++ {
 		res[i] = x.at(i).toId()
 	}
+	C.free(unsafe.Pointer(x.p))
+	x.p = nil
+	x.n = 0
 	return res
 }
 
+func (x *C.v23_syncbase_Ids) free() {
+	if x.p == nil {
+		return
+	}
+	for i := 0; i < int(x.n); i++ {
+		x.at(i).free()
+	}
+	C.free(unsafe.Pointer(x.p))
+	x.p = nil
+	x.n = 0
+}
+
 ////////////////////////////////////////////////////////////
 // C.v23_syncbase_KeyValue
 
@@ -215,8 +250,19 @@
 	if x.p == nil {
 		return ""
 	}
-	defer C.free(unsafe.Pointer(x.p))
-	return C.GoStringN(x.p, x.n)
+	res := C.GoStringN(x.p, x.n)
+	C.free(unsafe.Pointer(x.p))
+	x.p = nil
+	x.n = 0
+	return res
+}
+
+func (x *C.v23_syncbase_String) free() {
+	if x.p != nil {
+		C.free(unsafe.Pointer(x.p))
+		x.p = nil
+	}
+	x.n = 0
 }
 
 ////////////////////////////////////////////////////////////
@@ -238,11 +284,13 @@
 	if x.p == nil {
 		return nil
 	}
-	defer C.free(unsafe.Pointer(x.p))
 	res := make([]string, x.n)
 	for i := 0; i < int(x.n); i++ {
 		res[i] = x.at(i).toString()
 	}
+	C.free(unsafe.Pointer(x.p))
+	x.p = nil
+	x.n = 0
 	return res
 }
 
@@ -319,6 +367,13 @@
 	x.stack.init(verror.Stack(err).String())
 }
 
+func (x *C.v23_syncbase_VError) free() {
+	x.id.free()
+	x.actionCode = C.uint(0)
+	x.msg.free()
+	x.stack.free()
+}
+
 ////////////////////////////////////////////////////////////
 // C.v23_syncbase_WatchChange