TBR Java: updated wakeup support.
MultiPart: 1/2
Change-Id: Iaa7b01becf102ca93d3bd369b5fcb9f72f99102d
diff --git a/impl/google/jni.go b/impl/google/jni.go
index 8c27dbc..afb29c5 100644
--- a/impl/google/jni.go
+++ b/impl/google/jni.go
@@ -38,7 +38,6 @@
if err := jservices.Init(env); err != nil {
return err
}
-
if err := jdiscovery.Init(env); err != nil {
return err
}
diff --git a/impl/google/namespace/jni.go b/impl/google/namespace/jni.go
index 965376a..d315f27 100644
--- a/impl/google/namespace/jni.go
+++ b/impl/google/namespace/jni.go
@@ -63,7 +63,7 @@
if err != nil {
return
}
- opts, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+ opts, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
if err != nil {
return
}
@@ -125,7 +125,7 @@
if err != nil {
return
}
- options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+ options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
return
}
@@ -151,7 +151,7 @@
if err != nil {
return
}
- options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+ options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
return
}
@@ -175,7 +175,7 @@
if err != nil {
return
}
- options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+ options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
if err != nil {
return
}
@@ -204,7 +204,7 @@
if err != nil {
return
}
- options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+ options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
if err != nil {
return
}
@@ -248,7 +248,7 @@
if err != nil {
return
}
- options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+ options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
if err != nil {
return
}
@@ -287,6 +287,27 @@
})
}
+//export Java_io_v_impl_google_namespace_NamespaceImpl_nativeSetCachingPolicy
+func Java_io_v_impl_google_namespace_NamespaceImpl_nativeSetCachingPolicy(jenv *C.JNIEnv, jNamespaceClass C.jclass, goRef C.jlong, jDoCaching C.jboolean) C.jboolean {
+ n := *(*namespace.T)(jutil.GoRefValue(jutil.Ref(goRef)))
+ disable := naming.DisableCache(false)
+ if jDoCaching == C.JNI_FALSE {
+ disable = naming.DisableCache(true)
+ }
+ oldCtls := n.CacheCtl(disable)
+ var oldDisable naming.DisableCache
+ for _, ctl := range oldCtls {
+ switch value := ctl.(type) {
+ case naming.DisableCache:
+ oldDisable = value
+ }
+ }
+ if oldDisable {
+ return C.JNI_FALSE
+ }
+ return C.JNI_TRUE
+}
+
//export Java_io_v_impl_google_namespace_NamespaceImpl_nativeFlushCacheEntry
func Java_io_v_impl_google_namespace_NamespaceImpl_nativeFlushCacheEntry(jenv *C.JNIEnv, jNamespaceClass C.jclass, goRef C.jlong, jContext C.jobject, jName C.jstring) C.jboolean {
env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
@@ -319,6 +340,19 @@
}
}
+//export Java_io_v_impl_google_namespace_NamespaceImpl_nativeGetRoots
+func Java_io_v_impl_google_namespace_NamespaceImpl_nativeGetRoots(jenv *C.JNIEnv, jNamespaceClass C.jclass, goRef C.jlong) C.jobject {
+ env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
+ n := *(*namespace.T)(jutil.GoRefValue(jutil.Ref(goRef)))
+ roots := n.Roots()
+ jRoots, err := jutil.JStringList(env, roots)
+ if err != nil {
+ jutil.JThrowV(env, err)
+ return nil
+ }
+ return C.jobject(unsafe.Pointer(jRoots))
+}
+
func setPermissionsArgs(env jutil.Env, jContext, jPermissions C.jobject, jName, jVersion C.jstring, jOptions C.jobject) (context *context.T, permissions access.Permissions, name, version string, options []naming.NamespaceOpt, err error) {
context, _, err = jcontext.GoContext(env, jutil.Object(uintptr(unsafe.Pointer(jContext))))
if err != nil {
@@ -328,7 +362,7 @@
if err != nil {
return
}
- options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+ options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
name = jutil.GoString(env, jutil.Object(uintptr(unsafe.Pointer(jName))))
version = jutil.GoString(env, jutil.Object(uintptr(unsafe.Pointer(jVersion))))
return
@@ -354,7 +388,7 @@
if err != nil {
return
}
- options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+ options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
if err != nil {
return
}
diff --git a/impl/google/namespace/util.go b/impl/google/namespace/util.go
index 99a0876..7845613 100644
--- a/impl/google/namespace/util.go
+++ b/impl/google/namespace/util.go
@@ -11,6 +11,7 @@
"v.io/v23/naming"
"v.io/v23/options"
"v.io/v23/security"
+
jutil "v.io/x/jni/util"
)
@@ -32,35 +33,29 @@
return jNamespace, nil
}
-func javaToGoOptions(env jutil.Env, key string, jValue jutil.Object) (interface{}, error) {
- switch key {
- case "io.v.v23.SKIP_SERVER_ENDPOINT_AUTHORIZATION":
- value, err := jutil.CallBooleanMethod(env, jValue, "booleanValue", []jutil.Sign{})
- if err != nil {
- return nil, err
- }
- if value {
- // TODO(ashankar): The Java APIs need to reflect the
- // change in the Go APIs: any authorization policy can
- // be providfed as an option?
- return options.NameResolutionAuthorizer{security.AllowEveryone()}, nil
- }
- }
- // Otherwise we don't know what this option is, ignore it.
- return nil, jutil.SkipOption
-}
-
-func namespaceOptions(env jutil.Env, jOptions jutil.Object) ([]naming.NamespaceOpt, error) {
- opts, err := jutil.GoOptions(env, jOptions, javaToGoOptions)
+func goNamespaceOptions(env jutil.Env, jOptions jutil.Object) ([]naming.NamespaceOpt, error) {
+ var opts []naming.NamespaceOpt
+ r, err := jutil.GetBooleanOption(env, jOptions, "io.v.v23.naming.REPLACE_MOUNT")
if err != nil {
return nil, err
}
- var actualOpts []naming.NamespaceOpt
- for _, opt := range opts {
- switch opt := opt.(type) {
- case naming.NamespaceOpt:
- actualOpts = append(actualOpts, opt)
- }
+ opts = append(opts, naming.ReplaceMount(r))
+ s, err := jutil.GetBooleanOption(env, jOptions, "io.v.v23.naming.SERVES_MOUNT_TABLE")
+ if err != nil {
+ return nil, err
}
- return actualOpts, nil
+ opts = append(opts, naming.ServesMountTable(s))
+ l, err := jutil.GetBooleanOption(env, jOptions, "io.v.v23.naming.IS_LEAF")
+ if err != nil {
+ return nil, err
+ }
+ opts = append(opts, naming.IsLeaf(l))
+ e, err := jutil.GetBooleanOption(env, jOptions, "io.v.v23.SKIP_SERVER_ENDPOINT_AUTHORIZATION")
+ if err != nil {
+ return nil, err
+ }
+ if e {
+ opts = append(opts, options.NameResolutionAuthorizer{security.AllowEveryone()})
+ }
+ return opts, nil
}
diff --git a/impl/google/rpc/invoker.go b/impl/google/rpc/invoker.go
index 17cbae7..a0b4606 100644
--- a/impl/google/rpc/invoker.go
+++ b/impl/google/rpc/invoker.go
@@ -66,9 +66,8 @@
}
env, freeFunc = jutil.GetEnv()
defer freeFunc()
- jVomTagsLocal := jutil.NewLocalRef(env, jVomTags)
- jutil.DeleteGlobalRef(env, jVomTags)
- vomTags, err := jutil.GoByteArrayArray(env, jVomTagsLocal)
+ defer jutil.DeleteGlobalRef(env, jVomTags)
+ vomTags, err := jutil.GoByteArrayArray(env, jVomTags)
if err != nil {
return nil, nil, err
}
@@ -110,9 +109,8 @@
}
env, freeFunc = jutil.GetEnv()
defer freeFunc()
- jResultLocal := jutil.NewLocalRef(env, jResult)
- jutil.DeleteGlobalRef(env, jResult)
- vomResults, err := jutil.GoByteArrayArray(env, jResultLocal)
+ defer jutil.DeleteGlobalRef(env, jResult)
+ vomResults, err := jutil.GoByteArrayArray(env, jResult)
if err != nil {
return nil, err
}
@@ -140,6 +138,7 @@
}
env, freeFunc = jutil.GetEnv()
defer freeFunc()
+ defer jutil.DeleteGlobalRef(env, jInterfaces)
interfacesArr, err := jutil.GoObjectArray(env, jInterfaces)
if err != nil {
return nil, err
@@ -168,6 +167,7 @@
}
env, freeFunc = jutil.GetEnv()
defer freeFunc()
+ defer jutil.DeleteGlobalRef(env, jMethod)
var result signature.Method
err = jutil.GoVomCopy(env, jMethod, jMethodClass, &result)
if err != nil {
diff --git a/impl/google/rpc/jni.go b/impl/google/rpc/jni.go
index 868ed63..a27f405 100644
--- a/impl/google/rpc/jni.go
+++ b/impl/google/rpc/jni.go
@@ -220,6 +220,30 @@
return C.jobject(unsafe.Pointer(jStatus))
}
+//export Java_io_v_impl_google_rpc_ServerImpl_nativeAllPublished
+func Java_io_v_impl_google_rpc_ServerImpl_nativeAllPublished(jenv *C.JNIEnv, jServer C.jobject, goRef C.jlong, jContext C.jobject, jCallbackObj C.jobject) {
+ env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
+ server := *(*rpc.Server)(jutil.GoRefValue(jutil.Ref(goRef)))
+ jCallback := jutil.Object(uintptr(unsafe.Pointer(jCallbackObj)))
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
+ for {
+ status := server.Status()
+ done := true
+ for _, pub := range status.PublisherStatus {
+ if pub.LastState != pub.DesiredState {
+ done = false
+ break
+ }
+ }
+ if done {
+ break
+ }
+ <-status.Dirty
+ }
+ return jutil.NullObject, nil
+ })
+}
+
//export Java_io_v_impl_google_rpc_ServerImpl_nativeFinalize
func Java_io_v_impl_google_rpc_ServerImpl_nativeFinalize(jenv *C.JNIEnv, jServer C.jobject, goRef C.jlong) {
jutil.GoDecRef(jutil.Ref(goRef))
diff --git a/impl/google/rt/jni.go b/impl/google/rt/jni.go
index f6450fc..d6732f4 100644
--- a/impl/google/rt/jni.go
+++ b/impl/google/rt/jni.go
@@ -204,8 +204,8 @@
return C.jobject(unsafe.Pointer(jPrincipal))
}
-//export Java_io_v_impl_google_rt_VRuntimeImpl_nativeWithNamespace
-func Java_io_v_impl_google_rt_VRuntimeImpl_nativeWithNamespace(jenv *C.JNIEnv, jRuntime C.jclass, jContext C.jobject, jRoots C.jobjectArray) C.jobject {
+//export Java_io_v_impl_google_rt_VRuntimeImpl_nativeWithNewNamespace
+func Java_io_v_impl_google_rt_VRuntimeImpl_nativeWithNewNamespace(jenv *C.JNIEnv, jRuntime C.jclass, jContext C.jobject, jRoots C.jobjectArray) C.jobject {
env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
ctx, cancel, err := jcontext.GoContext(env, jutil.Object(uintptr(unsafe.Pointer(jContext))))
if err != nil {
diff --git a/jni_android.go b/jni_android.go
index c7273c0..15ac732 100644
--- a/jni_android.go
+++ b/jni_android.go
@@ -7,23 +7,48 @@
package jni
import (
+ "fmt"
"unsafe"
+ "v.io/v23/context"
+ "v.io/v23/namespace"
+ "v.io/v23/naming"
+
"v.io/x/lib/vlog"
- _ "v.io/x/ref/runtime/factories/android"
+ "v.io/x/ref/runtime/factories/android"
jdplugins "v.io/x/jni/impl/google/discovery/plugins"
jutil "v.io/x/jni/util"
+ jcontext "v.io/x/jni/v23/context"
)
// #include "jni.h"
import "C"
+var (
+ // Global reference for io.v.android.v23.V class.
+ jVClass jutil.Class
+)
+
+func Init(env jutil.Env) error {
+ var err error
+ jVClass, err = jutil.JFindClass(env, "io/v/android/v23/V")
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
//export Java_io_v_android_v23_V_nativeInitGlobalAndroid
-func Java_io_v_android_v23_V_nativeInitGlobalAndroid(jenv *C.JNIEnv, jVClass C.jclass, jOptions C.jobject) {
+func Java_io_v_android_v23_V_nativeInitGlobalAndroid(jenv *C.JNIEnv, _ C.jclass, jOptions C.jobject) {
env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
jOpts := jutil.Object(uintptr(unsafe.Pointer(jOptions)))
+ if err := Init(env); err != nil {
+ jutil.JThrowV(env, err)
+ return
+ }
+
// Setup logging.
_, _, level, vmodule, err := loggingOpts(env, jOpts)
if err != nil {
@@ -39,4 +64,29 @@
jutil.JThrowV(env, err)
return
}
+
+ // Setup namespace.
+ android.SetNamespaceFactory(func(ctx *context.T, ns namespace.T, _ ...string) (namespace.T, error) {
+ env, freeFunc := jutil.GetEnv()
+ defer freeFunc()
+ jContext, err := jcontext.JavaContext(env, ctx, nil)
+ if err != nil {
+ return nil, err
+ }
+ contextSign := jutil.ClassSign("io.v.v23.context.VContext")
+ wakeupMountRoot, err := jutil.CallStaticStringMethod(env, jVClass, "getWakeupMountRoot", []jutil.Sign{contextSign}, jContext)
+ if err != nil {
+ return nil, err
+ }
+ if wakeupMountRoot == "" {
+ return ns, nil
+ }
+ if !naming.Rooted(wakeupMountRoot) {
+ return nil, fmt.Errorf("wakeup mount root %s must be ... rooted.", wakeupMountRoot)
+ }
+ return &wakeupNamespace{
+ wakeupMountRoot: wakeupMountRoot,
+ ns: ns,
+ }, nil
+ })
}
diff --git a/util/opts.go b/util/opts.go
index 1566fc0..adb1775 100644
--- a/util/opts.go
+++ b/util/opts.go
@@ -7,6 +7,7 @@
package util
import (
+ "errors"
"fmt"
)
@@ -30,6 +31,16 @@
return CallObjectMethod(env, jOpts, "get", []Sign{StringSign}, ObjectSign, key)
}
+// SetOption associates the given value with the provided key in the provided options.
+func SetOption(env Env, jOpts Object, key string, jValue Object) error {
+ if jOpts.IsNull() {
+ return errors.New("Couldn't set option on a null options object.")
+ }
+ optionsSign := ClassSign("io.v.v23.Options")
+ _, err := CallObjectMethod(env, jOpts, "set", []Sign{StringSign, ObjectSign}, optionsSign, key, jValue)
+ return err
+}
+
// GetIntOption returns the integer option with the given key. It returns 0 if the option
// doesn't exist.
func GetIntOption(env Env, jOpts Object, key string) (int, error) {
@@ -46,6 +57,16 @@
return CallIntMethod(env, jVal, "intValue", nil)
}
+// SetIntOption associates the given integer value with the provided key
+// in the provided options.
+func SetIntOption(env Env, jOpts Object, key string, value int) error {
+ jValue, err := NewObject(env, jIntegerClass, []Sign{IntSign}, value)
+ if err != nil {
+ return err
+ }
+ return SetOption(env, jOpts, key, jValue)
+}
+
// GetBooleanOption returns the boolean option with the given key. It returns 'false' if the option
// doesn't exist.
func GetBooleanOption(env Env, jOpts Object, key string) (bool, error) {
@@ -62,6 +83,16 @@
return CallBooleanMethod(env, jVal, "booleanValue", nil)
}
+// SetBooleanOption associates the given boolean value with the provided key
+// in the provided options.
+func SetBooleanOption(env Env, jOpts Object, key string, value bool) error {
+ jValue, err := NewObject(env, jBooleanClass, []Sign{BoolSign}, value)
+ if err != nil {
+ return err
+ }
+ return SetOption(env, jOpts, key, jValue)
+}
+
// StringOption returns the string option with the given key. It returns an empty string if the
// option doesn't exist.
func GetStringOption(env Env, jOpts Object, key string) (string, error) {
@@ -77,3 +108,10 @@
}
return GoString(env, jVal), nil
}
+
+// SetStringOption associates the given string value with the provided key
+// in the provided options.
+func SetStringOption(env Env, jOpts Object, key string, value string) error {
+ jValue := JString(env, value)
+ return SetOption(env, jOpts, key, jValue)
+}
diff --git a/util/util.go b/util/util.go
index 71b51ec..70ae2f4 100644
--- a/util/util.go
+++ b/util/util.go
@@ -398,6 +398,16 @@
return ret, nil
}
+// JStringList converts the provided slice of Go strings into a Java
+// List<String>.
+func JStringList(env Env, strs []string) (Object, error) {
+ strArr := make([]Object, len(strs))
+ for i, str := range strs {
+ strArr[i] = JString(env, str)
+ }
+ return JObjectList(env, strArr, jStringClass)
+}
+
// JStringArray converts the provided slice of Go strings into a Java array of
// strings.
func JStringArray(env Env, strs []string) (Object, error) {
diff --git a/wakeup_android.go b/wakeup_android.go
new file mode 100644
index 0000000..6e7f9af
--- /dev/null
+++ b/wakeup_android.go
@@ -0,0 +1,131 @@
+// 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.
+
+// +build android
+
+package jni
+
+import (
+ "fmt"
+ "time"
+
+ "v.io/v23/context"
+ "v.io/v23/namespace"
+ "v.io/v23/naming"
+ "v.io/v23/security/access"
+)
+
+// wakeupNamespace is a namespace used by the persistent Android services
+// to mount themselves in a way that they can be woken up whenever a
+// client resolves their names.
+//
+// This namespace consists of two sub-parts:
+// 1) The original namespace (OrigNS),
+// 2) The mount root used for waking up the service (WakeMTRoot)
+//
+// Let's say the service wishes to mount itself under a relative name
+// 'a/b'. (Absolute names are disallowed.) The service first mounts its
+// endpoints under 'WakeMTRoot/server/a/b' and then mounts name
+// 'WakeMTRoot/client/a/b' under 'OrigNS/a/b'. When the client resolves
+// 'OrigNS/a/b', the name resolution will trigger resolution of
+// 'WakeMTRoot/client/a/b', which in turn triggers the process of waking
+// up the server.
+//
+// Note that we prefix mounted names by 'client' and 'server' above.
+// This is needed in order to distinguish the cases of a server mounting itself
+// into the mounttable (i.e., no wakeup needed) from the case of a client
+// resolving a name (i.e., wakeup needed).
+type wakeupNamespace struct {
+ wakeupMountRoot string
+ ns namespace.T
+}
+
+func (w *wakeupNamespace) Mount(ctx *context.T, name, server string, ttl time.Duration, opts ...naming.NamespaceOpt) error {
+ if naming.Rooted(name) {
+ return fmt.Errorf("Mount(%q): rooted names not allowed in wakeup namespace", name)
+ }
+ mountName := naming.Join(w.wakeupMountRoot, "server", name)
+ if err := w.ns.Mount(ctx, mountName, server, ttl, opts...); err != nil {
+ return err
+ }
+ // We set an infinite TTL at the original mount location as it allows us to wake up a
+ // server even after its mount entries have expired. (It's fine if the wakeup MT entries
+ // do expire.) Since entries are mounted forever, we explicitly have to clean up previous
+ // entries.
+ w.ns.Delete(ctx, name, false)
+ wakeupName := naming.Join(w.wakeupMountRoot, "client", name)
+ var zeroTTL time.Duration
+ return w.ns.Mount(ctx, name, wakeupName, zeroTTL, append(opts, naming.ServesMountTable(true))...)
+}
+
+func (w *wakeupNamespace) Unmount(ctx *context.T, name, server string, opts ...naming.NamespaceOpt) error {
+ if naming.Rooted(name) {
+ return fmt.Errorf("Unmount(%q): rooted names not allowed in wakeup namespace", name)
+ }
+ mountName := naming.Join(w.wakeupMountRoot, "server", name)
+ return w.ns.Unmount(ctx, mountName, server, opts...)
+ // We never unmount at the original location as it allows us to wake up a sleeping server.
+}
+
+func (w *wakeupNamespace) Delete(ctx *context.T, name string, deleteSubtree bool, opts ...naming.NamespaceOpt) error {
+ if naming.Rooted(name) {
+ return fmt.Errorf("Delete(%q): rooted names not allowed in wakeup namespace", name)
+ }
+ mountName := naming.Join(w.wakeupMountRoot, "server", name)
+ if err := w.ns.Delete(ctx, mountName, deleteSubtree, opts...); err != nil {
+ return err
+ }
+ return w.ns.Delete(ctx, name, deleteSubtree, opts...)
+}
+
+func (w *wakeupNamespace) Resolve(ctx *context.T, name string, opts ...naming.NamespaceOpt) (entry *naming.MountEntry, err error) {
+ return w.ns.Resolve(ctx, name, opts...)
+}
+
+func (w *wakeupNamespace) ShallowResolve(ctx *context.T, name string, opts ...naming.NamespaceOpt) (entry *naming.MountEntry, err error) {
+ return w.ns.ShallowResolve(ctx, name, opts...)
+}
+
+func (w *wakeupNamespace) ResolveToMountTable(ctx *context.T, name string, opts ...naming.NamespaceOpt) (entry *naming.MountEntry, err error) {
+ return w.ns.ResolveToMountTable(ctx, name, opts...)
+}
+
+func (w *wakeupNamespace) FlushCacheEntry(ctx *context.T, name string) bool {
+ if naming.Rooted(name) {
+ return false
+ }
+ mountName := naming.Join(w.wakeupMountRoot, "server", name)
+ return w.ns.FlushCacheEntry(ctx, mountName) || w.ns.FlushCacheEntry(ctx, name)
+}
+
+func (w *wakeupNamespace) CacheCtl(ctls ...naming.CacheCtl) []naming.CacheCtl {
+ return w.ns.CacheCtl(ctls...)
+}
+
+func (w *wakeupNamespace) Glob(ctx *context.T, pattern string, opts ...naming.NamespaceOpt) (<-chan naming.GlobReply, error) {
+ return w.ns.Glob(ctx, pattern, opts...)
+}
+
+func (w *wakeupNamespace) SetRoots(roots ...string) error {
+ return w.ns.SetRoots(roots...)
+}
+
+func (w *wakeupNamespace) Roots() []string {
+ return w.ns.Roots()
+}
+
+func (w *wakeupNamespace) SetPermissions(ctx *context.T, name string, perms access.Permissions, version string, opts ...naming.NamespaceOpt) error {
+ if naming.Rooted(name) {
+ return fmt.Errorf("SetPermissions(%q): rooted names not allowed in wakeup namespace", name)
+ }
+ mountName := naming.Join(w.wakeupMountRoot, "server", name)
+ if err := w.ns.SetPermissions(ctx, mountName, perms, version, opts...); err != nil {
+ return err
+ }
+ return w.ns.SetPermissions(ctx, name, perms, version, opts...)
+}
+
+func (w *wakeupNamespace) GetPermissions(ctx *context.T, name string, opts ...naming.NamespaceOpt) (perms access.Permissions, version string, err error) {
+ return w.ns.GetPermissions(ctx, name, opts...)
+}