Merge "services/application/application: replace Put by PutX, add Profiles command"
diff --git a/runtime/factories/fake/rpc.go b/runtime/factories/fake/rpc.go
index 3ce9bcc..903348f 100644
--- a/runtime/factories/fake/rpc.go
+++ b/runtime/factories/fake/rpc.go
@@ -37,17 +37,24 @@
 
 func (r *Runtime) GetListenSpec(ctx *context.T) rpc.ListenSpec {
 	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
-	return rpc.ListenSpec{}
+	ls, _ := ctx.Value(listenSpecKey).(rpc.ListenSpec)
+	return ls
 }
 
 func (r *Runtime) WithListenSpec(ctx *context.T, ls rpc.ListenSpec) *context.T {
 	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	return context.WithValue(ctx, listenSpecKey, ls)
 	return ctx
 }
 
+func SetFlowManager(ctx *context.T, manager flow.Manager) *context.T {
+	return context.WithValue(ctx, flowManagerKey, manager)
+}
+
 func (r *Runtime) ExperimentalGetFlowManager(ctx *context.T) flow.Manager {
 	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
-	panic("unimplemented")
+	fm, _ := ctx.Value(flowManagerKey).(flow.Manager)
+	return fm
 }
 
 func (r *Runtime) ExperimentalWithNewFlowManager(ctx *context.T) (*context.T, flow.Manager, error) {
diff --git a/runtime/factories/fake/runtime.go b/runtime/factories/fake/runtime.go
index 8cb7fec..464ab11 100644
--- a/runtime/factories/fake/runtime.go
+++ b/runtime/factories/fake/runtime.go
@@ -23,6 +23,8 @@
 	principalKey
 	loggerKey
 	backgroundKey
+	listenSpecKey
+	flowManagerKey
 )
 
 type Runtime struct {
diff --git a/runtime/internal/rpc/x_test.go b/runtime/internal/rpc/x_test.go
new file mode 100644
index 0000000..4517ae0
--- /dev/null
+++ b/runtime/internal/rpc/x_test.go
@@ -0,0 +1,47 @@
+// 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 rpc
+
+import (
+	"testing"
+
+	"v.io/v23"
+	"v.io/v23/context"
+	"v.io/v23/naming"
+	"v.io/v23/rpc"
+	"v.io/v23/verror"
+	"v.io/x/ref/runtime/factories/fake"
+	"v.io/x/ref/runtime/internal/flow/manager"
+)
+
+type testService struct{}
+
+func (t *testService) Echo(ctx *context.T, call rpc.ServerCall, arg string) (string, error) {
+	return "response:" + arg, nil
+}
+
+func TestXClientServer(t *testing.T) {
+	ctx, shutdown := v23.Init()
+	defer shutdown()
+	ctx = fake.SetFlowManager(ctx, manager.New(ctx, naming.FixedRoutingID(0x1)))
+	ctx = v23.WithListenSpec(ctx, rpc.ListenSpec{
+		Addrs: rpc.ListenAddrs{{Protocol: "tcp", Address: "127.0.0.1:0"}},
+	})
+	_, err := NewServer(ctx, "server", &testService{}, nil, nil, "")
+	if err != nil {
+		t.Fatal(verror.DebugString(err))
+	}
+	client, err := NewXClient(ctx)
+	if err != nil {
+		t.Fatal(verror.DebugString(err))
+	}
+	var result string
+	if err = client.Call(ctx, "server", "Echo", []interface{}{"hello"}, []interface{}{&result}); err != nil {
+		t.Fatal(verror.DebugString(err))
+	}
+	if want := "response:hello"; result != want {
+		t.Errorf("got %q wanted %q", result, want)
+	}
+}
diff --git a/runtime/internal/rpc/xclient.go b/runtime/internal/rpc/xclient.go
index 1f5dcb5..1cd90dd 100644
--- a/runtime/internal/rpc/xclient.go
+++ b/runtime/internal/rpc/xclient.go
@@ -46,7 +46,7 @@
 
 var _ rpc.Client = (*xclient)(nil)
 
-func InternalNewXClient(ctx *context.T, opts ...rpc.ClientOpt) (rpc.Client, error) {
+func NewXClient(ctx *context.T, opts ...rpc.ClientOpt) (rpc.Client, error) {
 	c := &xclient{
 		flowMgr: v23.ExperimentalGetFlowManager(ctx),
 		ns:      v23.GetNamespace(ctx),
@@ -287,7 +287,7 @@
 		for _, r := range responses {
 			if r != nil {
 				numResponses++
-				if verror.ErrorID(r.serverErr.Err) == message.ErrWrongProtocol.ID {
+				if r.serverErr != nil && verror.ErrorID(r.serverErr.Err) == message.ErrWrongProtocol.ID {
 					return nil, verror.NoRetry, false, r.serverErr.Err
 				}
 			}
@@ -463,7 +463,9 @@
 	subErr.Name = "remote=" + fc.flow.Conn().RemoteEndpoint().String()
 	// TODO(toddw): cancel context instead?
 	if _, cerr := fc.flow.WriteMsgAndClose(); cerr != nil && err == nil {
-		return verror.New(verror.ErrInternal, fc.ctx, subErr)
+		// TODO(mattr): The context is often already canceled here, in
+		// which case we'll get an error.  Not clear what to do.
+		//return verror.New(verror.ErrInternal, fc.ctx, subErr)
 	}
 	if err == nil {
 		return nil
diff --git a/runtime/internal/rpc/xserver.go b/runtime/internal/rpc/xserver.go
index 6b98b09..8660a2a 100644
--- a/runtime/internal/rpc/xserver.go
+++ b/runtime/internal/rpc/xserver.go
@@ -74,7 +74,7 @@
 	stats *rpcStats // stats for this server.
 }
 
-func InternalNewXServer(ctx *context.T, settingsPublisher *pubsub.Publisher, settingsName string, opts ...rpc.ServerOpt) (rpc.XServer, error) {
+func NewServer(ctx *context.T, name string, object interface{}, authorizer security.Authorizer, settingsPublisher *pubsub.Publisher, settingsName string, opts ...rpc.ServerOpt) (rpc.XServer, error) {
 	ctx, cancel := context.WithRootCancel(ctx)
 	flowMgr := v23.ExperimentalGetFlowManager(ctx)
 	ns, principal := v23.GetNamespace(ctx), v23.GetPrincipal(ctx)
@@ -119,6 +119,14 @@
 	stats.NewStringFunc(blessingsStatsName, func() string {
 		return fmt.Sprintf("%s (default)", s.principal.BlessingStore().Default())
 	})
+	if err = s.listen(ctx, v23.GetListenSpec(ctx)); err != nil {
+		s.Stop()
+		return nil, err
+	}
+	if err = s.serve(name, object, authorizer); err != nil {
+		s.Stop()
+		return nil, err
+	}
 	return s, nil
 }
 
@@ -212,7 +220,6 @@
 func (s *xserver) listen(ctx *context.T, listenSpec rpc.ListenSpec) error {
 	s.Lock()
 	defer s.Unlock()
-
 	var lastErr error
 	for _, addr := range listenSpec.Addrs {
 		if len(addr.Address) > 0 {
@@ -281,7 +288,7 @@
 	}
 }
 
-func (s *server) serve(name string, obj interface{}, authorizer security.Authorizer) error {
+func (s *xserver) serve(name string, obj interface{}, authorizer security.Authorizer) error {
 	if obj == nil {
 		return verror.New(verror.ErrBadArg, s.ctx, "nil object")
 	}
@@ -293,7 +300,7 @@
 	s.Lock()
 	s.isLeaf = true
 	s.Unlock()
-	return s.ServeDispatcher(name, &leafDispatcher{invoker, authorizer})
+	return s.serveDispatcher(name, &leafDispatcher{invoker, authorizer})
 }
 
 func (s *xserver) serveDispatcher(name string, disp rpc.Dispatcher) error {
diff --git a/runtime/internal/rt/runtime.go b/runtime/internal/rt/runtime.go
index 37a7d0a..482736c 100644
--- a/runtime/internal/rt/runtime.go
+++ b/runtime/internal/rt/runtime.go
@@ -415,7 +415,7 @@
 		client, err = irpc.NewTransitionClient(ctx, sm, ns, otherOpts...)
 		deps = append(deps, fm, sm)
 	case fm != nil:
-		client, err = irpc.InternalNewXClient(ctx, otherOpts...)
+		client, err = irpc.NewXClient(ctx, otherOpts...)
 		deps = append(deps, fm)
 	case sm != nil:
 		client, err = irpc.InternalNewClient(sm, ns, otherOpts...)
diff --git a/services/syncbase/server/app.go b/services/syncbase/server/app.go
index 89469e3..e07094c 100644
--- a/services/syncbase/server/app.go
+++ b/services/syncbase/server/app.go
@@ -54,8 +54,8 @@
 	return a.s.createApp(ctx, call, a.name, perms)
 }
 
-func (a *app) Delete(ctx *context.T, call rpc.ServerCall) error {
-	return a.s.deleteApp(ctx, call, a.name)
+func (a *app) Destroy(ctx *context.T, call rpc.ServerCall) error {
+	return a.s.destroyApp(ctx, call, a.name)
 }
 
 func (a *app) Exists(ctx *context.T, call rpc.ServerCall) (bool, error) {
@@ -204,7 +204,7 @@
 	return nil
 }
 
-func (a *app) DeleteNoSQLDatabase(ctx *context.T, call rpc.ServerCall, dbName string) error {
+func (a *app) DestroyNoSQLDatabase(ctx *context.T, call rpc.ServerCall, dbName string) error {
 	if !a.exists {
 		vlog.Fatalf("app %q does not exist", a.name)
 	}
@@ -221,13 +221,13 @@
 	defer a.mu.Unlock()
 	d, ok := a.dbs[dbName]
 	if !ok {
-		return nil // delete is idempotent
+		return nil // destroy is idempotent
 	}
 
 	// 1. Check databaseData perms.
 	if err := d.CheckPermsInternal(ctx, call, d.St()); err != nil {
 		if verror.ErrorID(err) == verror.ErrNoExist.ID {
-			return nil // delete is idempotent
+			return nil // destroy is idempotent
 		}
 		return err
 	}
diff --git a/services/syncbase/server/interfaces/app.go b/services/syncbase/server/interfaces/app.go
index f9d0323..569fa0d 100644
--- a/services/syncbase/server/interfaces/app.go
+++ b/services/syncbase/server/interfaces/app.go
@@ -25,8 +25,8 @@
 	// CreateNoSQLDatabase creates the specified NoSQL database.
 	CreateNoSQLDatabase(ctx *context.T, call rpc.ServerCall, dbName string, perms access.Permissions, metadata *wire.SchemaMetadata) error
 
-	// DeleteNoSQLDatabase deletes the specified NoSQL database.
-	DeleteNoSQLDatabase(ctx *context.T, call rpc.ServerCall, dbName string) error
+	// DestroyNoSQLDatabase deletes the specified NoSQL database.
+	DestroyNoSQLDatabase(ctx *context.T, call rpc.ServerCall, dbName string) error
 
 	// SetDatabasePerms sets the perms for the specified database.
 	SetDatabasePerms(ctx *context.T, call rpc.ServerCall, dbName string, perms access.Permissions, version string) error
diff --git a/services/syncbase/server/interfaces/database.go b/services/syncbase/server/interfaces/database.go
index 01fa56f..6365b84 100644
--- a/services/syncbase/server/interfaces/database.go
+++ b/services/syncbase/server/interfaces/database.go
@@ -21,7 +21,7 @@
 
 	// CheckPermsInternal checks whether the given RPC (ctx, call) is allowed per
 	// the database perms.
-	// Designed for use from within App.DeleteNoSQLDatabase.
+	// Designed for use from within App.DestroyNoSQLDatabase.
 	CheckPermsInternal(ctx *context.T, call rpc.ServerCall, st store.StoreReader) error
 
 	// SetPermsInternal updates the database perms.
diff --git a/services/syncbase/server/mojo_impl.go b/services/syncbase/server/mojo_impl.go
index 27bd987..34c0f37 100644
--- a/services/syncbase/server/mojo_impl.go
+++ b/services/syncbase/server/mojo_impl.go
@@ -210,13 +210,13 @@
 	return toMojoError(err), nil
 }
 
-func (m *mojoImpl) AppDelete(name string) (mojom.Error, error) {
-	ctx, call := m.newCtxCall(name, methodDesc(wire.AppDesc, "Delete"))
+func (m *mojoImpl) AppDestroy(name string) (mojom.Error, error) {
+	ctx, call := m.newCtxCall(name, methodDesc(wire.AppDesc, "Destroy"))
 	stub, err := m.getApp(ctx, call, name)
 	if err != nil {
 		return toMojoError(err), nil
 	}
-	err = stub.Delete(ctx, call)
+	err = stub.Destroy(ctx, call)
 	return toMojoError(err), nil
 }
 
@@ -278,13 +278,13 @@
 	return toMojoError(err), nil
 }
 
-func (m *mojoImpl) DbDelete(name string) (mojom.Error, error) {
-	ctx, call := m.newCtxCall(name, methodDesc(nosqlwire.DatabaseDesc, "Delete"))
+func (m *mojoImpl) DbDestroy(name string) (mojom.Error, error) {
+	ctx, call := m.newCtxCall(name, methodDesc(nosqlwire.DatabaseDesc, "Destroy"))
 	stub, err := m.getDb(ctx, call, name)
 	if err != nil {
 		return toMojoError(err), nil
 	}
-	err = stub.Delete(ctx, call, NoSchema)
+	err = stub.Destroy(ctx, call, NoSchema)
 	return toMojoError(err), nil
 }
 
diff --git a/services/syncbase/server/nosql/database.go b/services/syncbase/server/nosql/database.go
index e006fb3..172eca5 100644
--- a/services/syncbase/server/nosql/database.go
+++ b/services/syncbase/server/nosql/database.go
@@ -142,14 +142,14 @@
 	return d.a.CreateNoSQLDatabase(ctx, call, d.name, perms, metadata)
 }
 
-func (d *databaseReq) Delete(ctx *context.T, call rpc.ServerCall, schemaVersion int32) error {
+func (d *databaseReq) Destroy(ctx *context.T, call rpc.ServerCall, schemaVersion int32) error {
 	if d.batchId != nil {
 		return wire.NewErrBoundToBatch(ctx)
 	}
 	if err := d.checkSchemaVersion(ctx, schemaVersion); err != nil {
 		return err
 	}
-	return d.a.DeleteNoSQLDatabase(ctx, call, d.name)
+	return d.a.DestroyNoSQLDatabase(ctx, call, d.name)
 }
 
 func (d *databaseReq) Exists(ctx *context.T, call rpc.ServerCall, schemaVersion int32) (bool, error) {
diff --git a/services/syncbase/server/service.go b/services/syncbase/server/service.go
index c920f48..6ca96f1 100644
--- a/services/syncbase/server/service.go
+++ b/services/syncbase/server/service.go
@@ -250,19 +250,19 @@
 	return nil
 }
 
-func (s *service) deleteApp(ctx *context.T, call rpc.ServerCall, appName string) error {
+func (s *service) destroyApp(ctx *context.T, call rpc.ServerCall, appName string) error {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	a, ok := s.apps[appName]
 	if !ok {
-		return nil // delete is idempotent
+		return nil // destroy is idempotent
 	}
 
 	if err := store.RunInTransaction(s.st, func(tx store.Transaction) error {
 		// Read-check-delete appData.
 		if err := util.GetWithAuth(ctx, call, tx, a.stKey(), &appData{}); err != nil {
 			if verror.ErrorID(err) == verror.ErrNoExist.ID {
-				return nil // delete is idempotent
+				return nil // destroy is idempotent
 			}
 			return err
 		}
diff --git a/services/syncbase/store/model.go b/services/syncbase/store/model.go
index be7265d..e0ee44f 100644
--- a/services/syncbase/store/model.go
+++ b/services/syncbase/store/model.go
@@ -37,15 +37,10 @@
 	Delete(key []byte) error
 }
 
-// storeReadWriter combines StoreReader and StoreWriter.
-type storeReadWriter interface {
-	StoreReader
-	StoreWriter
-}
-
 // Store is a CRUD-capable storage engine that supports transactions.
 type Store interface {
-	storeReadWriter
+	StoreReader
+	StoreWriter
 
 	// Close closes the store.
 	Close() error
diff --git a/services/syncbase/vsync/test_util.go b/services/syncbase/vsync/test_util.go
index d6b60c1..128db13 100644
--- a/services/syncbase/vsync/test_util.go
+++ b/services/syncbase/vsync/test_util.go
@@ -69,7 +69,7 @@
 	return verror.NewErrNotImplemented(ctx)
 }
 
-func (a *mockApp) DeleteNoSQLDatabase(ctx *context.T, call rpc.ServerCall, dbName string) error {
+func (a *mockApp) DestroyNoSQLDatabase(ctx *context.T, call rpc.ServerCall, dbName string) error {
 	return verror.NewErrNotImplemented(ctx)
 }