add support for BlessingRoots.Dump()

Change-Id: Iafe72f09a1f121608a011fef918eac959bc77739
diff --git a/util/call.go b/util/call.go
index c9c73f2..faa63f0 100644
--- a/util/call.go
+++ b/util/call.go
@@ -315,6 +315,47 @@
 	return ret, nil
 }
 
+// CallMultimapMethod calls a Java method that returns a Multimap.
+func CallMultimapMethod(env interface{}, object interface{}, name string, argSigns []Sign, args ...interface{}) (map[unsafe.Pointer][]unsafe.Pointer, error) {
+	jMultimap, err := CallObjectMethod(env, object, name, argSigns, MultimapSign, args...)
+	if err != nil {
+		return nil, err
+	}
+	if jMultimap == nil {
+		return nil, nil
+	}
+	jEntrySet, err := CallObjectMethod(env, jMultimap, "entrySet", nil, SetSign)
+	if err != nil {
+		return nil, err
+	}
+	jIter, err := CallObjectMethod(env, jEntrySet, "iterator", nil, IteratorSign)
+	if err != nil {
+		return nil, err
+	}
+	ret := make(map[unsafe.Pointer][]unsafe.Pointer)
+	for {
+		if hasNext, err := CallBooleanMethod(env, jIter, "hasNext", nil); err != nil {
+			return nil, err
+		} else if !hasNext {
+			break
+		}
+		jEntry, err := CallObjectMethod(env, jIter, "next", nil, ObjectSign)
+		if err != nil {
+			return nil, err
+		}
+		jKey, err := CallObjectMethod(env, jEntry, "getKey", nil, ObjectSign)
+		if err != nil {
+			return nil, err
+		}
+		jValue, err := CallObjectMethod(env, jEntry, "getValue", nil, ObjectSign)
+		if err != nil {
+			return nil, err
+		}
+		ret[jKey] = append(ret[jKey], jValue)
+	}
+	return ret, nil
+}
+
 // CallBooleanMethod calls a Java method that returns a boolean.
 func CallBooleanMethod(env interface{}, object interface{}, name string, argSigns []Sign, args ...interface{}) (bool, error) {
 	jenv, jobject, jmid, valArray, freeFunc, err := setupMethodCall(env, object, name, argSigns, BoolSign, args)
diff --git a/util/sign.go b/util/sign.go
index 58171c2..2256559 100644
--- a/util/sign.go
+++ b/util/sign.go
@@ -43,6 +43,8 @@
 	SetSign = ClassSign("java.util.Set")
 	// MapSign denotes a signature of a Java Map type.
 	MapSign = ClassSign("java.util.Map")
+	// MultimapSign denotes a signature of a Guava Multimap type.
+	MultimapSign = ClassSign("com.google.common.collect.Multimap")
 	// IteratorSign denotes a signature of a Java Iterator type.
 	IteratorSign = ClassSign("java.util.Iterator")
 	// ByteArraySign denotes a signature of a Java byte array type.
diff --git a/util/util.go b/util/util.go
index ff137f6..0c313d7 100644
--- a/util/util.go
+++ b/util/util.go
@@ -53,6 +53,8 @@
 	jStringClass C.jclass
 	// Global reference for java.util.HashMap class.
 	jHashMapClass C.jclass
+	// Global reference for com.google.common.collect.HashMultimap class.
+	jHashMultimapClass C.jclass
 	// Global reference for []byte class.
 	jByteArrayClass C.jclass
 	// Cached Java VM.
@@ -127,6 +129,11 @@
 		return err
 	}
 	jHashMapClass = C.jclass(class)
+	class, err = JFindClass(env, "com/google/common/collect/HashMultimap")
+	if err != nil {
+		return err
+	}
+	jHashMultimapClass = C.jclass(class)
 	class, err = JFindClass(env, "[B")
 	if err != nil {
 		return err
@@ -642,6 +649,27 @@
 	return unsafe.Pointer(jMap), nil
 }
 
