ref/services/device: Introduce a DispatcherWrapper and use it to convert
the device manager to xrpc.

Change-Id: I57bc5778c07dd95c766d6b5a7b1a1b25df10a85a
diff --git a/lib/dispatcher/wrapped.go b/lib/dispatcher/wrapped.go
new file mode 100644
index 0000000..b8aaf19
--- /dev/null
+++ b/lib/dispatcher/wrapped.go
@@ -0,0 +1,42 @@
+// 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.
+
+package dispatcher
+
+import (
+	"v.io/v23/context"
+	"v.io/v23/rpc"
+	"v.io/v23/security"
+)
+
+// DispatcherWrapper is used when a dispatcher can't be constructed at server
+// creation time.  The most common use for this is when the dispatcher needs
+// to know some information about the server to be constructed.  For example
+// it is sometimes helpful to know the server's endpoints.
+// In such cases you can construct a DispatcherWrapper which will simply block
+// all lookups until the real dispatcher is set with SetDispatcher.
+type DispatcherWrapper struct {
+	wrapped      rpc.Dispatcher
+	wrappedIsSet chan struct{}
+}
+
+// Lookup will wait until SetDispatcher is called and then simply forward requests
+// to the underlying dispatcher.
+func (w *DispatcherWrapper) Lookup(ctx *context.T, suffix string) (interface{}, security.Authorizer, error) {
+	<-w.wrappedIsSet
+	return w.wrapped.Lookup(ctx, suffix)
+}
+
+// SetDispatcher sets the underlying dispatcher and allows Lookups to proceed.
+func (w *DispatcherWrapper) SetDispatcher(d rpc.Dispatcher) {
+	w.wrapped = d
+	close(w.wrappedIsSet)
+}
+
+// NewDispatcherWrapper creates a new DispatcherWrapper.
+func NewDispatcherWrapper() *DispatcherWrapper {
+	return &DispatcherWrapper{
+		wrappedIsSet: make(chan struct{}),
+	}
+}
diff --git a/services/device/claimable/main.go b/services/device/claimable/main.go
index 0fd53e4..1c92bbf 100644
--- a/services/device/claimable/main.go
+++ b/services/device/claimable/main.go
@@ -20,6 +20,7 @@
 	"v.io/x/ref/lib/security/securityflag"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/roaming"
 	"v.io/x/ref/services/device/internal/claim"
 	"v.io/x/ref/services/identity"
@@ -41,16 +42,10 @@
 		return errors.New("device is already claimed")
 	}
 
-	server, err := v23.NewServer(ctx)
+	server, err := xrpc.NewDispatchingServer(ctx, "", claimable)
 	if err != nil {
 		return err
 	}
-	if _, err := server.Listen(v23.GetListenSpec(ctx)); err != nil {
-		return err
-	}
-	if err := server.ServeDispatcher("", claimable); err != nil {
-		return err
-	}
 
 	status := server.Status()
 	ctx.Infof("Listening on: %v", status.Endpoints)
diff --git a/services/device/deviced/internal/starter/starter.go b/services/device/deviced/internal/starter/starter.go
index afd6e1e..f6cdcbf 100644
--- a/services/device/deviced/internal/starter/starter.go
+++ b/services/device/deviced/internal/starter/starter.go
@@ -20,6 +20,7 @@
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/v23/verror"
+	displib "v.io/x/ref/lib/dispatcher"
 	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/runtime/factories/roaming"
 	"v.io/x/ref/services/debug/debuglib"
@@ -331,21 +332,17 @@
 // (1) Function to be called to force the service to shutdown
 // (2) Any errors in starting the service (in which case, (1) will be nil)
 func startDeviceServer(ctx *context.T, args DeviceArgs, mt string, permStore *pathperms.PathStore) (shutdown func(), err error) {
-	server, err := v23.NewServer(ctx)
+	ctx = v23.WithListenSpec(ctx, args.ListenSpec)
+	wrapper := displib.NewDispatcherWrapper()
+	server, err := xrpc.NewDispatchingServer(ctx, args.name(mt), wrapper)
 	if err != nil {
 		return nil, err
 	}
-	shutdown = func() { server.Stop() }
-	endpoints, err := server.Listen(args.ListenSpec)
-	if err != nil {
-		shutdown()
-		return nil, err
-	}
-	args.ConfigState.Name = endpoints[0].Name()
+	args.ConfigState.Name = server.Status().Endpoints[0].Name()
 
 	dispatcher, dShutdown, err := impl.NewDispatcher(ctx, args.ConfigState, mt, args.TestMode, args.RestartCallback, permStore)
 	if err != nil {
-		shutdown()
+		server.Stop()
 		return nil, err
 	}
 
@@ -357,10 +354,7 @@
 		dShutdown()
 		ctx.Infof("Stopped device.")
 	}
-	if err := server.ServeDispatcher(args.name(mt), dispatcher); err != nil {
-		shutdown()
-		return nil, err
-	}
+	wrapper.SetDispatcher(dispatcher)
 	ctx.Infof("Device manager (%v) published as %v", args.ConfigState.Name, args.name(mt))
 	return shutdown, nil
 }