x/ref: jni cgo bridge for Invite Scan and Neighborhood Ad/Scan
Implements the jni layer on top of cgo.
We expose hooks to Invite Scan and Neighborhood Ad/Scan as well
as the corresponding Stop functions.
This wasn't perfectly tested, but I know that
- It compiles.
- TODOs app compiles with these changes and will at least get to the
main screen. It will crash on watch and skips the login attempt.
MultiPart: 1/2
Change-Id: I6b32d0006bc6f26f0b3848c9497fc6385c26c95d
diff --git a/services/syncbase/bridge/cgo/impl.go b/services/syncbase/bridge/cgo/impl.go
index 25cceef..a0788e8 100644
--- a/services/syncbase/bridge/cgo/impl.go
+++ b/services/syncbase/bridge/cgo/impl.go
@@ -72,16 +72,10 @@
static void CallDbSyncgroupInvitesOnInvite(v23_syncbase_DbSyncgroupInvitesCallbacks cbs, v23_syncbase_Invite i) {
cbs.onInvite(cbs.handle, i);
}
-static void CallDbSyncgroupInvitesOnDone(v23_syncbase_DbSyncgroupInvitesCallbacks cbs) {
- cbs.onDone(cbs.handle);
-}
static void CallNeighborhoodScanCallbacksOnPeer(v23_syncbase_NeighborhoodScanCallbacks cbs, v23_syncbase_AppPeer p) {
cbs.onPeer(cbs.handle, p);
}
-static void CallNeighborhoodScanCallbacksOnDone(v23_syncbase_NeighborhoodScanCallbacks cbs) {
- cbs.onDone(cbs.handle);
-}
*/
import "C"
@@ -98,7 +92,6 @@
// testLogin indicates if the test login mode is used. This is triggered
// by using an empty identity provider.
testLogin bool
-
// neighborhoodAdStatus tracks the status of the neighborhood advertisement.
neighborhoodAdStatus *adStatus
)
@@ -123,6 +116,7 @@
if isLoggedIn() {
v23_syncbase_Serve(&cErr)
}
+ neighborhoodAdStatus = newAdStatus()
}
type adStatus struct {
@@ -362,7 +356,6 @@
for {
peer, ok := <-scanChan
if !ok {
- C.CallNeighborhoodScanCallbacksOnDone(cbs)
break
}
cPeer := C.v23_syncbase_AppPeer{}
@@ -771,7 +764,6 @@
for {
invite, ok := <-scanChan
if !ok {
- C.CallDbSyncgroupInvitesOnDone(cbs)
break
}
cInvite := C.v23_syncbase_Invite{}
diff --git a/services/syncbase/bridge/cgo/jni.go b/services/syncbase/bridge/cgo/jni.go
index 2493d23..9cc9098 100644
--- a/services/syncbase/bridge/cgo/jni.go
+++ b/services/syncbase/bridge/cgo/jni.go
@@ -43,6 +43,22 @@
0, v23_syncbase_internal_onKeyValue, v23_syncbase_internal_onDone};
return cbs;
}
+
+void v23_syncbase_internal_onInvite(v23_syncbase_Handle handle, v23_syncbase_Invite);
+
+static v23_syncbase_DbSyncgroupInvitesCallbacks newVSyncgroupInvitesCallbacks() {
+ v23_syncbase_DbSyncgroupInvitesCallbacks cbs = {
+ 0, v23_syncbase_internal_onInvite};
+ return cbs;
+}
+
+void v23_syncbase_internal_onPeer(v23_syncbase_Handle handle, v23_syncbase_AppPeer);
+
+static v23_syncbase_NeighborhoodScanCallbacks newVNeighborhoodScanCallbacks() {
+ v23_syncbase_NeighborhoodScanCallbacks cbs = {
+ 0, v23_syncbase_internal_onPeer};
+ return cbs;
+}
*/
import "C"
@@ -55,7 +71,9 @@
hashMapClass jHashMap
idClass jIdClass
keyValueClass jKeyValue
+ neighborhoodPeerClass jNeighborhoodPeer
permissionsClass jPermissions
+ syncgroupInviteClass jSyncgroupInvite
syncgroupMemberInfoClass jSyncgroupMemberInfo
syncgroupSpecClass jSyncgroupSpec
verrorClass jVErrorClass
@@ -85,7 +103,9 @@
hashMapClass = newJHashMap(env)
idClass = newJIdClass(env)
keyValueClass = newJKeyValue(env)
+ neighborhoodPeerClass = newJNeighborhoodPeer(env)
permissionsClass = newJPermissions(env)
+ syncgroupInviteClass = newJSyncgroupInvite(env)
syncgroupMemberInfoClass = newJSyncgroupMemberInfo(env)
syncgroupSpecClass = newJSyncgroupSpec(env)
verrorClass = newJVErrorClass(env)
@@ -433,6 +453,46 @@
maybeThrowException(env, &cErr)
}
+//export v23_syncbase_internal_onInvite
+func v23_syncbase_internal_onInvite(handle C.v23_syncbase_Handle, invite C.v23_syncbase_Invite) {
+ id := uint64(uintptr(handle))
+ h := globalRefMap.Get(id).(*inviteCallbacksHandle)
+ env, free := getEnv()
+ obj := invite.extractToJava(env)
+ arg := *(*C.jvalue)(unsafe.Pointer(&obj))
+ C.CallVoidMethodA(env, C.jobject(unsafe.Pointer(h.obj)), h.callbacks.onInvite, &arg)
+ if C.ExceptionOccurred(env) != nil {
+ C.ExceptionDescribe(env)
+ panic("java exception")
+ }
+ free()
+}
+
+type inviteCallbacksHandle struct {
+ obj uintptr
+ callbacks jSyncgroupInvitesCallbacks
+}
+
+//export Java_io_v_syncbase_internal_Database_SyncgroupInvitesNewScan
+func Java_io_v_syncbase_internal_Database_SyncgroupInvitesNewScan(env *C.JNIEnv, cls C.jclass, name C.jstring, callbacks C.jobject) C.jlong {
+ cName := newVStringFromJava(env, name)
+ cbs := C.newVSyncgroupInvitesCallbacks()
+ cbs.handle = C.v23_syncbase_Handle(uintptr(globalRefMap.Add(&inviteCallbacksHandle{
+ obj: uintptr(unsafe.Pointer(C.NewGlobalRef(env, callbacks))),
+ callbacks: newJSyncgroupInvitesCallbacks(env, callbacks),
+ })))
+ var cErr C.v23_syncbase_VError
+ var scanId C.uint64_t
+ v23_syncbase_DbSyncgroupInvitesNewScan(cName, cbs, &scanId, &cErr)
+ maybeThrowException(env, &cErr)
+ return C.jlong(scanId)
+}
+
+//export Java_io_v_syncbase_internal_Database_SyncgroupInvitesStopScan
+func Java_io_v_syncbase_internal_Database_SyncgroupInvitesStopScan(env *C.JNIEnv, cls C.jclass, scanId C.jlong) {
+ v23_syncbase_DbSyncgroupInvitesStopScan(C.uint64_t(scanId))
+}
+
//export Java_io_v_syncbase_internal_Collection_GetPermissions
func Java_io_v_syncbase_internal_Collection_GetPermissions(env *C.JNIEnv, cls C.jclass, name C.jstring, handle C.jstring) C.jobject {
cName := newVStringFromJava(env, name)
@@ -550,6 +610,65 @@
maybeThrowException(env, &cErr)
}
+//export Java_io_v_syncbase_internal_Neighborhood_StartAdvertising
+func Java_io_v_syncbase_internal_Neighborhood_StartAdvertising(env *C.JNIEnv, cls C.jclass, visibility C.jobject) {
+ cVisibility := newVStringsFromJava(env, visibility)
+ var cErr C.v23_syncbase_VError
+ v23_syncbase_NeighborhoodStartAdvertising(cVisibility, &cErr)
+ maybeThrowException(env, &cErr)
+}
+
+//export Java_io_v_syncbase_internal_Neighborhood_StopAdvertising
+func Java_io_v_syncbase_internal_Neighborhood_StopAdvertising(env *C.JNIEnv, cls C.jclass) {
+ v23_syncbase_NeighborhoodStopAdvertising()
+}
+
+//export Java_io_v_syncbase_internal_Neighborhood_IsAdvertising
+func Java_io_v_syncbase_internal_Neighborhood_IsAdvertising(env *C.JNIEnv, cls C.jclass) C.jboolean {
+ var x C.v23_syncbase_Bool
+ v23_syncbase_NeighborhoodIsAdvertising(&x)
+ return C.jboolean(x)
+}
+
+//export v23_syncbase_internal_onPeer
+func v23_syncbase_internal_onPeer(handle C.v23_syncbase_Handle, peer C.v23_syncbase_AppPeer) {
+ id := uint64(uintptr(handle))
+ h := globalRefMap.Get(id).(*peerCallbacksHandle)
+ env, free := getEnv()
+ obj := peer.extractToJava(env)
+ arg := *(*C.jvalue)(unsafe.Pointer(&obj))
+ C.CallVoidMethodA(env, C.jobject(unsafe.Pointer(h.obj)), h.callbacks.onPeer, &arg)
+ if C.ExceptionOccurred(env) != nil {
+ C.ExceptionDescribe(env)
+ panic("java exception")
+ }
+ free()
+}
+
+type peerCallbacksHandle struct {
+ obj uintptr
+ callbacks jNeighborhoodScanCallbacks
+}
+
+//export Java_io_v_syncbase_internal_Neighborhood_NewScan
+func Java_io_v_syncbase_internal_Neighborhood_NewScan(env *C.JNIEnv, cls C.jclass, callbacks C.jobject) C.jlong {
+ cbs := C.newVNeighborhoodScanCallbacks()
+ cbs.handle = C.v23_syncbase_Handle(uintptr(globalRefMap.Add(&peerCallbacksHandle{
+ obj: uintptr(unsafe.Pointer(C.NewGlobalRef(env, callbacks))),
+ callbacks: newJNeigbhorhoodScanCallbacks(env, callbacks),
+ })))
+ var cErr C.v23_syncbase_VError
+ var scanId C.uint64_t
+ v23_syncbase_NeighborhoodNewScan(cbs, &scanId, &cErr)
+ maybeThrowException(env, &cErr)
+ return C.jlong(scanId)
+}
+
+//export Java_io_v_syncbase_internal_Neighborhood_StopScan
+func Java_io_v_syncbase_internal_Neighborhood_StopScan(env *C.JNIEnv, cls C.jclass, scanId C.jlong) {
+ v23_syncbase_NeighborhoodStopScan(C.uint64_t(scanId))
+}
+
//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 {
cName := newVStringFromJava(env, name)
@@ -647,6 +766,14 @@
// All the extractToJava methods return Java types and deallocate all the
// pointers inside v23_syncbase_* variable.
+func (x *C.v23_syncbase_AppPeer) extractToJava(env *C.JNIEnv) C.jobject {
+ obj := C.NewObjectA(env, neighborhoodPeerClass.class, neighborhoodPeerClass.init, nil)
+ C.SetObjectField(env, obj, neighborhoodPeerClass.appName, x.appName.extractToJava(env))
+ C.SetObjectField(env, obj, neighborhoodPeerClass.blessings, x.blessings.extractToJava(env))
+ C.SetBooleanField(env, obj, neighborhoodPeerClass.isLost, x.isLost.extractToJava())
+ return obj
+}
+
func (x *C.v23_syncbase_ChangeType) extractToJava(env *C.JNIEnv) C.jobject {
var obj C.jobject
switch *x {
@@ -722,6 +849,14 @@
return obj
}
+func (x *C.v23_syncbase_Invite) extractToJava(env *C.JNIEnv) C.jobject {
+ obj := C.NewObjectA(env, syncgroupInviteClass.class, syncgroupInviteClass.init, nil)
+ C.SetObjectField(env, obj, syncgroupInviteClass.syncgroup, x.syncgroup.extractToJava(env))
+ C.SetObjectField(env, obj, syncgroupInviteClass.addresses, x.addresses.extractToJava(env))
+ C.SetObjectField(env, obj, syncgroupInviteClass.blessingNames, x.blessingNames.extractToJava(env))
+ return obj
+}
+
func (x *C.v23_syncbase_KeyValue) extractToJava(env *C.JNIEnv) C.jobject {
obj := C.NewObjectA(env, keyValueClass.class, keyValueClass.init, nil)
C.SetObjectField(env, obj, keyValueClass.key, x.key.extractToJava(env))
diff --git a/services/syncbase/bridge/cgo/jni_lib.go b/services/syncbase/bridge/cgo/jni_lib.go
index 45aa6b3..9287212 100644
--- a/services/syncbase/bridge/cgo/jni_lib.go
+++ b/services/syncbase/bridge/cgo/jni_lib.go
@@ -321,6 +321,66 @@
}
}
+type jSyncgroupInvite struct {
+ class C.jclass
+ init C.jmethodID
+ syncgroup C.jfieldID
+ addresses C.jfieldID
+ blessingNames C.jfieldID
+}
+
+func newJSyncgroupInvite(env *C.JNIEnv) jSyncgroupInvite {
+ cls, init := initClass(env, "io/v/syncbase/core/SyncgroupInvite")
+ return jSyncgroupInvite{
+ class: cls,
+ init: init,
+ syncgroup: jGetFieldID(env, cls, "syncgroup", "Lio/v/syncbase/core/Id;"),
+ addresses: jGetFieldID(env, cls, "addresses", "Ljava/util/List;"),
+ blessingNames: jGetFieldID(env, cls, "blessingNames", "Ljava/util/List;"),
+ }
+}
+
+type jSyncgroupInvitesCallbacks struct {
+ onInvite C.jmethodID
+}
+
+func newJSyncgroupInvitesCallbacks(env *C.JNIEnv, obj C.jobject) jSyncgroupInvitesCallbacks {
+ cls := C.GetObjectClass(env, obj)
+ return jSyncgroupInvitesCallbacks{
+ onInvite: jGetMethodID(env, cls, "onInvite", "(Lio/v/syncbase/core/SyncgroupInvite;)V"),
+ }
+}
+
+type jNeighborhoodPeer struct {
+ class C.jclass
+ init C.jmethodID
+ appName C.jfieldID
+ blessings C.jfieldID
+ isLost C.jfieldID
+}
+
+func newJNeighborhoodPeer(env *C.JNIEnv) jNeighborhoodPeer {
+ cls, init := initClass(env, "io/v/syncbase/core/NeighborhoodPeer")
+ return jNeighborhoodPeer{
+ class: cls,
+ init: init,
+ appName: jGetFieldID(env, cls, "appName", "Ljava/lang/String;"),
+ blessings: jGetFieldID(env, cls, "blessings", "Ljava/lang/String;"),
+ isLost: jGetFieldID(env, cls, "isLost", "Z"),
+ }
+}
+
+type jNeighborhoodScanCallbacks struct {
+ onPeer C.jmethodID
+}
+
+func newJNeigbhorhoodScanCallbacks(env *C.JNIEnv, obj C.jobject) jNeighborhoodScanCallbacks {
+ cls := C.GetObjectClass(env, obj)
+ return jNeighborhoodScanCallbacks{
+ onPeer: jGetMethodID(env, cls, "onPeer", "(Lio/v/syncbase/core/NeighborhoodPeer;)V"),
+ }
+}
+
// 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) {
diff --git a/services/syncbase/bridge/cgo/lib.h b/services/syncbase/bridge/cgo/lib.h
index 553eadc..c129a0b 100644
--- a/services/syncbase/bridge/cgo/lib.h
+++ b/services/syncbase/bridge/cgo/lib.h
@@ -140,6 +140,7 @@
typedef struct {
v23_syncbase_Id syncgroup;
v23_syncbase_Strings addresses;
+ v23_syncbase_Strings blessingNames;
} v23_syncbase_Invite;
// syncbase.discovery.AppPeer
@@ -169,13 +170,11 @@
typedef struct {
v23_syncbase_Handle handle;
void (*onInvite)(v23_syncbase_Handle handle, v23_syncbase_Invite);
- void (*onDone)(v23_syncbase_Handle handle);
} v23_syncbase_DbSyncgroupInvitesCallbacks;
typedef struct {
v23_syncbase_Handle handle;
void (*onPeer)(v23_syncbase_Handle handle, v23_syncbase_AppPeer);
- void (*onDone)(v23_syncbase_Handle handle);
} v23_syncbase_NeighborhoodScanCallbacks;
#endif // V23_SYNCBASE_LIB_H_
diff --git a/services/syncbase/bridge/cgo/types.go b/services/syncbase/bridge/cgo/types.go
index 680011d..6492064 100644
--- a/services/syncbase/bridge/cgo/types.go
+++ b/services/syncbase/bridge/cgo/types.go
@@ -452,6 +452,7 @@
func (x *C.v23_syncbase_Invite) init(invite discovery.Invite) {
x.syncgroup.init(invite.Syncgroup)
x.addresses.init(invite.Addresses)
+ x.blessingNames.init(invite.BlessingNames)
}
////////////////////////////////////////////////////////////
diff --git a/services/syncbase/discovery/discovery.go b/services/syncbase/discovery/discovery.go
index 3273aad..9dea36a 100644
--- a/services/syncbase/discovery/discovery.go
+++ b/services/syncbase/discovery/discovery.go
@@ -15,6 +15,7 @@
"v.io/v23/context"
"v.io/v23/conventions"
"v.io/v23/discovery"
+ "v.io/v23/naming"
"v.io/v23/security"
wire "v.io/v23/services/syncbase"
"v.io/v23/syncbase/util"
@@ -920,10 +921,11 @@
// Invite represents an invitation to join a syncgroup as found via Discovery.
type Invite struct {
- Syncgroup wire.Id // Syncgroup is the Id of the syncgroup you've been invited to.
- Addresses []string // Addresses are the list of addresses of the inviting server.
- Lost bool // If this invite is a lost invite or not.
- key string // Unexported. The implementation uses this key to de-dupe ads.
+ Syncgroup wire.Id // Syncgroup is the Id of the syncgroup you've been invited to.
+ Addresses []string // Addresses are the list of addresses of the inviting server.
+ BlessingNames []string // BlessingNames are the list of blessings of the inviting server.
+ Lost bool // If this invite is a lost invite or not.
+ key string // Unexported. The implementation uses this key to de-dupe ads.
}
func makeInvite(ad discovery.Advertisement) (Invite, wire.Id, bool) {
@@ -942,17 +944,38 @@
return Invite{}, dbId, false
}
i := Invite{
- Addresses: append([]string{}, ad.Addresses...),
- Syncgroup: sgId,
+ Addresses: append([]string{}, ad.Addresses...),
+ BlessingNames: computeBlessingNamesFromEndpoints(ad.Addresses),
+ Syncgroup: sgId,
}
sort.Strings(i.Addresses)
i.key = fmt.Sprintf("%v", i)
return i, dbId, true
}
+// computeBlessingNamesFromEndpoints parses endpoints and concatenates found blessings together.
+func computeBlessingNamesFromEndpoints(addresses []string) []string {
+ blessingsMap := make(map[string]struct{})
+
+ for _, address := range addresses {
+ if e, err := naming.ParseEndpoint(address); err != nil {
+ for _, name := range e.BlessingNames() {
+ blessingsMap[name] = struct{}{}
+ }
+ }
+ }
+
+ blessingNames := make([]string, len(blessingsMap))
+ for blessings, _ := range blessingsMap {
+ blessingNames = append(blessingNames, blessings)
+ }
+ return blessingNames
+}
+
func (i Invite) copy() copyable {
cp := i
cp.Addresses = append([]string(nil), i.Addresses...)
+ cp.BlessingNames = append([]string(nil), i.BlessingNames...)
return cp
}
@@ -967,6 +990,7 @@
func (i Invite) copyAsLost() copyable {
cp := i
cp.Addresses = append([]string(nil), i.Addresses...)
+ cp.BlessingNames = append([]string(nil), i.BlessingNames...)
cp.Lost = true
return cp
}