+// JObjectMultimap converts the provided Go map of Java objects into a Java
+// object multimap.
+// NOTE: Because CGO creates package-local types and because this method may be
+// invoked from a different package, Java types are passed in an empty interface
+// and then cast into their package local types.
+func JObjectMultimap(jEnv interface{}, goMap map[interface{}][]interface{}) (unsafe.Pointer, error) {
+	env := getEnv(jEnv)
+	jMap, err := NewObject(env, jHashMultimapClass, nil)
+	if err != nil {
+		return nil, err
+	}
+	for jKey, jVals := range goMap {
+		for _, jVal := range jVals {
+			if _, err := CallBooleanMethod(env, jMap, "put", []Sign{ObjectSign, ObjectSign}, getObject(jKey), getObject(jVal)); err != nil {
+				return nil, err
+			}
+		}
+	}
+	return unsafe.Pointer(jMap), nil
+}
+
 // GoObjectMap converts the provided Java object map into a Go map of Java
 // objects.
 // NOTE: Because CGO creates package-local types and because this method may be
diff --git a/v23/security/jni.go b/v23/security/jni.go
index d243b2b..31c5138 100644
--- a/v23/security/jni.go
+++ b/v23/security/jni.go
@@ -605,6 +605,34 @@
 	return C.jstring(jutil.JString(env, str))
 }
 
+//export Java_io_v_v23_security_BlessingRootsImpl_nativeDump
+func Java_io_v_v23_security_BlessingRootsImpl_nativeDump(env *C.JNIEnv, jBlessingStoreImpl C.jobject, goPtr C.jlong) C.jobject {
+	dump := (*(*security.BlessingRoots)(jutil.Ptr(goPtr))).Dump()
+	result := make(map[interface{}][]interface{})
+	for pattern, keys := range dump {
+		jBlessingPattern, err := JavaBlessingPattern(env, pattern)
+		if err != nil {
+			jutil.JThrowV(env, err)
+			return nil
+		}
+		jPublicKeys := make([]interface{}, len(keys))
+		for i, key := range keys {
+			var err error
+			if jPublicKeys[i], err = JavaPublicKey(env, key); err != nil {
+				jutil.JThrowV(env, err)
+				return nil
+			}
+		}
+		result[jBlessingPattern] = jPublicKeys
+	}
+	jMap, err := jutil.JObjectMultimap(env, result)
+	if err != nil {
+		jutil.JThrowV(env, err)
+		return nil
+	}
+	return C.jobject(jMap)
+}
+
 //export Java_io_v_v23_security_BlessingRootsImpl_nativeFinalize
 func Java_io_v_v23_security_BlessingRootsImpl_nativeFinalize(env *C.JNIEnv, jBlessingRootsImpl C.jobject, goPtr C.jlong) {
 	jutil.GoUnref(jutil.Ptr(goPtr))
diff --git a/v23/security/roots.go b/v23/security/roots.go
index 533eafa..8f33278 100644
--- a/v23/security/roots.go
+++ b/v23/security/roots.go
@@ -95,3 +95,32 @@
 	}
 	return ret
 }
+
+func (r *blessingRoots) Dump() map[security.BlessingPattern][]security.PublicKey {
+	env, freeFunc := jutil.GetEnv()
+	defer freeFunc()
+	ret, err := jutil.CallMultimapMethod(env, r.jBlessingRoots, "dump", []jutil.Sign{})
+	if err != nil {
+		log.Printf("Couldn't get Java Dump: %v", err)
+		return nil
+	}
+	result := make(map[security.BlessingPattern][]security.PublicKey)
+	for jPattern, jKeys := range ret {
+		pattern, err := GoBlessingPattern(env, jPattern)
+		if err != nil {
+			log.Printf("Couldn't convert Java BlessingPattern: %v", err)
+			return nil
+		}
+		var entry []security.PublicKey
+		for _, jKey := range jKeys {
+			key, err := GoPublicKey(env, jKey)
+			if err != nil {
+				log.Printf("Couldn't convert Java PublicKey: %v", err)
+				return nil
+			}
+			entry = append(entry, key)
+		}
+		result[pattern] = entry
+	}
+	return result
+}