diff options
author | Jon Skeet <skeet@pobox.com> | 2008-08-14 20:35:31 +0100 |
---|---|---|
committer | Jon Skeet <skeet@pobox.com> | 2008-08-14 20:35:31 +0100 |
commit | 3ae573c17475021046f3d3b2b5f01de91c80aa1d (patch) | |
tree | afbb78dc737e949a7aa0da2859d4589ca81ce7fb /csharp | |
parent | 1e42fdde2ebf34dddf1297bbecc56439ecef323f (diff) | |
download | protobuf-3ae573c17475021046f3d3b2b5f01de91c80aa1d.tar.gz protobuf-3ae573c17475021046f3d3b2b5f01de91c80aa1d.tar.bz2 protobuf-3ae573c17475021046f3d3b2b5f01de91c80aa1d.zip |
Fleshed out service interfaces, and wrote the simpler service tests. Mocking tests still to be done.
Diffstat (limited to 'csharp')
-rw-r--r-- | csharp/ProtocolBuffers.Test/ServiceTest.cs | 38 | ||||
-rw-r--r-- | csharp/ProtocolBuffers/IRpcChannel.cs | 21 | ||||
-rw-r--r-- | csharp/ProtocolBuffers/IRpcController.cs | 80 | ||||
-rw-r--r-- | 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 { + + /// <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(); + } + } } } 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 { /// <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); } } 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 { + /// <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 } } 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 { + /// <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); } } |