v.io/x/ref: create group service dispatcher creation utility function

This is needed for creating Java groups servers.

MultiPart: 3/3
Change-Id: If391b03a4e36dfa1e95139138c65533faa80e02e
diff --git a/services/groups/groupsd/main.go b/services/groups/groupsd/main.go
index 401ef1c..ba6b33b 100644
--- a/services/groups/groupsd/main.go
+++ b/services/groups/groupsd/main.go
@@ -11,21 +11,14 @@
 
 import (
 	"fmt"
-	"strings"
 
 	"v.io/v23"
 	"v.io/v23/context"
-	"v.io/v23/conventions"
-	"v.io/v23/rpc"
-	"v.io/v23/security"
-	"v.io/v23/verror"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
 	_ "v.io/x/ref/runtime/factories/roaming"
-	"v.io/x/ref/services/groups/internal/server"
-	"v.io/x/ref/services/groups/internal/store/leveldb"
-	"v.io/x/ref/services/groups/internal/store/mem"
+	"v.io/x/ref/services/groups/lib"
 )
 
 var (
@@ -33,8 +26,6 @@
 	flagEngine  string
 	flagRootDir string
 	flagPersist string
-
-	errNotAuthorizedToCreate = verror.Register("v.io/x/ref/services/groups/groupsd.errNotAuthorizedToCreate", verror.NoRetry, "{1} {2} Creator user ids {3} are not authorized to create group {4}, group name must begin with one of the user ids")
 )
 
 func main() {
@@ -46,29 +37,6 @@
 	cmdline.Main(cmdGroupsD)
 }
 
-// Authorizer implementing the authorization policy for Create operations.
-//
-// A user is allowed to create any group that begins with the user id.
-//
-// TODO(ashankar): This is experimental use of the "conventions" API and of a
-// creation policy. This policy was thought of in a 5 minute period. Think
-// about this more!
-type createAuthorizer struct{}
-
-func (createAuthorizer) Authorize(ctx *context.T, call security.Call) error {
-	userids := conventions.GetClientUserIds(ctx, call)
-	for _, uid := range userids {
-		if strings.HasPrefix(call.Suffix(), uid+"/") {
-			return nil
-		}
-	}
-	// Revert to the default authorization policy.
-	if err := security.DefaultAuthorizer().Authorize(ctx, call); err == nil {
-		return nil
-	}
-	return verror.New(errNotAuthorizedToCreate, ctx, userids, call.Suffix())
-}
-
 var cmdGroupsD = &cmdline.Command{
 	Runner: v23cmd.RunnerFunc(runGroupsD),
 	Name:   "groupsd",
@@ -80,18 +48,9 @@
 }
 
 func runGroupsD(ctx *context.T, env *cmdline.Env, args []string) error {
-	var dispatcher rpc.Dispatcher
-	switch flagEngine {
-	case "leveldb":
-		store, err := leveldb.Open(flagRootDir)
-		if err != nil {
-			ctx.Fatalf("Open(%v) failed: %v", flagRootDir, err)
-		}
-		dispatcher = server.NewManager(store, createAuthorizer{})
-	case "memstore":
-		dispatcher = server.NewManager(mem.New(), createAuthorizer{})
-	default:
-		return fmt.Errorf("unknown storage engine %v", flagEngine)
+	dispatcher, err := lib.NewGroupsDispatcher(flagRootDir, flagEngine)
+	if err != nil {
+		return err
 	}
 	ctx, server, err := v23.WithNewDispatchingServer(ctx, flagName, dispatcher)
 	if err != nil {
diff --git a/services/groups/lib/dispatcher.go b/services/groups/lib/dispatcher.go
new file mode 100644
index 0000000..ce3f45b
--- /dev/null
+++ b/services/groups/lib/dispatcher.go
@@ -0,0 +1,67 @@
+// 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 lib
+
+import (
+	"fmt"
+	"strings"
+
+	"v.io/v23/rpc"
+	"v.io/x/ref/services/groups/internal/server"
+	"v.io/x/ref/services/groups/internal/store/leveldb"
+	"v.io/x/ref/services/groups/internal/store/mem"
+	"v.io/v23/context"
+	"v.io/v23/security"
+	"v.io/v23/conventions"
+	"v.io/v23/verror"
+)
+
+var (
+	errNotAuthorizedToCreate = verror.Register("v.io/x/ref/services/groups/groupsd.errNotAuthorizedToCreate", verror.NoRetry, "{1} {2} Creator user ids {3} are not authorized to create group {4}, group name must begin with one of the user ids")
+)
+
+// Authorizer implementing the authorization policy for Create operations.
+//
+// A user is allowed to create any group that begins with the user id.
+//
+// TODO(ashankar): This is experimental use of the "conventions" API and of a
+// creation policy. This policy was thought of in a 5 minute period. Think
+// about this more!
+type createAuthorizer struct{}
+
+func (createAuthorizer) Authorize(ctx *context.T, call security.Call) error {
+	userids := conventions.GetClientUserIds(ctx, call)
+	for _, uid := range userids {
+		if strings.HasPrefix(call.Suffix(), uid+"/") {
+			return nil
+		}
+	}
+	// Revert to the default authorization policy.
+	if err := security.DefaultAuthorizer().Authorize(ctx, call); err == nil {
+		return nil
+	}
+	return verror.New(errNotAuthorizedToCreate, ctx, userids, call.Suffix())
+}
+
+// NewGroupsDispatcher creates a new dispatcher for the groups service.
+//
+// rootDir is the directory for persisting groups.
+//
+// engine is the storage engine for groups.  Currently, only "leveldb" and
+// "memstore" are supported.
+func NewGroupsDispatcher(rootDir, engine string) (rpc.Dispatcher, error) {
+	switch engine {
+	case "leveldb":
+		store, err := leveldb.Open(rootDir)
+		if err != nil {
+			return nil, fmt.Errorf("Open(%v) failed: %v", rootDir, err)
+		}
+		return server.NewManager(store, createAuthorizer{}), nil
+	case "memstore":
+		return server.NewManager(mem.New(), createAuthorizer{}), nil
+	default:
+		return nil, fmt.Errorf("unknown storage engine %v", engine)
+	}
+}