浏览代码

Fleshed out service interfaces, and wrote the simpler service tests. Mocking tests still to be done.

Jon Skeet 17 年之前
父节点
当前提交
3ae573c174

+ 36 - 2
csharp/ProtocolBuffers.Test/ServiceTest.cs

@@ -1,10 +1,44 @@
 using System;
-using System.Collections.Generic;
-using System.Text;
+using Google.ProtocolBuffers.Descriptors;
+using Google.ProtocolBuffers.TestProtos;
 using NUnit.Framework;
 
 namespace Google.ProtocolBuffers {
+
+  /// <summary>
+  /// Tests for generated service classes.
+  /// TODO(jonskeet): Convert the mocking tests using Rhino.Mocks.
+  /// </summary>
   [TestFixture]
   public class ServiceTest {
+
+    private static readonly MethodDescriptor FooDescriptor = TestService.Descriptor.Methods[0];
+    private static readonly MethodDescriptor BarDescriptor = TestService.Descriptor.Methods[1];
+
+    [Test]
+    public void GetRequestPrototype() {
+      TestService mockService = new TestServiceImpl();
+
+      Assert.AreSame(mockService.GetRequestPrototype(FooDescriptor), FooRequest.DefaultInstance);
+      Assert.AreSame(mockService.GetRequestPrototype(BarDescriptor), BarRequest.DefaultInstance);
+    }
+
+    [Test]
+    public void GetResponsePrototype() {
+      TestService mockService = new TestServiceImpl();
+
+      Assert.AreSame(mockService.GetResponsePrototype(FooDescriptor), FooResponse.DefaultInstance);
+      Assert.AreSame(mockService.GetResponsePrototype(BarDescriptor), BarResponse.DefaultInstance);
+    }
+    
+    class TestServiceImpl : TestService {
+      public override void Foo(IRpcController controller, FooRequest request, Action<FooResponse> done) {
+        throw new System.NotImplementedException();
+      }
+
+      public override void Bar(IRpcController controller, BarRequest request, Action<BarResponse> done) {
+        throw new System.NotImplementedException();
+      }
+    }
   }
 }

+ 16 - 5
csharp/ProtocolBuffers/IRpcChannel.cs

@@ -1,14 +1,25 @@
 using System;
-using System.Collections.Generic;
-using System.Text;
 using Google.ProtocolBuffers.Descriptors;
 
 namespace Google.ProtocolBuffers {
   /// <summary>
-  /// TODO(jonskeet): Do this properly.
+  /// Interface for an RPC channel. A channel represents a communication line to
+  /// a service (IService implementation) which can be used to call that service's
+  /// methods. The service may be running on another machine. Normally, you should
+  /// not call an IRpcChannel directly, but instead construct a stub wrapping it.
+  /// Generated service classes contain a CreateStub method for precisely this purpose.
   /// </summary>
   public interface IRpcChannel {
-    void CallMethod<T>(MethodDescriptor method, IRpcController controller,
-        IMessage request, IMessage responsePrototype, Action<T> done);
+    /// <summary>
+    /// Calls the given method of the remote service. This method is similar
+    /// to <see cref="IService.CallMethod" /> with one important difference: the
+    /// caller decides the types of the IMessage objects, not the implementation.
+    /// The request may be of any type as long as <c>request.Descriptor == method.InputType</c>.
+    /// The response passed to the callback will be of the same type as
+    /// <paramref name="responsePrototype"/> (which must be such that
+    /// <c>responsePrototype.Descriptor == method.OutputType</c>).
+    /// </summary>
+    void CallMethod(MethodDescriptor method, IRpcController controller,
+        IMessage request, IMessage responsePrototype, Action<IMessage> done);
   }
 }

+ 78 - 2
csharp/ProtocolBuffers/IRpcController.cs

@@ -1,8 +1,84 @@
 using System;
