From 3ae573c17475021046f3d3b2b5f01de91c80aa1d Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Thu, 14 Aug 2008 20:35:31 +0100 Subject: Fleshed out service interfaces, and wrote the simpler service tests. Mocking tests still to be done. --- csharp/ProtocolBuffers.Test/ServiceTest.cs | 38 +++++++++++++- csharp/ProtocolBuffers/IRpcChannel.cs | 21 ++++++-- csharp/ProtocolBuffers/IRpcController.cs | 80 +++++++++++++++++++++++++++++- csharp/ProtocolBuffers/IService.cs | 59 +++++++++++++++++++++- 4 files changed, 188 insertions(+), 10 deletions(-) diff --git a/csharp/ProtocolBuffers.Test/ServiceTest.cs b/csharp/ProtocolBuffers.Test/ServiceTest.cs index 0f706a43..82da04ba 100644 --- a/csharp/ProtocolBuffers.Test/ServiceTest.cs +++ b/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 { + + /// + /// Tests for generated service classes. + /// TODO(jonskeet): Convert the mocking tests using Rhino.Mocks. + /// [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 done) { + throw new System.NotImplementedException(); + } + + public override void Bar(IRpcController controller, BarRequest request, Action done) { + throw new System.NotImplementedException(); + } + } } } diff --git a/csharp/ProtocolBuffers/IRpcChannel.cs b/csharp/ProtocolBuffers/IRpcChannel.cs index 96c82e16..28b321c6 100644 --- a/csharp/ProtocolBuffers/IRpcChannel.cs +++ b/csharp/ProtocolBuffers/IRpcChannel.cs @@ -1,14 +1,25 @@ using System; -using System.Collections.Generic; -using System.Text; using Google.ProtocolBuffers.Descriptors; namespace Google.ProtocolBuffers { /// - /// 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. /// public interface IRpcChannel { - void CallMethod(MethodDescriptor method, IRpcController controller, - IMessage request, IMessage responsePrototype, Action done); + /// + /// Calls the given method of the remote service. This method is similar + /// to 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 request.Descriptor == method.InputType. + /// The response passed to the callback will be of the same type as + /// (which must be such that + /// responsePrototype.Descriptor == method.OutputType). + /// + void CallMethod(MethodDescriptor method, IRpcController controller, + IMessage request, IMessage responsePrototype, Action done); } } diff --git a/csharp/ProtocolBuffers/IRpcController.cs b/csharp/ProtocolBuffers/IRpcController.cs index 81348fde..f4e29ad4 100644 --- a/csharp/ProtocolBuffers/IRpcController.cs +++ b/csharp/ProtocolBuffers/IRpcController.cs @@ -1,8 +1,84 @@ using System; -using System.Collections.Generic; -using System.Text; namespace Google.ProtocolBuffers { + /// + /// 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). + /// 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). + + /// + /// 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. + /// + void Reset(); + + /// + /// 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. + /// + bool Failed { get; } + + /// + /// If Failed is true, ErrorText returns a human-readable description of the error. + /// + string ErrorText { get; } + + /// + /// 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. + /// + 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). + + /// + /// Causes Failed to return true on the client side. + /// 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. + /// + void SetFailed(string reason); + + /// + /// 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. + /// + bool isCanceled(); + + /// + /// 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. + /// + /// + void NotifyOnCancel(Action callback); + #endregion } } diff --git a/csharp/ProtocolBuffers/IService.cs b/csharp/ProtocolBuffers/IService.cs index d53395aa..1405fbb9 100644 --- a/csharp/ProtocolBuffers/IService.cs +++ b/csharp/ProtocolBuffers/IService.cs @@ -1,9 +1,66 @@ using System; using System.Collections.Generic; using System.Text; +using Google.ProtocolBuffers.Descriptors; namespace Google.ProtocolBuffers { + /// + /// 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). + /// public interface IService { - // TODO(jonskeet): Fill this in + /// + /// The ServiceDescriptor describing this service and its methods. + /// + ServiceDescriptor DescriptorForType { get; } + + /// + /// 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. + /// + /// Preconditions + /// + /// method.Service == DescriptorForType + /// request is of the exact same class as the object returned by GetRequestPrototype(method) + /// 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. + /// + /// + /// + /// Postconditions + /// + /// will be called when the method is complete. + /// This may before CallMethod returns or it may be at some point in the future. + /// The parameter to is the response. It will be of the + /// exact same type as would be returned by . + /// If the RPC failed, the parameter to will be null. + /// Further details about the failure can be found by querying . + /// + /// + /// + void CallMethod(MethodDescriptor method, IRpcController controller, + IMessage request, Action done); + + /// + /// 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. + /// + IMessage GetRequestPrototype(MethodDescriptor method); + + /// + /// 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. + /// + IMessage GetResponsePrototype(MethodDescriptor method); } } -- cgit v1.2.3