PageRenderTime 1490ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/source/library/Interlace/ReactorUtilities/RemotingProtocol.cs

https://bitbucket.org/VahidN/interlace
C# | 338 lines | 228 code | 69 blank | 41 comment | 35 complexity | c212ce47b809830908c959e4a13ca3f2 MD5 | raw file
  1. #region Using Directives and Copyright Notice
  2. // Copyright (c) 2007-2010, Computer Consultancy Pty Ltd
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are met:
  7. // * Redistributions of source code must retain the above copyright
  8. // notice, this list of conditions and the following disclaimer.
  9. // * Redistributions in binary form must reproduce the above copyright
  10. // notice, this list of conditions and the following disclaimer in the
  11. // documentation and/or other materials provided with the distribution.
  12. // * Neither the name of the Computer Consultancy Pty Ltd nor the
  13. // names of its contributors may be used to endorse or promote products
  14. // derived from this software without specific prior written permission.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. // ARE DISCLAIMED. IN NO EVENT SHALL COMPUTER CONSULTANCY PTY LTD BE LIABLE
  20. // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  22. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  23. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  26. // DAMAGE.
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Reflection;
  30. using System.Text;
  31. using Interlace.Utilities;
  32. #endregion
  33. namespace Interlace.ReactorUtilities
  34. {
  35. public class RemotingProtocol : SerializeProtocol
  36. {
  37. // Objects implemented and offered by this side for the other side to call are "stubs":
  38. Dictionary<int, RemotingStub> _stubs;
  39. // Each stub is given a unique integer, with the root having a permanent one of "0":
  40. const int _rootStubId = 0;
  41. int _nextStubId = 1;
  42. // Objects existing on the remote side for us to call are "references". Only the root
  43. // references is cached; others must have one proxy for each time the object was
  44. // sent to us, otherwise the simplistic distributed garbage collection fails:
  45. object _remoteRootObjectProxy = null;
  46. Type _remoteRootObjectProxyType = null;
  47. // Each time we send a request it is given an asynchronous completion token or "cookie":
  48. int _nextCompletionCookie = 0;
  49. // Details of the request must be held (keyed by cookie number) until the response is received:
  50. Dictionary<int, RemotingRequestToken> _tokens;
  51. public RemotingProtocol()
  52. {
  53. _stubs = new Dictionary<int, RemotingStub>();
  54. _tokens = new Dictionary<int, RemotingRequestToken>();
  55. }
  56. RemotingStub CreateStub(Type type, object implementation)
  57. {
  58. RemotingStub stub = new RemotingStub(type, implementation, _nextStubId);
  59. _nextStubId++;
  60. _stubs[stub.StubId] = stub;
  61. return stub;
  62. }
  63. public void RegisterLocalRootObject<TInterface>(object implementation) where TInterface : class
  64. {
  65. if (_stubs.Count != 0) throw new InvalidOperationException();
  66. if (_stubs.ContainsKey(_rootStubId))
  67. {
  68. throw new InvalidOperationException(
  69. "A root object has already been registered; root objects can not be changed.");
  70. }
  71. if (implementation as TInterface == null)
  72. {
  73. throw new InvalidOperationException("The stub does not implement the specified interface.");
  74. }
  75. RemotingStub rootStub = new RemotingStub(typeof(TInterface), implementation, _rootStubId);
  76. _stubs[rootStub.StubId] = rootStub;
  77. }
  78. public TInterface GetRemoteRootObject<TInterface>() where TInterface : class
  79. {
  80. if (_remoteRootObjectProxy == null)
  81. {
  82. _remoteRootObjectProxy = Proxies.MakeProxy<TInterface>(
  83. new RemotingProxy(_rootStubId, typeof(TInterface), this, false));
  84. _remoteRootObjectProxyType = typeof(TInterface);
  85. }
  86. else
  87. {
  88. if (!_remoteRootObjectProxyType.Equals(typeof(TInterface)))
  89. {
  90. throw new InvalidOperationException(string.Format(
  91. "The remote root object has already been requested as a \"{0}\" type object. The " +
  92. "request for a \"{1}\" type object is therefore invalid.",
  93. _remoteRootObjectProxyType.Name, typeof(TInterface).Name));
  94. }
  95. }
  96. return _remoteRootObjectProxy as TInterface;
  97. }
  98. internal int GetNextCompletionCookieAndAdvance()
  99. {
  100. int cookie = _nextCompletionCookie;
  101. _nextCompletionCookie++;
  102. return cookie;
  103. }
  104. internal void SendRequest(RemotingRequestToken token)
  105. {
  106. _tokens.Add(token.Request.CompletionCookie, token);
  107. SendObject(token.Request);
  108. }
  109. protected override void HandleReceivedObject(object obj)
  110. {
  111. if (obj is RemotingRequest)
  112. {
  113. RemotingRequest request = obj as RemotingRequest;
  114. HandleReceivedRequest(request);
  115. }
  116. else if (obj is RemotingResponse)
  117. {
  118. RemotingResponse response = obj as RemotingResponse;
  119. HandleReceivedResponse(response);
  120. }
  121. else if (obj is RemotingExceptionResponse)
  122. {
  123. RemotingExceptionResponse exceptionResponse = obj as RemotingExceptionResponse;
  124. HandleReceivedExceptionResponse(exceptionResponse);
  125. }
  126. else if (obj is RemotingProtocolError)
  127. {
  128. }
  129. else
  130. {
  131. SendObject(new RemotingProtocolError(RemotingProtocolErrorKind.InvalidType));
  132. }
  133. }
  134. void HandleReceivedRequest(RemotingRequest request)
  135. {
  136. // Find the stub and the method to call on it:
  137. if (!_stubs.ContainsKey(request.StubId))
  138. {
  139. SendObject(new RemotingProtocolError(RemotingProtocolErrorKind.ReferenceNotFound));
  140. }
  141. RemotingStub stub = _stubs[request.StubId];
  142. if (stub.Type.Name != request.InterfaceName)
  143. {
  144. SendObject(new RemotingProtocolError(RemotingProtocolErrorKind.ReferenceInterfaceNotFound));
  145. }
  146. MethodInfo method = stub.Type.GetMethod(request.MethodName);
  147. if (method == null)
  148. {
  149. SendObject(new RemotingProtocolError(RemotingProtocolErrorKind.InterfaceMethodNotSupported));
  150. }
  151. // Check for proxied return values (interfaces that become new stubs):
  152. ObjectCallback callback;
  153. if (method.GetCustomAttributes(typeof(ProxiedReturnAttribute), true).Length > 0)
  154. {
  155. Type returnType = method.ReturnType;
  156. if (!Deferred.IsTypedDeferred(returnType))
  157. {
  158. throw new InvalidOperationException(
  159. "The type of a deferred returned from a proxied return method must be a typed deferred.");
  160. }
  161. Type interfaceType = Deferred.DeferredResultType(returnType);
  162. if (!interfaceType.IsInterface)
  163. {
  164. throw new InvalidOperationException(
  165. "The type of a deferred returned from a proxied return method must be a deferred returning an interface.");
  166. }
  167. // Create a stub with the resulting interface, and return a reference to the remote side:
  168. callback = delegate(object result)
  169. {
  170. object replacedResult = null;
  171. if (result != null)
  172. {
  173. RemotingStub newStub = CreateStub(interfaceType, result);
  174. replacedResult = new RemotingReference(newStub.StubId);
  175. }
  176. RemotingResponse response =
  177. new RemotingResponse(request.CompletionCookie, replacedResult);
  178. SendObject(response);
  179. return null;
  180. };
  181. }
  182. else
  183. {
  184. // Otherwise, just return the result:
  185. callback = delegate(object result)
  186. {
  187. RemotingResponse response = new RemotingResponse(request.CompletionCookie, result);
  188. SendObject(response);
  189. return null;
  190. };
  191. }
  192. try
  193. {
  194. // Invoke the stub method:
  195. DeferredObject deferred = method.Invoke(stub.Implementation, request.Arguments) as DeferredObject;
  196. deferred.ObjectCompletion(callback,
  197. delegate(DeferredFailure failure)
  198. {
  199. SendObject(new RemotingExceptionResponse(request.CompletionCookie, failure.Exception));
  200. return null;
  201. }, null);
  202. }
  203. catch (TargetInvocationException e)
  204. {
  205. SendObject(new RemotingExceptionResponse(request.CompletionCookie, e.InnerException));
  206. }
  207. catch (Exception e)
  208. {
  209. SendObject(new RemotingExceptionResponse(request.CompletionCookie, e));
  210. }
  211. }
  212. RemotingRequestToken HandleTokenLookupAndRemovalForResponse(int completionCookie)
  213. {
  214. // Check if the response is expected:
  215. if (!_tokens.ContainsKey(completionCookie))
  216. {
  217. SendObject(new RemotingProtocolError(RemotingProtocolErrorKind.CompletionTokenNotRecognised));
  218. return null;
  219. }
  220. // Retrieve and remove it form the expected response dictionary:
  221. RemotingRequestToken token = _tokens[completionCookie];
  222. _tokens.Remove(completionCookie);
  223. return token;
  224. }
  225. void HandleReceivedResponse(RemotingResponse response)
  226. {
  227. RemotingRequestToken token = HandleTokenLookupAndRemovalForResponse(response.CompletionCookie);
  228. if (token == null) return;
  229. object returnedResult = response.Result;
  230. // If a remoting reference was returned, replace it with a proxy:
  231. if (response.Result is RemotingReference)
  232. {
  233. if (!Deferred.IsTypedDeferred(token.ReturnType))
  234. {
  235. SendObject(new RemotingProtocolError(RemotingProtocolErrorKind.Unknown));
  236. return;
  237. }
  238. Type interfaceType = Deferred.DeferredResultType(token.ReturnType);
  239. if (!interfaceType.IsInterface)
  240. {
  241. SendObject(new RemotingProtocolError(RemotingProtocolErrorKind.Unknown));
  242. return;
  243. }
  244. RemotingReference reference = response.Result as RemotingReference;
  245. returnedResult = Proxies.MakeProxy(interfaceType,
  246. new RemotingProxy(reference.StubId, interfaceType, this, true));
  247. }
  248. // Complete the deferred:
  249. token.Deferred.SucceedObject(returnedResult);
  250. }
  251. void HandleReceivedExceptionResponse(RemotingExceptionResponse exceptionResponse)
  252. {
  253. RemotingRequestToken token = HandleTokenLookupAndRemovalForResponse(exceptionResponse.CompletionCookie);
  254. if (token == null) return;
  255. if (exceptionResponse.Exception != null)
  256. {
  257. token.Deferred.Fail(DeferredFailure.FromException(
  258. new RemotingException("An exception occurred accessing a remote service. The inner exception contains more details.",
  259. exceptionResponse.Exception)));
  260. }
  261. else
  262. {
  263. token.Deferred.Fail(DeferredFailure.FromException(
  264. new RemotingException("An exception occurred accessing a remote service, but the \"{0}\" exception could not be serialized.",
  265. exceptionResponse.Exception)));
  266. }
  267. }
  268. }
  269. }