-using System.Collections.Generic;
-using System.Text;
 
 namespace Google.ProtocolBuffers {
+  /// <summary>
+  /// Mediates a single method call. The primary purpose of the controller
+  /// is to provide a way to manipulate settings specific to the
+  /// RPC implementation and to find out about RPC-level errors.
+  /// 
+  /// The methods provided by this interface are intended to be a "least
+  /// common denominator" set of features which we expect all implementations to
+  /// support. Specific implementations may provide more advanced features,
+  /// (e.g. deadline propagation).
+  /// </summary>
   public interface IRpcController {
+
+    #region Client side calls
+    // These calls may be made from the client side only.  Their results
+    // are undefined on the server side (may throw exceptions).
+
+    /// <summary>
+    /// Resets the controller to its initial state so that it may be reused in
+    /// a new call.  This can be called from the client side only.  It must not
+    /// be called while an RPC is in progress.
+    /// </summary>
+    void Reset();
+
+    /// <summary>
+    /// After a call has finished, returns true if the call failed.  The possible
+    /// reasons for failure depend on the RPC implementation. Failed must
+    /// only be called on the client side, and must not be called before a call has
+    /// finished.
+    /// </summary>
+    bool Failed { get; }
+
+    /// <summary>
+    /// If Failed is true, ErrorText returns a human-readable description of the error.
+    /// </summary>
+    string ErrorText { get; }
+
+    /// <summary>
+    /// Advises the RPC system that the caller desires that the RPC call be
+    /// canceled. The RPC system may cancel it immediately, may wait awhile and
+    /// then cancel it, or may not even cancel the call at all. If the call is
+    /// canceled, the "done" callback will still be called and the RpcController
+    /// will indicate that the call failed at that time.
+    /// </summary>
+    void StartCancel();
+    #endregion
+
+    #region Server side calls
+    // These calls may be made from the server side only.  Their results
+    // are undefined on the client side (may throw exceptions).
+
+    /// <summary>
+    /// Causes Failed to return true on the client side. <paramref name="reason"/>
+    /// will be incorporated into the message returned by ErrorText.
+    /// If you find you need to return machine-readable information about
+    /// failures, you should incorporate it into your response protocol buffer
+    /// and should *not* call SetFailed.
+    /// </summary>
+    void SetFailed(string reason);
+
+    /// <summary>
+    /// If true, indicates that the client canceled the RPC, so the server may as
+    /// well give up on replying to it. This method must be called on the server
+    /// side only. The server should still call the final "done" callback.
+    /// </summary>
+    bool isCanceled();
+
+    /// <summary>
+    /// Requests that the given callback be called when the RPC is canceled.
+    /// The parameter passed to the callback will always be null. The callback will
+    /// be called exactly once. If the RPC completes without being canceled, the
+    /// callback will be called after completion. If the RPC has already been canceled
+    /// when NotifyOnCancel is called, the callback will be called immediately.
+    /// 
+    /// NotifyOnCancel must be called no more than once per request. It must be
+    /// called on the server side only.
+    /// </summary>
+    /// <param name="callback"></param>
+    void NotifyOnCancel(Action<object> callback);
+    #endregion
   }
 }

+ 58 - 1
csharp/ProtocolBuffers/IService.cs

@@ -1,9 +1,66 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
+using Google.ProtocolBuffers.Descriptors;
 
 namespace Google.ProtocolBuffers {
+  /// <summary>
+  /// Base interface for protocol-buffer-based RPC services. Services themselves
+  /// are abstract classes (implemented either by servers or as stubs) but they
+  /// implement this itnerface. The methods of this interface can be used to call
+  /// the methods of the service without knowing its exact type at compile time
+  /// (analagous to the IMessage interface).
+  /// </summary>
   public interface IService {
-    // TODO(jonskeet): Fill this in
+    /// <summary>
+    /// The ServiceDescriptor describing this service and its methods.
+    /// </summary>
+    ServiceDescriptor DescriptorForType { get; }
+
+    /// <summary>
+    /// Call a method of the service specified by MethodDescriptor.  This is
+    /// normally implemented as a simple switch that calls the standard
+    /// definitions of the service's methods.
+    /// <para>
+    /// Preconditions
+    /// <list>
+    /// <item><c>method.Service == DescriptorForType</c></item>
+    /// <item>request is of the exact same class as the object returned by GetRequestPrototype(method)</item>
+    /// <item>controller is of the correct type for the RPC implementation being used by this service.
+    /// For stubs, the "correct type" depends on the IRpcChannel which the stub is using. Server-side
+    /// implementations are expected to accept whatever type of IRpcController the server-side RPC implementation
+    /// uses.</item>
+    /// </list>
+    /// </para>
+    /// <para>
+    /// Postconditions
+    /// <list>
+    /// <item><paramref name="done" /> will be called when the method is complete.
+    /// This may before CallMethod returns or it may be at some point in the future.</item>
+    /// <item>The parameter to <paramref name="done"/> is the response. It will be of the
+    /// exact same type as would be returned by <see cref="GetResponsePrototype"/>.</item>
+    /// <item>If the RPC failed, the parameter to <paramref name="done"/> will be null.
+    /// Further details about the failure can be found by querying <paramref name="controller"/>.</item>
+    /// </list>
+    /// </para>
+    /// </summary>
+    void CallMethod(MethodDescriptor method, IRpcController controller,
+                    IMessage request, Action<IMessage> done);
+
+    /// <summary>
+    /// CallMethod requires that the request passed in is of a particular implementation
+    /// of IMessage. This method gets the default instance of this type of a given method.
+    /// You can then call WeakCreateBuilderForType to create a builder to build an object which
+    /// you can then pass to CallMethod.
+    /// </summary>
+    IMessage GetRequestPrototype(MethodDescriptor method);
+
+    /// <summary>
+    /// Like GetRequestPrototype, but returns a prototype of the response message.
+    /// This is generally not needed because the IService implementation contructs
+    /// the response message itself, but it may be useful in some cases to know ahead
+    /// of time what type of object will be returned.
+    /// </summary>
+    IMessage GetResponsePrototype(MethodDescriptor method);
   }
 }