From f0c05aad1c5989ce51d6acba49024f2ef0dc8818 Mon Sep 17 00:00:00 2001 From: steforster Date: Tue, 4 Mar 2014 12:36:48 +0100 Subject: [PATCH] Catalog implemented. Port sharing implemented. Renamed trace to log. --- .gitignore | 1 - README.md | 127 ++++- {test/logs => logs}/dummy_log.txt | 0 src/Remact.Catalog/Catalog.cs | 6 +- src/Remact.Catalog/CatalogService.cs | 134 ++--- src/Remact.Catalog/Program.cs | 76 +-- .../Properties/Settings.Designer.cs | 2 +- src/Remact.Catalog/Properties/Settings.cs | 28 + .../Properties/Settings.settings | 6 +- src/Remact.Catalog/Remact.Catalog.Mono.csproj | 5 +- src/Remact.Net/ActorInput.cs | 13 +- src/Remact.Net/ActorMessage.cs | 29 +- src/Remact.Net/ActorOutput.cs | 24 +- src/Remact.Net/ActorPort.cs | 90 ++-- src/Remact.Net/Contracts/ActorInfo.cs | 29 +- src/Remact.Net/Contracts/ErrorMessage.cs | 69 +-- src/Remact.Net/Contracts/IRemactCatalog.cs | 21 +- src/Remact.Net/Contracts/IRemactService.cs | 22 +- src/Remact.Net/IActor.cs | 18 +- src/Remact.Net/IRemactConfig.cs | 21 +- .../Protocol/IRemactProtocolDriver.cs | 6 +- src/Remact.Net/Protocol/Wamp/WampClient.cs | 29 +- .../Protocol/Wamp/WampClientProxy.cs | 4 +- src/Remact.Net/RemactConfigDefault.cs | 45 +- .../Remote/MultithreadedServiceNet40.cs | 43 +- src/Remact.Net/Remote/RemactCatalogClient.cs | 379 ++++++------- src/Remact.Net/Remote/RemactClient.cs | 497 ++++++------------ src/Remact.Net/Remote/RemactService.cs | 111 ++-- src/Remact.Net/Remote/RemactServiceUser.cs | 16 - src/Remact.Net/Util/RaLog.cs | 92 ++-- src/Remact.Net/Util/RaLogPluginConsole.cs | 44 +- src/Remact.Net/Util/RaLogPluginFile.cs | 168 +++--- src/Remact.Net/Util/RemactApplication.cs | 25 +- test/BrowserTestClient/_startService.Mono.cmd | 2 +- test/ConsoleTestApps/Mono/Test1.Service.cs | 2 +- test/SpeedTestApp/Mono/_startTest2.cmd | 3 +- .../src/Client/FrmClient.Designer.cs | 2 +- test/SpeedTestApp/src/Client/FrmClient.cs | 5 +- test/SpeedTestApp/src/Service/FrmService.cs | 3 +- test/SpeedTestApp/src/Service/Test2Service.cs | 3 +- 40 files changed, 1035 insertions(+), 1165 deletions(-) rename {test/logs => logs}/dummy_log.txt (100%) create mode 100644 src/Remact.Catalog/Properties/Settings.cs diff --git a/.gitignore b/.gitignore index 3b8eef1..1abfafa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ *~ [Bb]in/ [Oo]bj/ -[Ll]ogs/ *.suo *.user diff --git a/README.md b/README.md index 2bb3f03..e19da0e 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,28 @@ Remote Actors for .NET ====================== -**Remact.Net** facilitates the development of [actors](http://en.wikipedia.org/wiki/Actor_model) in .NET languages. +**Remact.Net** facilitates the development of remote actors in .NET languages. It is a class library written in C-Sharp. It runs in Microsoft .NET and Linux-Mono environments. +The [Actor model](http://en.wikipedia.org/wiki/Actor_model) is inspired by physics and by nature. +It brings order to multithread, multicore, multihost systems. +These days many systems have such requirements. But industrial control systems with dynamically connected +and movable intelligent subsystems have been in focus of the design of Remact.Net. +Therefore, Remact.Net supports many instances of the same application running on one or on distributed hosts. +It also supports dynamic discovery of actors and does not require configuration of host names and TCP ports. + +In spite of all these features, Remact.Net is remarkably slim and easy to adapt for special needs. + + **Project status** Remact.Net is work in progress. Some integration test are running. [AsyncWcfLib](http://sourceforge.net/projects/asyncwcflib/) is the predecessor of Remact.Net. -The motivation for this new project is improved support for bidirectional communication, -higher performance on Linux/Mono based environments and wider interoperability -so that actors written in Java or JavaScript (browser based actors) can participate. +The motivation for this new project is to improve support for bidirectional communication, +higher performance on Linux/Mono based environments and interoperability +with actors written in Java or JavaScript (browser based actors). @@ -24,7 +34,7 @@ The following goals have been reached: - [*] Local actors (message passing between threads) - [*] Remote actors (message passing between hosts or processes) - [*] WebSockets, Json and other open standards are used to link Remact actors. -- [*] High throughput on Linux and Windows: More than 5000 request/respose pairs per second. +- [*] High throughput on Linux and Windows: More than 5000 request/respose pairs per second between processes. @@ -46,7 +56,7 @@ For other languages there are libraries to help programming remote actors: Dependencies and standards -------------------------- Remact.Net is built on open standards and uses open source components. -I would like to thank all participants for their contribution to the open source community. +I would like to thank all who built these bits for their contribution to the open source community. * [WebSocket](http://tools.ietf.org/html/rfc6455), the IETF standard RFC6455 @@ -55,6 +65,8 @@ I would like to thank all participants for their contribution to the open source * [WAMP](http://wamp.ws/), the WebSocket Application Messaging Protocol +* [JSON-RPC](http://www.jsonrpc.org/specification), instad of WAMP, Json-RPC can be used on top of the WebSocket protocol. + * [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json), a class library for Json serialization * [Log4Net](http://logging.apache.org/log4net/), logging component from Apache @@ -71,12 +83,11 @@ Documentation ------------- Currently, [the main conceptual ideas](http://sourceforge.net/p/asyncwcflib/wiki/Actors/) should be read on the AsyncWcfLib pages. -The folder **src/Remact.Net/Contracts** contains remotely callable methods and their corresponding request- and response messages. -These definitions and their XML comments form the basic interface definition of actors. +The folder **src/Remact.Net/Contracts** and **test/SpeedTestApp/src/Contracts** contains interfaces for remotely callable methods and their +corresponding request- and response messages. These definitions and their XML comments form the basic interface definition of actors. -Application actors will add more contracts in other assemblies. -These definitions must be present on both sides of the communication channel. -For actors not written in a .NET programming language (e.g. Java Script), the interface definition must be translated. +These contracts must be present on both sides of the communication channel. +For actors not written in a .NET programming language (e.g. Java Script), the interface contract must be translated. Receiving of WAMP messages is done in five steps: * Deserialization to a [Newtonsoft.Json.Linq.JToken](http://weblog.west-wind.com/posts/2012/Aug/30/Using-JSONNET-for-dynamic-JSON-parsing) @@ -86,9 +97,95 @@ Receiving of WAMP messages is done in five steps: * Optional dispatching to a method having the matching parameter type +Conceptual parts +---------------- + +**Actors** + +An actor is a group of objects that are accessed by one thread. Data inside an actor is consistant. +An actor may feature several input and output ports. These ports are connected to other actors on the same or on a remote process. +The actor contains one message queue. All incoming messages pass this queue (incoming requests, incoming responses). + + +**Outputs** + +An output port is connected to an input port of another actor. +Messages are sent by the output to the connected input. Sending is a non blocking operation. +Optionally the remote actor may send a reply message. It is handled as an asynchronous callback event +in a lambda expression or in an async method. +The remote actor may send notification messages to an output. It is handled in a method defined by the contract interface. +Disconnecting the output is signaled to the input. A disconnect signal may also be issued by the input. + + +**Inputs** + +Many remote outputs may be connected to one input port. +Once connected, messages may be sent from the output to the input but also from the input to the output. +Address and version information for each connected output is available from the input server. +Additional session data may be kept for each connected actor output. +Periodic messages should be exchanged by the actors to check the communication channels. + + +**ActorMessage and Payload** + +The ActorMessage class addresses source and destination port. It is used to route the payload data through the system. +The payload may be of any serializable object type. +Serialization is done by Newtonsoft.Json. Therefore, attributes like [JsonProperty] and [JsonIgnore] may be used to +control the serialization process. By default all public properties and fields are serialized. + + +**Methods** + +Messages are sent to the method that handles the message payload type as a single input parameter. +The method also defines the reply payload as the return type. +A void method will normally not reply a message. +In case of error or exception, methods will reply an ErrorMessage. + -How to build ------------- +**Contract interface** + +A contract interface defines the set of methods and the corresponding request and response message types that are available +on a certain input or output port. +On the receiving side the methods will have the specified, single message payload parameter and additional parameters +for message source identification and session data. + + + +Communication models +-------------------- + +**Client / Service** + +One actor may contain several clients and services. +The output (client) sends a request to the input (service) of another actor and will get the reply from it. + + +**Notifications** + +The input port may send a callback notification to the output port. +The output may send a notification to the input port. +Notifications are defined as parameter of a void method of the receiving port contract interface. + + +**Service / Client** + +Communication is symmetrical. Therefore inputs may also send requests to a method of the connected output +and get a response from it. The difference between input and output lies in the 1 : many relationship +and in the active part of the output during connection buildup. +Some day we hopefully will find a better name for the two connected communication port types. + + +**Publish / Subscribe** + +An input port can send messages to all its connected output ports. +Connecting to such an input in fact means - subscribing to its publications. +The publisher knows who received its publications because all communication is done through reliable +TCP connections to known partners. + + + +How to build and test Remact.Net +-------------------------------- The Remact.Mono solution is used for VisualStudio 2012 and for MonoDevelop 2.8.6.3. Source projects of other git repositories are referenced from Remact.Mono.sln. You have to clone all these (small) repos to be able to build Remact: @@ -102,6 +199,10 @@ Afterwards your project folder should look like this: $ ls Alchemy-Websockets Newtonsoft.Json Remact.Net ... +Newtonsoft.Json needs some small adaptions to run under Mono. I copied "Newtonsoft.Json.Net40.csproj" to +"Newtonsoft.Json.Mono.csproj" and switched the target framework to 4.0 (not client profile). + +Then you should be able to compile in VS2010 or VS2012 the "Remact.Mono.sln" (Release) and start "test\SpeedTestApp\Mono\_startTest2.cmd". License diff --git a/test/logs/dummy_log.txt b/logs/dummy_log.txt similarity index 100% rename from test/logs/dummy_log.txt rename to logs/dummy_log.txt diff --git a/src/Remact.Catalog/Catalog.cs b/src/Remact.Catalog/Catalog.cs index 14e50d4..e33285f 100644 --- a/src/Remact.Catalog/Catalog.cs +++ b/src/Remact.Catalog/Catalog.cs @@ -11,6 +11,7 @@ using Remact.Net; using Remact.Net.Remote; using Remact.Net.Protocol; +using Remact.Net.Contracts; namespace Remact.Catalog { @@ -293,7 +294,8 @@ private void Open() m_CatalogService = new CatalogService(); // Open the service - m_RemactService = new ActorInput(RemactConfigDefault.Instance.CatalogServiceName, m_CatalogService.OnRequest); + m_RemactService = new ActorInput(RemactConfigDefault.Instance.CatalogServiceName, m_CatalogService.OnUnknownRequest); + m_RemactService.Dispatcher.AddActorInterface(typeof(IRemactCatalog), m_CatalogService); m_RemactService.OnInputConnected += m_CatalogService.OnClientConnectedOrDisconnected; m_RemactService.OnInputDisconnected += m_CatalogService.OnClientConnectedOrDisconnected; m_RemactService.LinkInputToNetwork( null, RemactConfigDefault.Instance.CatalogPort, publishToCatalog: false, serviceConfig: this ); // calls our DoServiceConfiguration @@ -306,7 +308,7 @@ private void Open() if (host != null && host.Trim().Length > 0) { var output = new ActorOutput("Clt>"+host, OnResponseFromPeerCatalog); - output.LinkOutputToRemoteService(new Uri("http://" + host + ':' + RemactConfigDefault.Instance.CatalogPort + output.LinkOutputToRemoteService(new Uri("ws://" + host + ':' + RemactConfigDefault.Instance.CatalogPort + "/" + RemactConfigDefault.WsNamespace + "/" + RemactConfigDefault.Instance.CatalogServiceName),// no catalog lookup as uri is given. this ); // calls our DoClientConfiguration output.OutputContext = new SvcDat(); diff --git a/src/Remact.Catalog/CatalogService.cs b/src/Remact.Catalog/CatalogService.cs index 5f2efab..f105fa2 100644 --- a/src/Remact.Catalog/CatalogService.cs +++ b/src/Remact.Catalog/CatalogService.cs @@ -17,118 +17,64 @@ namespace Remact.Catalog /// class CatalogService { - //---------------------------------------------------------------------------------------------- - #region Public Methods - - public void OnClientConnectedOrDisconnected (ActorMessage id) { Program.Catalog.SvcRegisterChanged = true; } - public void OnRequest (ActorMessage id) + public void OnUnknownRequest (ActorMessage msg) { - ActorInfo service = id.Payload as ActorInfo; - bool ok = false; - if (service != null && service.IsServiceName) - { - switch (service.Usage) - { - case ActorInfo.Use.ServiceEnableRequest: ok = RegisterService(service, id); break; - case ActorInfo.Use.ServiceDisableRequest: ok = RegisterService(service, id); break; - case ActorInfo.Use.ServiceAddressRequest: ok = GetAddress(service, id); break; - default: break;// continue below - } - } - - ActorInfoList list = id.Payload as ActorInfoList; - if (list != null) - { - ok = RegisterList(list, id); - } - - - if (!ok) - { - RaLog.Warning(id.SvcRcvId, "Unknown request or no service: " + id.Payload.ToString()); - id.SendResponse(new ErrorMessage(ErrorMessage.Code.AppRequestNotAcceptedByService, "Remact.CatalogService")); - } + RaLog.Warning(msg.SvcRcvId, "Unknown request or no service: " + msg.Payload.ToString()); + msg.SendResponse(new ErrorMessage(ErrorMessage.Code.AppRequestNotAcceptedByService, "Remact.CatalogService")); } - - #endregion - //---------------------------------------------------------------------------------------------- - #region Private Methods - - // A single service entry is beeing enabled, disabled or updated - // return the service info as response - private bool RegisterService (ActorInfo req, ActorMessage id) + // service request method implements IRemactCatalog + private ReadyMessage InputIsOpen(ActorInfo actorInput, ActorMessage msg) { - ActorInfo response = req; - if (Program.Catalog.RegisterService (req, id.SvcRcvId)) - { - // req is used in the SvcRegister now. We have to create a copy - response = new ActorInfo(req); - } - - // reply the registered service - if (req.Usage == ActorInfo.Use.ServiceEnableRequest) - { - response.Usage = ActorInfo.Use.ServiceEnableResponse; - } - else - { - response.Usage = ActorInfo.Use.ServiceDisableResponse; - } - - id.SendResponse (response); - return true; + Program.Catalog.RegisterService(actorInput, msg.SvcRcvId); + return new ReadyMessage(); } + // service request method implements IRemactCatalog + ReadyMessage InputIsClosed(ActorInfo actorInput, ActorMessage msg) + { + Program.Catalog.RegisterService(actorInput, msg.SvcRcvId); + return new ReadyMessage(); + } - // A list of service entries is beeing enabled, disabled or updated - // return our list as response, to synchronize the peer catalog - private bool RegisterList (ActorInfoList list, ActorMessage id) + // service request method implements IRemactCatalog + ActorInfo LookupInput(string actorInputName, ActorMessage msg) { - RaLog.Info( id.SvcRcvId, "PeerRtr sends list containing " + list.Item.Count + " services." ); - foreach( ActorInfo s in list.Item ) + ActorInfo found = null; + foreach (ActorInfo s in Program.Catalog.SvcRegister.Item) + { + if (s.Name == actorInputName && s.IsServiceName) + { + found = new ActorInfo(s); // create a copy in order not to change the SvcRegister + found.Usage = ActorInfo.Use.ServiceAddressResponse; + break; + } + } + + if (found == null) { - Program.Catalog.RegisterService (s, id.SvcRcvId); + msg.SendResponse(new ErrorMessage(ErrorMessage.Code.AppDataNotAvailableInService, + "Service name = '" + actorInputName + "' not registered in '" + Program.Catalog.Service.Uri + "'")); } - id.SendResponse (Program.Catalog.SvcRegister); - return true; + + return found; } - - // GetAddress: Search URI (with TCP port number) of a registered service - private bool GetAddress (ActorInfo search, ActorMessage id) + // service request method implements IRemactCatalog + ActorInfoList SynchronizeCatalog(ActorInfoList serviceList, ActorMessage msg) { - bool found = false; - foreach (ActorInfo s in Program.Catalog.SvcRegister.Item) - { - if (s.Name == search.Name - && s.IsServiceName == search.IsServiceName) + RaLog.Info(msg.SvcRcvId, "Peer catalog sends list containing " + serviceList.Item.Count + " services."); + foreach (ActorInfo s in serviceList.Item) { - search = new ActorInfo (s); // create a copy in order not to change the SvcRegister - search.Usage = ActorInfo.Use.ServiceAddressResponse; - found = true; - break; + Program.Catalog.RegisterService(s, msg.SvcRcvId); } - } - if (!found) - { - id.SendResponse (new ErrorMessage (ErrorMessage.Code.AppDataNotAvailableInService, - "Service name = '" + search.Name + "' not registered in '" + Program.Catalog.Service.Uri + "'")); - } - else - { - id.SendResponse (search); - } - return true; - }// GetAddress - - #endregion - - }// class CatalogService -}// namespace \ No newline at end of file + return Program.Catalog.SvcRegister; + } + } +} \ No newline at end of file diff --git a/src/Remact.Catalog/Program.cs b/src/Remact.Catalog/Program.cs index 01a4147..385e966 100644 --- a/src/Remact.Catalog/Program.cs +++ b/src/Remact.Catalog/Program.cs @@ -10,42 +10,44 @@ namespace Remact.Catalog { - static class Program - { - public static Catalog Catalog { get; private set; } - - /// - /// The main entry point for the Remact.Catalog application. - /// - [STAThread] - static void Main (string[] args) + static class Program { - int appInstance = 1; - try - { - if (args.Length > 0) appInstance = Convert.ToInt32 (args[0]); // by default the first commandline argument - } - catch { } - RaLog.UsePlugin (new RaLog.PluginFile ()); - RaLog.Start (appInstance); - RemactApplication.InstallExitHandler (); - RaLog.Run (); // open file and write first messages - RaLog.Info( "Catalog", "Start" ); - try - { - Catalog = new Catalog(); - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault (false); - Application.Run (new FrmCatalog()); - Catalog.Dispose(); - } - catch (Exception ex) // any Exception - { - RaLog.Exception("Catalog: Fatal error", ex); - Catalog.Dispose(); - } - RaLog.Info( "Catalog", "Stop" ); - RaLog.Stop(); - }// Main - } + public static Catalog Catalog { get; private set; } + + /// + /// The main entry point for the Remact.Catalog application. + /// + [STAThread] + static void Main (string[] args) + { + int appInstance = 1; + try + { + if (args.Length > 0) appInstance = Convert.ToInt32 (args[0]); // by default the first commandline argument + } + catch { } + RaLog.UsePlugin (new RaLog.PluginFile ()); + RaLog.Start (appInstance); + RemactApplication.InstallExitHandler (); + RaLog.Run (); // open file and write first messages + RaLog.Info( "Catalog", "Start" ); + try + { + Catalog = new Catalog(); + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault (false); + Application.Run (new FrmCatalog()); + Catalog.Dispose(); + } + catch (Exception ex) // any Exception + { + RaLog.Exception("Catalog: Fatal error", ex); + Catalog.Dispose(); + } + + RemactConfigDefault.Instance.Shutdown(); + RaLog.Info("Catalog", "Stop"); + RaLog.Stop(); + }// Main + } } diff --git a/src/Remact.Catalog/Properties/Settings.Designer.cs b/src/Remact.Catalog/Properties/Settings.Designer.cs index a86f8af..1e5704b 100644 --- a/src/Remact.Catalog/Properties/Settings.Designer.cs +++ b/src/Remact.Catalog/Properties/Settings.Designer.cs @@ -27,7 +27,7 @@ internal sealed partial class Settings : global::System.Configuration.Applicatio [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("\r\n\r\n Test1\r\n Test2\r\n")] + "tring>host123\r\n host345\r\n")] public global::System.Collections.Specialized.StringCollection PeerHosts { get { return ((global::System.Collections.Specialized.StringCollection)(this["PeerHosts"])); diff --git a/src/Remact.Catalog/Properties/Settings.cs b/src/Remact.Catalog/Properties/Settings.cs new file mode 100644 index 0000000..29b95f3 --- /dev/null +++ b/src/Remact.Catalog/Properties/Settings.cs @@ -0,0 +1,28 @@ +namespace Remact.Catalog.Properties { + + + // This class allows you to handle specific events on the settings class: + // The SettingChanging event is raised before a setting's value is changed. + // The PropertyChanged event is raised after a setting's value is changed. + // The SettingsLoaded event is raised after the setting values are loaded. + // The SettingsSaving event is raised before the setting values are saved. + internal sealed partial class Settings { + + public Settings() { + // // To add event handlers for saving and changing settings, uncomment the lines below: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + // this.SettingsSaving += this.SettingsSavingEventHandler; + // + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Add code to handle the SettingChangingEvent event here. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Add code to handle the SettingsSaving event here. + } + } +} diff --git a/src/Remact.Catalog/Properties/Settings.settings b/src/Remact.Catalog/Properties/Settings.settings index 5b15e16..f40e81a 100644 --- a/src/Remact.Catalog/Properties/Settings.settings +++ b/src/Remact.Catalog/Properties/Settings.settings @@ -1,12 +1,12 @@  - + <?xml version="1.0" encoding="utf-16"?> <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - <string>Test1</string> - <string>Test2</string> + <string>host123</string> + <string>host345</string> </ArrayOfString> diff --git a/src/Remact.Catalog/Remact.Catalog.Mono.csproj b/src/Remact.Catalog/Remact.Catalog.Mono.csproj index 61960a4..777c587 100644 --- a/src/Remact.Catalog/Remact.Catalog.Mono.csproj +++ b/src/Remact.Catalog/Remact.Catalog.Mono.csproj @@ -11,6 +11,7 @@ Remact.Catalog Remact.Catalog 512 + false publish\ true Disk @@ -23,7 +24,6 @@ true 0 1.0.0.%2a - false false true @@ -46,6 +46,7 @@ prompt 4 bin\Release.Mono\Remact.Catalog.xml + false @@ -55,6 +56,7 @@ + @@ -64,6 +66,7 @@ + ResXFileCodeGenerator diff --git a/src/Remact.Net/ActorInput.cs b/src/Remact.Net/ActorInput.cs index 4147b21..615dd35 100644 --- a/src/Remact.Net/ActorInput.cs +++ b/src/Remact.Net/ActorInput.cs @@ -94,15 +94,6 @@ internal void PrepareServiceName (Uri uri) Uri = uri; } - /// - /// Default = false. When set to true: Disable catalog client, no input of this application will publish its service name to the Remact.Catalog. - /// - public static bool DisableCatalogClient - { - get { return RemactCatalogClient.Instance().DisableCatalogClient; } - set { RemactCatalogClient.Instance().DisableCatalogClient = value; } - } - /// /// Link this input to the network. Remote clients will be able to connect to this service after Open() has been called. /// When this method is not called, the service is accessible application internally only. @@ -363,7 +354,7 @@ protected override bool OnConnectDisconnect (ActorMessage msg, ActorInfo info) { if (OnInputConnected != null) OnInputConnected(msg); // optional event } - else if (info.Usage == ActorInfo.Use.ClientDisconnectRequest) + else if (info.Usage == ActorInfo.Use.ClientDisconnectNotification) { if (OnInputDisconnected != null) OnInputDisconnected(msg); // optional event } @@ -477,7 +468,7 @@ protected override bool OnConnectDisconnect (ActorMessage msg, ActorInfo info) { if (OnInputConnected != null) OnInputConnected(msg, senderCtx); // optional event } - else if (info.Usage == ActorInfo.Use.ClientDisconnectRequest) + else if (info.Usage == ActorInfo.Use.ClientDisconnectNotification) { if (OnInputDisconnected != null) OnInputDisconnected(msg, senderCtx); // optional event } diff --git a/src/Remact.Net/ActorMessage.cs b/src/Remact.Net/ActorMessage.cs index a36636a..408ce8b 100644 --- a/src/Remact.Net/ActorMessage.cs +++ b/src/Remact.Net/ActorMessage.cs @@ -64,7 +64,7 @@ public enum ActorMessageType /// /// All data for a message sent through Remact. /// Contains the Payload itself as well as some request identification and a reference to the sending ActorPort. - /// The class may be used to send a response to the sender and to trace unique message identification. + /// The class may be used to send a response to the sender and to log unique message identification. /// public class ActorMessage { @@ -125,7 +125,7 @@ public class ActorMessage /// /// The sending partner. /// The ClientId used on the service. - /// The RequestId is incremented by the client. + /// The RequestId is incremented by the client. When 0 is passed, a notification is created. /// The receiving partner. /// The receiving method defines the payload type. /// The user payload to send. @@ -157,7 +157,7 @@ public class ActorMessage /// - /// Creates a new ActorMessage. + /// Copies an ActorMessage. /// /// An ActorMessage to copy all information from. internal ActorMessage(ActorMessage msg) @@ -294,7 +294,7 @@ internal void SendResponseFrom(ActorPort service, object payload, AsyncResponseH /// - /// Each message my be printed e.g. to trace. + /// Each message may be printed e.g. to log. /// /// The message in readable text form. public override string ToString () @@ -328,7 +328,7 @@ public string PayloadType /// - /// Generates part of a standardised mark for trace output on client side. + /// Generates part of a standardised mark for log output on client side. /// internal string DestMark(bool full) { @@ -347,7 +347,7 @@ internal string DestMark(bool full) } /// - /// Generates part of a standardised mark for trace output on service side. + /// Generates part of a standardised mark for log output on service side. /// internal string SourceMark(bool full) { @@ -365,13 +365,6 @@ internal string SourceMark(bool full) } } - - /// - /// Generates part of a standardised mark for trace output. - /// - /// - /// Generates part of a standardised mark for trace output. - /// private string ReqMark { get @@ -391,22 +384,22 @@ private string RspMark } /// - /// Client sending request: Standardised mark for trace output. + /// Client sending request: Standardised mark for log output. /// public string CltSndId { get { return string.Concat( SourceMark(false), ReqMark, "-->"); } } /// - /// Service receiving request: Standardised mark for trace output. + /// Service receiving request: Standardised mark for log output. /// public string SvcRcvId { get { return string.Concat( SourceMark(true), ReqMark, "~~>"); } } /// - /// Service sending response: Standardised mark for trace output. + /// Service sending response: Standardised mark for log output. /// public string SvcSndId { get { return string.Concat( DestMark(true), RspMark, "<~~" ); } } /// - /// Client receiving response: Standardised mark for trace output. + /// Client receiving response: Standardised mark for log output. /// public string CltRcvId { get { return string.Concat( DestMark(false), RspMark, "<--"); } } }; @@ -418,7 +411,7 @@ private string RspMark /// /// All data for a message sent through Remact. /// Contains a Payload of type T as well as some request identification and a reference to the sending ActorPort. - /// The class may be used to send a response to the sender and to trace unique message identification. + /// The class may be used to send a response to the sender and to log unique message identification. /// public class ActorMessage : ActorMessage { diff --git a/src/Remact.Net/ActorOutput.cs b/src/Remact.Net/ActorOutput.cs index 7184d43..daeee8a 100644 --- a/src/Remact.Net/ActorOutput.cs +++ b/src/Remact.Net/ActorOutput.cs @@ -74,30 +74,18 @@ public void LinkOutputTo (IActorInput partner) } /// - /// Link output to remote service. Look for the service Uri at Remact.Catalog on local host. + /// Link output to remote service. Look for the service Uri at Remact.Catalog (catalog uri is defined by RemactConfigDefault). /// Remact.Catalog may have synchronized its service register with peer catalogs on other hosts. /// - /// The unique service name to connect to. - /// Plugin your own client configuration instead of RemactDefaults.Instance.DoClientConfiguration. - public void LinkOutputToRemoteService( string serviceName, IActorOutputConfiguration clientConfig = null ) - { - LinkOutputToRemoteService( "localhost", serviceName, clientConfig ); - } - - /// - /// Link output to remote service. Look for the service Uri at Remact.Catalog on a remote host. - /// Remact.Catalog may have synchronized its service register with peer catalogs on other hosts. - /// - /// The hostname, where the Remact.Catalog runs. /// The unique service name. /// Plugin your own client configuration instead of RemactDefaults.Instance.DoClientConfiguration. - public void LinkOutputToRemoteService(string catalogHost, string serviceName, IActorOutputConfiguration clientConfig = null) + public void LinkOutputToRemoteService (string serviceName, IActorOutputConfiguration clientConfig = null) { Disconnect(); - if (catalogHost != null && serviceName != null && serviceName.Length > 0) + if (!string.IsNullOrEmpty(serviceName)) { var clt = new RemactClient(this); - clt.LinkToService(catalogHost, serviceName, clientConfig); + clt.LinkToService(serviceName, clientConfig); m_Output = clt.ServiceIdent; m_BasicOutput = clt; m_MyOutputProxy = clt; @@ -105,11 +93,11 @@ public void LinkOutputToRemoteService(string catalogHost, string serviceName, IA } /// - /// Link output to remote service. No lookup at Remact.Catalog is needed as we know the romote host and the services TCP portnumber. + /// Link output to remote service. No lookup at Remact.Catalog is needed as we know the romote host and the service TCP portnumber. /// /// The uri of the remote service. /// Plugin your own client configuration instead of RemactDefaults.DoClientConfiguration. - public void LinkOutputToRemoteService( Uri serviceUri, IActorOutputConfiguration clientConfig = null ) + public void LinkOutputToRemoteService (Uri serviceUri, IActorOutputConfiguration clientConfig = null) { Disconnect(); if (serviceUri != null) diff --git a/src/Remact.Net/ActorPort.cs b/src/Remact.Net/ActorPort.cs index 14720e8..8529e05 100644 --- a/src/Remact.Net/ActorPort.cs +++ b/src/Remact.Net/ActorPort.cs @@ -26,7 +26,7 @@ public class ActorPort: IActorPort public bool IsServiceName {get; internal set;} /// - /// Identification in Trace and name of endpoint address in App.config file. + /// Identification in log and name of endpoint address in App.config file. /// public string Name {get; internal set;} @@ -179,7 +179,7 @@ internal void UseDataFrom (ActorInfo p) /// - /// Trace or display status info + /// Log or display status info /// /// Readable communication partner description public override string ToString () @@ -189,7 +189,7 @@ public override string ToString () /// - /// Trace or display formatted status info + /// Log or display formatted status info /// /// Start with this text /// intend the following lines by some spaces @@ -289,11 +289,7 @@ public void PostInput(ActorMessage msg) { if( !m_Connected ) { - var disconnectMsg = msg.Payload as ActorInfo; - if (disconnectMsg == null || disconnectMsg.Usage != ActorInfo.Use.ServiceDisconnectResponse) - { - DispatchingError(msg, new ErrorMessage(ErrorMessage.Code.NotConnected, "Cannot post message")); - } + DispatchingError(msg, new ErrorMessage(ErrorMessage.Code.NotConnected, "Cannot post message")); } else if (m_RedirectIncoming != null) { @@ -396,9 +392,11 @@ public void SendOut(ActorMessage msg) /// Invokes the specified remote method and pass the payload as parameter. /// The remote method has to return a payload of type TRsp. /// The asynchronous responseHandler expects a payload of type TRsp. + /// Unexpected response types do not throw an exception. Such (error) message are sent to the default message handler. /// /// The name of the method to be called. /// The message payload to send. It must be of a type acceptable for the called method. + /// The message that has been sent. Useful for tracing. /// A method or lambda expression handling the asynchronous response. /// The expected type of the response payload. When receiving other types, the message will be passed to the default message handler. public void Ask(string method, object payload, Action responseHandler) where TRsp : class @@ -426,7 +424,7 @@ public void SendOut(ActorMessage msg) /// Ask: sends a request message to the partner ActorPort. /// Invokes the specified remote method and passes the payload as parameter. /// The remote method has to return a payload of type TRsp. - /// The returned asynchronous Task expects a payload of type TRsp. + /// The returned asynchronous Task.Result is of type TRsp. /// /// The name of the method to be called. /// The message payload to send. It must be of a type acceptable for the called method. @@ -435,30 +433,50 @@ public void SendOut(ActorMessage msg) /// A Task to track the asynchronous completion of the request. public Task> Ask(string method, object payload) where TRsp : class { - var tcs = new TaskCompletionSource>(); - - ActorMessage msg = new ActorMessage(this, OutputClientId, NextRequestId, - this, method, payload, + ActorMessage sentMessage; + return Ask (method, payload, out sentMessage, true); + } - delegate (ActorMessage rsp) - { - TRsp typedPayload; - if (rsp.Type == ActorMessageType.Response - && rsp.TryConvertPayload(out typedPayload)) - { - var typedRsp = new ActorMessage(typedPayload, rsp); - tcs.SetResult(typedRsp); - } - else - { - var dynamicRsp = new ActorMessage((dynamic)rsp.Payload, rsp); - var ex = new ActorException(dynamicRsp, "unexpected response type '" + rsp.Payload.GetType().FullName + "' from method '" + method + "'"); - tcs.SetException(ex); - } - return null; - }); + /// + /// Ask: sends a request message to the partner ActorPort. + /// Invokes the specified remote method and passes the payload as parameter. + /// The remote method has to return a payload of type TRsp. + /// The returned asynchronous Task.Result is of type TRsp. + /// + /// The name of the method to be called. + /// The message payload to send. It must be of a type acceptable for the called method. + /// The message that has been sent. Useful for tracing. + /// When set to true, a response with unexpected type (e.g. an ErrorMessage) will be thrown as an ActorException{OtherType}. + /// When set to false, a message with unexpected response type will be sent to the default message handler. + /// The expected type of the response payload. + /// A Task to track the asynchronous completion of the request. + public Task> Ask(string method, object payload, out ActorMessage sentMessage, bool throwException = true) where TRsp : class + { + var tcs = new TaskCompletionSource>(); - SendOut(msg); + sentMessage = new ActorMessage(this, OutputClientId, NextRequestId, this, method, payload, + + delegate(ActorMessage rsp) // the response handler + { + TRsp typedPayload; + if (rsp.Type == ActorMessageType.Response + && rsp.TryConvertPayload(out typedPayload)) + { + var typedRsp = new ActorMessage(typedPayload, rsp); + tcs.SetResult(typedRsp); + return null; + } + else if (throwException) + { + var dynamicRsp = new ActorMessage((dynamic)rsp.Payload, rsp); + var ex = new ActorException(dynamicRsp, "unexpected response type '" + rsp.Payload.GetType().FullName + "' from method '" + method + "'"); + tcs.SetException(ex); + return null; + } + return rsp; // response will be handled by default message handler + }); + + SendOut (sentMessage); return tcs.Task; } @@ -467,7 +485,7 @@ public void SendOut(ActorMessage msg) #region Message dispatching /// - /// The dispatcher for incoming messages must be added by the user. + /// Get the dispatcher for incoming messages. The user must call Dispatcher.AddActorInterface() to make the dispatcher ready for incoming messages. /// public Dispatcher Dispatcher { @@ -495,9 +513,9 @@ public Dispatcher Dispatcher /// /// Set your logging object here (null by default). - /// It is passed to the logging methods of RaLog.ITracePlugin. - /// You will use it when writing your own adapter class based on RaLog.ITracePlugin. - /// The adapter class is needed to redirect trace output to your own logging/tracing framework. + /// It is passed to the logging methods of RaLog.ILogPlugin. + /// You will use it when writing your own adapter class based on RaLog.ILogPlugin. + /// The adapter class is needed to redirect log output to your own logging/tracing framework. /// public object Logger { get; set; } @@ -646,7 +664,7 @@ internal void DispatchMessage (ActorMessage msg) if (connectMsg != null) { if (connectMsg.Usage == ActorInfo.Use.ClientConnectRequest - || connectMsg.Usage == ActorInfo.Use.ClientDisconnectRequest) + || connectMsg.Usage == ActorInfo.Use.ClientDisconnectNotification) { if (TraceConnect && IsServiceName) { diff --git a/src/Remact.Net/Contracts/ActorInfo.cs b/src/Remact.Net/Contracts/ActorInfo.cs index 29286d3..d546808 100644 --- a/src/Remact.Net/Contracts/ActorInfo.cs +++ b/src/Remact.Net/Contracts/ActorInfo.cs @@ -25,7 +25,7 @@ public class ActorInfo public bool IsServiceName; /// - /// Identification in Trace and name of endpoint address in App.config file. + /// Identification in logs and name of endpoint address in App.config file. /// public string Name; @@ -121,6 +121,7 @@ public Use Usage private int usage; /// + /// TODO replace this by bool Active /// Usage of ActorInfo triggers functionality on service oder client side while connecting/disconnecting. /// Use is set to ServiceEnableRequest when a Service is opened or ClientConnectRequest when a client is connected. /// Use is set to another state when a Service is closed or a client is disconnected or a timeout has occured. @@ -132,15 +133,11 @@ public enum Use /// Undef, - /// - /// A constructor has been called that sets the own address of a service or client. - /// - MyAddress, - /// /// The identified client has sent a connect request to its service. /// ClientConnectRequest, + /// /// The identified service has accepted the connect request from a client. /// @@ -149,34 +146,18 @@ public enum Use /// /// The identified client has sent a disconnect request to its service. /// - ClientDisconnectRequest, - /// - /// The identified service has accepted the disconnect request from a client. - /// - ServiceDisconnectResponse, + ClientDisconnectNotification, /// /// The identified service has sent a register request to Remact.Catalog. /// ServiceEnableRequest, - /// - /// The identified service has been registered in Remact.Catalog. - /// - ServiceEnableResponse, /// /// The identified service is going to be closed, it has informed Remact.Catalog about it. /// ServiceDisableRequest, - /// - /// The identified service is marked as closed in Remact.Catalog. - /// - ServiceDisableResponse, - /// - /// The service name is going to be looked up in Remact.Catalog. - /// - ServiceAddressRequest, /// /// The complete, matching service identification has been found in Remact.Catalog registry. /// @@ -335,8 +316,6 @@ public override string ToString () switch (Usage) { - case Use.ServiceEnableResponse: - case Use.ServiceDisableResponse: case Use.ServiceAddressResponse: return String.Format ("{0} for '{1}'", Usage.ToString(), name); diff --git a/src/Remact.Net/Contracts/ErrorMessage.cs b/src/Remact.Net/Contracts/ErrorMessage.cs index 250e7da..ffdbf79 100644 --- a/src/Remact.Net/Contracts/ErrorMessage.cs +++ b/src/Remact.Net/Contracts/ErrorMessage.cs @@ -45,7 +45,7 @@ public Code Error { if (error <= 0 || error >= (int)Code.Last) {return Code.Undef;} - else if (error > (int)Code.LastAppCode && error < (int)Code.NotConnected) + else if (error > (int)Code.LastAppCode && error < (int)Code.NotImplementedOnService) {return Code.Undef;} else {return(Code) error;} } @@ -69,13 +69,7 @@ public enum Code // -------------------- Errorcodes for free use, not used by Remact ------------------ /// - /// An exception occured while executing the request in the service-application. - /// Set by library user. - /// - AppUnhandledExceptionOnService, - - /// - /// The service-application does not know or does not accept this request. + /// The service-application does not accept this request. /// Set by library user. /// AppRequestNotAcceptedByService, @@ -93,15 +87,31 @@ public enum Code AppDataNotAvailableInService, /// - /// This and enum values up to NotConnected are internally mapped to 'Undef' (used to check version compatibility). + /// This and enum values up to NotImplementedOnService are internally mapped to 'Undef' (used to check version compatibility). /// LastAppCode, // -------------------- Errorcodes used by Remact ------------------ + /// + /// The service throws a NotImplementedException or NotSupportedException. This is the case, when an unknown request has been received. + /// A software update on the server may be needed. + /// + NotImplementedOnService = 1000, + + /// + /// The service throws an ArgumentException or ArgumentNullException. This is the case when wrong request arguments are sent. + /// + ArgumentExceptionOnService, + + /// + /// Another exception occured while executing the request in the service-application. + /// + UnhandledExceptionOnService, + /// /// Cannot send as the client is not (yet) connected. /// - NotConnected = 1000, + NotConnected, /// /// Cannot open the client (configuration error). @@ -124,9 +134,9 @@ public enum Code CouldNotStartConnect, /// - /// No response from service, when trying to connect. + /// Error on service, when trying to disconnect. /// - CouldNotConnect, + CouldNotDisconnect, /// /// Wrong response from Remact.Catalog, when trying to connect. @@ -149,45 +159,20 @@ public enum Code CouldNotDispatch, /// - /// The service did not respond in time. Detected by client. - /// - TimeoutOnClient, - - /// - /// The service did not respond in time. Detected by service itself. - /// - TimeoutOnService, - - /// - /// Exception while deserializing or serializing on service side. - /// - ReqOrRspNotSerializableOnService, - - /// - /// null message received. + /// Exception while deserializing on service side. /// - RspNotDeserializableOnClient, + ReqestNotDeserializableOnService, /// - /// The request-message-type is not registered as a known type on this service. + /// Exception while deserializing on client side. /// - RequestTypeUnknownOnService, + ResponseNotDeserializableOnClient, /// /// ActorMessage with unknown client id. /// ClientIdNotFoundOnService, - /// - /// An exception occured while executing the request in the service-application. - /// - ClientDetectedUnhandledExceptionOnService, - - /// - /// An exception occured while executing the request in the service-application. - /// - UnhandledExceptionOnService, - /// /// This and higher enum values are internally mapped to 'Undef' (used to check version compatibility). /// @@ -247,7 +232,7 @@ public ErrorMessage (Code err, Exception ex) }// CTOR 2 /// - /// Trace the errormessage + /// Log the error message /// /// string containing all information about the error public override string ToString () diff --git a/src/Remact.Net/Contracts/IRemactCatalog.cs b/src/Remact.Net/Contracts/IRemactCatalog.cs index b1a3c01..395a984 100644 --- a/src/Remact.Net/Contracts/IRemactCatalog.cs +++ b/src/Remact.Net/Contracts/IRemactCatalog.cs @@ -1,6 +1,8 @@  // Copyright (c) 2014, github.com/steforster/Remact.Net +using System.Threading.Tasks; + namespace Remact.Net.Contracts { /// @@ -13,27 +15,34 @@ namespace Remact.Net.Contracts public interface IRemactCatalog { /// - /// Called when an actor opens its input for remote access. + /// Periodically called when an actor has its input open for remote access. /// /// The of the opened service (ActorInput). - ReadyMessage OpenedInput(ActorInfo actorInput); + Task> InputIsOpen(ActorInfo actorInput); /// /// Called before an actor closes its remotly accessable input. /// /// The of the closing service (ActorInput). - ReadyMessage ClosingInput(ActorInfo actorInput); + Task> InputIsClosed(ActorInfo actorInput); /// - /// Called when a client looks up an ActorInput (service). + /// Called when a client looks up an ActorInput (service) at the catalog. /// /// The name of the ActorInput. - /// An actor input name must be unique in the plant, independant of host or application. + /// An actor input name must be unique in the plant, independent of host or application. /// When several actors have opened inputs with the same name, the lookup returns the actor with the longest running time. /// This allows to start 'backup' actors that will come into play, when the longest running actor is shut down /// and the clients try to reconnnect the lost connection. /// /// The of an opened ActorInput (service). Null, when no such ActorInput is found. - ActorInfo LookupInput(string actorInputName); + Task> LookupInput(string actorInputName); + + /// + /// Synchronization request by peer catalog. Incoming ActorInfo with larger hop count should be discarded. + /// + /// The list of services known by the peer catalog. + /// The list of services registered in our catalog. + Task> SynchronizeCatalog(ActorInfoList serviceList); } } \ No newline at end of file diff --git a/src/Remact.Net/Contracts/IRemactService.cs b/src/Remact.Net/Contracts/IRemactService.cs index 4a1f58c..2912cc9 100644 --- a/src/Remact.Net/Contracts/IRemactService.cs +++ b/src/Remact.Net/Contracts/IRemactService.cs @@ -1,29 +1,27 @@  // Copyright (c) 2014, github.com/steforster/Remact.Net +using System.Threading.Tasks; namespace Remact.Net.Contracts { /// /// Common, remotly callable methods provided by all Remact ActorInputs (services). - /// - /// Note: Method calls returning void are not awaitable for a reply on the client side. These are 'one way' notifications. - /// Use 'ReadyMessage' as return value to be able to await a reply on client side. - /// /// - public interface IRemactService + internal interface IRemactService { /// /// Called when a client connects to a service. + /// The sent destination method name actually is "Remact.ActorInfo.ClientConnectRequest". /// - /// The of the client. - /// The of the service (ActorInput). - ActorInfo ConnectClient(ActorInfo actorOutput); + /// The of the client. Usage = ClientConnectRequest. + /// The of the service (ActorInput). Usage = ServiceConnectResponse. + Task> Remact_ActorInfo_ClientConnectRequest(ActorInfo actorOutput); /// - /// Called when a client gracefully disconnects from a service. + /// Called when a client gracefully disconnects from a service. No reply is expected. + /// The sent destination method name actually is "Remact.ActorInfo.ClientDisconnectNotification". /// - /// The of the client. - /// The of the service (ActorInput). - ActorInfo DisconnectClient(ActorInfo actorOutput); + /// The of the client. Usage = ClientDisconnectNotification. + void Remact_ActorInfo_ClientDisconnectNotification(ActorInfo actorOutput); } } \ No newline at end of file diff --git a/src/Remact.Net/IActor.cs b/src/Remact.Net/IActor.cs index 0772b34..8770f1b 100644 --- a/src/Remact.Net/IActor.cs +++ b/src/Remact.Net/IActor.cs @@ -58,7 +58,7 @@ public enum PortState public interface IActorPort { /// - /// Identification in Trace and name of endpoint address in App.config file. + /// Identification in logs and name of endpoint address in App.config file. /// string Name {get;} @@ -121,7 +121,7 @@ public interface IActorPort List AddressList { get; } /// - /// Trace or display formatted status info + /// Log or display formatted status info /// /// Start with this text /// intend the following lines by some spaces @@ -265,27 +265,19 @@ public interface IActorOutput: IActorPort void LinkOutputTo (IActorInput output); /// - /// Add a RemactClient and lookup the service Uri at Remact.Catalog. + /// Add a RemactClient and lookup the service Uri at Remact.Catalog (catalog uri is defined by RemactConfigDefault). /// Remact.Catalog may have synchronized its service register with peer catalogs on other hosts. /// /// The unique service name to connect to. /// Plugin your own client configuration instead of RemactDefaults.DoClientConfiguration. - void LinkOutputToRemoteService( string serviceName, IActorOutputConfiguration clientConfig = null ); - - /// - /// Add a RemactClient and lookup the service Uri at Remact.Catalog. - /// - /// The hostname, where the Remact.Catalog runs. - /// The unique service name to connect to. - /// Plugin your own client configuration instead of RemactDefaults.DoClientConfiguration. - void LinkOutputToRemoteService(string catalogHost, string serviceName, IActorOutputConfiguration clientConfig = null); + void LinkOutputToRemoteService (string serviceName, IActorOutputConfiguration clientConfig = null); /// /// Add a RemactClient. No lookup at Remact.Catalog is needed as we know the TCP portnumber. /// /// The uri of the remote service. /// Plugin your own client configuration instead of RemactDefaults.DoClientConfiguration. - void LinkOutputToRemoteService( Uri serviceUri, IActorOutputConfiguration clientConfig = null ); + void LinkOutputToRemoteService (Uri serviceUri, IActorOutputConfiguration clientConfig = null); /// /// The request id given to the last message sent from this client. diff --git a/src/Remact.Net/IRemactConfig.cs b/src/Remact.Net/IRemactConfig.cs index 18f8d20..4181b6c 100644 --- a/src/Remact.Net/IRemactConfig.cs +++ b/src/Remact.Net/IRemactConfig.cs @@ -18,15 +18,26 @@ public interface IRemactConfig : IActorInputConfiguration, IActorOutputConfigura //---------------------------------------------------------------------------------------------- #region == Remact.Catalog configuration == + + /// + /// Default = false. When set to true: No input of this application will publish its service name to the Remact.Catalog. No output may be connected by service name only. + /// + bool DisableCatalogClient { get; set; } + + /// + /// Normally the Remact.Catalog is running on every host having services. Therefore the default hostname is 'localhost'. + /// + string CatalogHost { get; set; } + /// /// The Remact.Catalog service listens on this port. The Remact.Catalog must be running on every host having services. /// - int CatalogPort {get;} + int CatalogPort {get;} /// /// The Remact.Catalog service listens on this name. /// - string CatalogServiceName {get;} + string CatalogServiceName {get;} #endregion @@ -60,7 +71,7 @@ public interface IRemactConfig : IActorInputConfiguration, IActorOutputConfigura bool IsAppIdUniqueInPlant (int appId); /// - /// When ApplicationInstance remains 0, the operating system process id is used as a application instance id for communication and trace. + /// When ApplicationInstance is 0, the operating system process id is used for application identification. /// bool IsProcessIdUsed (int appId); @@ -81,10 +92,10 @@ public interface IRemactConfig : IActorInputConfiguration, IActorOutputConfigura #endregion //---------------------------------------------------------------------------------------------- - #region == Tracing == + #region == Logging == /// - /// Get the folder name where tracefiles may be stored. + /// Get the folder name where log files are stored. /// string LogFolder {get;} diff --git a/src/Remact.Net/Protocol/IRemactProtocolDriver.cs b/src/Remact.Net/Protocol/IRemactProtocolDriver.cs index f425431..4eb9c44 100644 --- a/src/Remact.Net/Protocol/IRemactProtocolDriver.cs +++ b/src/Remact.Net/Protocol/IRemactProtocolDriver.cs @@ -47,10 +47,10 @@ public interface IRemactProtocolDriverService public interface IRemactProtocolDriverCallbacks { /// - /// Occurs when the WebSocketClient could connect to the server. + /// Occurs when the WebSocketClient has finished connecting to the server. /// - /// response is of type 'ActorMessage'. response.Payload contains information in case of error. - void OnOpenCompleted(object response); + /// Response.Payload contains information in case of error. + void OnOpenCompleted(ActorMessage request); /// /// Gets the endpoint uri of the client diff --git a/src/Remact.Net/Protocol/Wamp/WampClient.cs b/src/Remact.Net/Protocol/Wamp/WampClient.cs index dab7f80..86f24c5 100644 --- a/src/Remact.Net/Protocol/Wamp/WampClient.cs +++ b/src/Remact.Net/Protocol/Wamp/WampClient.cs @@ -87,7 +87,7 @@ private void OnConnected(UserContext context) context.SetOnDisconnect(OnDisconnect); } - ConnectCallback(request); + _callback.OnOpenCompleted(request); } private void OnConnectFailure(UserContext context) @@ -95,32 +95,9 @@ private void OnConnectFailure(UserContext context) _faulted = true; var request = (ActorMessage)context.Data; request.Payload = new ErrorMessage(ErrorMessage.Code.CouldNotOpen, context.LatestException); - ConnectCallback(request); + _callback.OnOpenCompleted(request); } - private void ConnectCallback(ActorMessage request) - { - if (_disposed) - { - return; - } - - if (request.Source.IsMultithreaded) - { - _callback.OnOpenCompleted(request); // Test1.ClientNoSync, CatalogClient - } - else if (request.Source.SyncContext == null) - { - RaLog.Error("Remact", "No synchronization context to open " + request.Source.Name, request.Source.Logger); - _callback.OnOpenCompleted(request); - } - else - { - request.Source.SyncContext.Post(_callback.OnOpenCompleted, request); - } - } - - public void Dispose() { try @@ -157,7 +134,7 @@ private void ResponseNotDeserializable(int id, string errorDesc) return; // do not respond endless on erronous error messages } - var error = new ErrorMessage(ErrorMessage.Code.RspNotDeserializableOnClient, errorDesc); + var error = new ErrorMessage(ErrorMessage.Code.ResponseNotDeserializableOnClient, errorDesc); var message = new ActorMessage(null, 0, id, null, null, error); ErrorFromClient(message); } diff --git a/src/Remact.Net/Protocol/Wamp/WampClientProxy.cs b/src/Remact.Net/Protocol/Wamp/WampClientProxy.cs index bfcaa62..63e733d 100644 --- a/src/Remact.Net/Protocol/Wamp/WampClientProxy.cs +++ b/src/Remact.Net/Protocol/Wamp/WampClientProxy.cs @@ -50,7 +50,7 @@ public void OnServiceDisconnect() #region IRemactProtocolDriverCallbacks implementation - public void OnOpenCompleted(object response) + public void OnOpenCompleted(ActorMessage request) { throw new NotImplementedException(); } @@ -58,7 +58,7 @@ public void OnOpenCompleted(object response) private void RequestNotDeserializable(int id, string errorDesc) { - var error = new ErrorMessage(ErrorMessage.Code.ReqOrRspNotSerializableOnService, errorDesc); + var error = new ErrorMessage(ErrorMessage.Code.ReqestNotDeserializableOnService, errorDesc); OnErrorFromService(id, error); } diff --git a/src/Remact.Net/RemactConfigDefault.cs b/src/Remact.Net/RemactConfigDefault.cs index 9264060..2b58542 100644 --- a/src/Remact.Net/RemactConfigDefault.cs +++ b/src/Remact.Net/RemactConfigDefault.cs @@ -56,6 +56,7 @@ public static IRemactConfig Instance // static configuration Alchemy.Handlers.Handler.FastDirectSendingMode = true; + CatalogHost = "localhost"; } #endregion @@ -123,14 +124,6 @@ protected virtual void OnClientConnected(WebSocketPortManager portManager, UserC { RaLog.Error("Svc:", "No service found on '" + absolutePath + "' to connect client " + userContext.ClientAddress); } - - //if (ServiceIdent.Uri == null) - //{ - // // TODO - // UriBuilder uri = new UriBuilder(OperationContext.Current.Channel.LocalAddress.Uri); - // uri.Host = ServiceIdent.HostName; - // ServiceIdent.Uri = uri.Uri; - //} } /// @@ -148,6 +141,20 @@ public virtual void DoClientConfiguration(object clientBase, ref Uri uri, bool f //---------------------------------------------------------------------------------------------- #region == Remact.Catalog configuration == + /// + /// Default = false. When set to true: No input of this application will publish its service name to the Remact.Catalog. No output may be connected by service name only. + /// + public bool DisableCatalogClient + { + get { return RemactCatalogClient.Instance.DisableCatalogClient; } + set { RemactCatalogClient.Instance.DisableCatalogClient = value; } + } + + /// + /// Normally the Remact.Catalog is running on every host having services. Therefore the default hostname is 'localhost'. + /// + public virtual string CatalogHost { get; set; } + /// /// The Remact.Catalog service listens on this port. The Remact.Catalog must be running on every host having services. /// @@ -211,7 +218,7 @@ public virtual Assembly CifAssembly public virtual bool IsAppIdUniqueInPlant (int appId) {return appId >= 100;} /// - /// When ApplicationInstance remains 0, the operating system process id is used as a application instance payload for communication and trace. + /// When ApplicationInstance is 0, the operating system process id is used for application identification. /// public virtual bool IsProcessIdUsed (int appId) {return appId == 0;} @@ -265,9 +272,8 @@ public virtual string GetAppIdentification (string appName, int appInstance, str /// Library users may change here how to extract the application instance id from commandline arguments. /// /// the commandline arguments passed to Main() - /// null or the plugin to write trace - /// when true: install handlers for normal and exceptional application exit - public static void ApplicationStart (string[] args, RaLog.ITracePlugin traceWriter) + /// null or the plugin to write trace + public static void ApplicationStart (string[] args, RaLog.ILogPlugin logWriter) { int appInstance; // by default the first commandline argument if (args.Length == 0 || !int.TryParse(args[0], out appInstance)) @@ -275,7 +281,7 @@ public static void ApplicationStart (string[] args, RaLog.ITracePlugin traceWrit appInstance = 0; // use ProcessId } - RaLog.UsePlugin (traceWriter); + RaLog.UsePlugin (logWriter); RaLog.Start (appInstance); RemactApplication.InstallExitHandler(); RaLog.Run(); // open file and write first messages @@ -284,7 +290,7 @@ public static void ApplicationStart (string[] args, RaLog.ITracePlugin traceWrit protected string m_LogFolder = null; /// - /// Get the folder name where tracefiles may be stored. + /// Get the folder name where log files may be stored. /// public virtual string LogFolder { @@ -295,16 +301,19 @@ public virtual string LogFolder m_LogFolder = sBase + "/../logs"; if (Directory.Exists(m_LogFolder)) return m_LogFolder; - m_LogFolder = sBase + "/../../logs"; + m_LogFolder = Path.GetFullPath(sBase + "/../../logs"); + if (Directory.Exists(m_LogFolder)) return m_LogFolder; + + m_LogFolder = Path.GetFullPath(sBase + "/../../../logs"); if (Directory.Exists(m_LogFolder)) return m_LogFolder; - m_LogFolder = sBase + "/../../../logs"; + m_LogFolder = Path.GetFullPath(sBase + "/../../../../logs"); if (Directory.Exists(m_LogFolder)) return m_LogFolder; - m_LogFolder = sBase + "/../../../../logs"; + m_LogFolder = Path.GetFullPath(sBase + "/../../../../../logs"); if (Directory.Exists(m_LogFolder)) return m_LogFolder; - // store trace beside .exe file, if no other tracepath exists + // store logs beside .exe file, if no other logs path exists m_LogFolder = sBase; return m_LogFolder; } diff --git a/src/Remact.Net/Remote/MultithreadedServiceNet40.cs b/src/Remact.Net/Remote/MultithreadedServiceNet40.cs index 0c0993b..16a6c25 100644 --- a/src/Remact.Net/Remote/MultithreadedServiceNet40.cs +++ b/src/Remact.Net/Remote/MultithreadedServiceNet40.cs @@ -38,32 +38,32 @@ public MultithreadedServiceNet40(RemactService service, RemactServiceUser svcUse void IRemactProtocolDriverService.MessageFromClient(ActorMessage message) { object response = null; + bool connectEvent = false; + bool disconnectEvent = false; try { - // We are instantiated for each connected client, we know the _svcUser (other stacks do not). + // We are instantiated for each connected client, we know the _svcUser (because of the underlying WebSocket implementation). // Several threads may access the common RemactService. TODO: is the lock really needed ? lock (_service) { - response = _service.CheckBasicResponse(message, ref _svcUser); + response = _service.CheckBasicResponse(message, ref _svcUser, ref connectEvent, ref disconnectEvent); } // multithreaded access, several requests may run in parallel. They will be scheduled for execution on the right synchronization context. - if( response != null ) + if (response != null) { - var connectMsg = response as ActorInfo; - if (connectMsg != null) // no error and connected + if (connectEvent || disconnectEvent) // no error { - var reqCopy = new ActorMessage(message.Source, message.ClientId, message.RequestId, - message.Destination, message.DestinationMethod, message.Payload, - message.SourceLambda); + var reqCopy = new ActorMessage(message); reqCopy.Response = reqCopy; // do not send a ReadyMessage - var task = DoRequestAsync( reqCopy ); // call event OnInputConnected or OnInputDisconnected on the correct thread. - if (connectMsg.Usage != ActorInfo.Use.ServiceDisconnectResponse) + var task = DoRequestAsync(reqCopy); // call event OnInputConnected or OnInputDisconnected on the correct thread. + if (disconnectEvent) { - var dummy = task.Result; // blocking wait! + return; // no reply to disconnect notification } - // return the original response. + var dummy = task.Result; // blocking wait } + // return the basic response. } else { @@ -73,10 +73,25 @@ void IRemactProtocolDriverService.MessageFromClient(ActorMessage message) return; } } - catch( Exception ex ) + catch (NotImplementedException ex) { RaLog.Exception(message.SvcRcvId, ex, _service.ServiceIdent.Logger); - response = new ErrorMessage( ErrorMessage.Code.UnhandledExceptionOnService, ex ); + response = new ErrorMessage(ErrorMessage.Code.NotImplementedOnService, ex); + } + catch (NotSupportedException ex) + { + RaLog.Exception(message.SvcRcvId, ex, _service.ServiceIdent.Logger); + response = new ErrorMessage(ErrorMessage.Code.NotImplementedOnService, ex); + } + catch (ArgumentException ex) + { + RaLog.Exception(message.SvcRcvId, ex, _service.ServiceIdent.Logger); + response = new ErrorMessage(ErrorMessage.Code.ArgumentExceptionOnService, ex); + } + catch (Exception ex) + { + RaLog.Exception(message.SvcRcvId, ex, _service.ServiceIdent.Logger); + response = new ErrorMessage(ErrorMessage.Code.UnhandledExceptionOnService, ex); } message.SendResponse(response); } diff --git a/src/Remact.Net/Remote/RemactCatalogClient.cs b/src/Remact.Net/Remote/RemactCatalogClient.cs index d7e52ed..5803344 100644 --- a/src/Remact.Net/Remote/RemactCatalogClient.cs +++ b/src/Remact.Net/Remote/RemactCatalogClient.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; -using System.Threading; // Timer +using System.Threading; +using System.Threading.Tasks; +using Remact.Net.Contracts; // Timer namespace Remact.Net.Remote { @@ -12,7 +14,7 @@ namespace Remact.Net.Remote /// /// A internally used singleton object for all RemactServices and RemactClientAsync to register/lookup a service with Remact.Catalog /// - internal class RemactCatalogClient + internal class RemactCatalogClient : IRemactCatalog { //---------------------------------------------------------------------------------------------- #region Fields @@ -21,7 +23,7 @@ internal class RemactCatalogClient private static object ms_Lock = new Object(); private static bool ms_DisableCatalogClient; - private RemactClient m_CatalogClient; + private ActorOutput m_CatalogClient; private List m_ClientList; private List m_ServiceList; private int m_nCurrentSvc; @@ -32,14 +34,15 @@ internal class RemactCatalogClient internal bool DisableCatalogClient { - get { return ms_DisableCatalogClient; } - set { - ms_DisableCatalogClient = value; - if (!ms_DisableCatalogClient) + get { return ms_DisableCatalogClient; } + set { - ms_Instance.m_Timer.Change (0, 1000);// connect in Timer thread + ms_DisableCatalogClient = value; + if (!ms_DisableCatalogClient) + { + ms_Instance.m_Timer.Change (0, 1000);// connect in Timer thread + } } - } } #endregion @@ -47,124 +50,127 @@ internal bool DisableCatalogClient #region Events // running on threadpool timer thread - private void OnTimerTick (object state) + private void OnTimerTick (object dummy) { - if (m_Running) return; - m_Running = true; - try - { //------------------------------- - if (m_nCurrentSvc == -100) // Disconnect all services and clients - { - if (ms_Instance != null && m_ServiceList != null) - { - int nServices = m_ServiceList.Count; - lock (ms_Lock) + if (m_Running) return; + m_Running = true; + var state = m_CatalogClient.OutputState; + try + { //------------------------------- + if (m_nCurrentSvc == -100) // Disconnect all services and clients { - while (m_ServiceList.Count > 0) - { - m_ServiceList[0].Disconnect(); // shutdown all services and send ServiceDisable messages to Remact.CatalogService when overloaded in RemactClientAsync - } + if (ms_Instance != null && m_ServiceList != null) + { + int nServices = m_ServiceList.Count; + lock (ms_Lock) + { + while (m_ServiceList.Count > 0) + { + m_ServiceList[0].Disconnect(); // shutdown all services and send ServiceDisable messages to Remact.CatalogService when overloaded in RemactClientAsync + } - while (m_ClientList.Count > 0) - { - m_ClientList[0].Disconnect (); // shutdown all clients and send ClientDisconnectRequest to its connected service - } - } - if (nServices > 0) Thread.Sleep (20 + (nServices*10)); // let the communication end - Disconnect (); // shutdown the CatalogClient itself - } - }//------------------------------- - else if (m_CatalogClient.IsFaulted) - { - if (m_nConnectTries < 0) RaLog.Error("Remact", "Catalog client in fault state !", RemactApplication.Logger); - m_CatalogClient.AbortCommunication (); - m_Timer.Change (15000, 1000); // 15 s warten und neu starten - }//------------------------------- - else if (m_CatalogClient.IsDisconnected) - { - if (!ms_DisableCatalogClient) - { - //m_nSendingThreadId = Thread.CurrentThread.ManagedThreadId; - if (++m_nConnectTries >= 10) m_nConnectTries = 1; - var uri = new Uri("ws://localhost:" + RemactConfigDefault.Instance.CatalogPort + "/" + RemactConfigDefault.WsNamespace + "/" + RemactConfigDefault.Instance.CatalogServiceName); - m_CatalogClient.TryConnectVia( uri, OnMessageReceived, toCatalog:true ); - } - lock (ms_Lock) - { // ev. wurde der CatalogService neu gestartet - foreach (RemactService s in m_ServiceList) + while (m_ClientList.Count > 0) + { + m_ClientList[0].Disconnect (); // shutdown all clients and send ClientDisconnectRequest to its connected service + } + } + + if (nServices > 0) Thread.Sleep (20 + (nServices*10)); // let the communication end + Disconnect (); // shutdown the CatalogClient itself + } + }//------------------------------- + else if (state == PortState.Faulted) { - s.IsServiceRegistered = false; // Status zurücksetzen, so dass er wieder gemeldet wird - } - } - }//------------------------------- - else if (m_CatalogClient.IsConnected) - { - m_nConnectTries = -1; - if (m_ServiceList.Count <= m_nCurrentSvc) - { - m_nCurrentSvc = 0; // keine Svc oder Maximum erreicht - } - else - { - lock (ms_Lock) + if (m_nConnectTries < 0) RaLog.Error("Remact", "Catalog client in fault state !", RemactApplication.Logger); + m_CatalogClient.Disconnect(); + m_Timer.Change (15000, 1000); // 15 s warten und neu starten + }//------------------------------- + else if (state == PortState.Disconnected || state == PortState.Unlinked) { - ActorInfo req = new ActorInfo (m_ServiceList[m_nCurrentSvc].ServiceIdent, ActorInfo.Use.ServiceEnableRequest); - RemactService svc = m_ServiceList[m_nCurrentSvc]; - if (!svc.IsServiceRegistered) - { - svc.NextEnableMessage = DateTime.Now.AddSeconds(20); - svc.IsServiceRegistered = true; - ActorMessage id = m_CatalogClient.SendActorInfo(req); - RaLog.Info( id.CltSndId, "send to Remact.Catalog: " + req.ToString(), RemactApplication.Logger );// msg.CltSndId is updated in Send() - - } - else if (m_ServiceList[m_nCurrentSvc].NextEnableMessage < DateTime.Now) - { - m_ServiceList[m_nCurrentSvc].NextEnableMessage = DateTime.Now.AddSeconds(20); - m_CatalogClient.SendActorInfo(req); - //RaLog.Info (req.CltSndId, "Alive "+req.ToString ()); // req.CltSndId is updated in Send() - } - m_nCurrentSvc++; // next Svc on next timer event - } - } - }//connected + if (!ms_DisableCatalogClient) + { + if (++m_nConnectTries >= 10) m_nConnectTries = 1; + var uri = new Uri(string.Format("ws://{0}:{1}/{2}/{3}", RemactConfigDefault.Instance.CatalogHost, RemactConfigDefault.Instance.CatalogPort, RemactConfigDefault.WsNamespace, RemactConfigDefault.Instance.CatalogServiceName)); + m_CatalogClient.LinkOutputToRemoteService(uri); + m_CatalogClient.TryConnect(); + } - if (ms_DisableCatalogClient && m_Timer != null) m_Timer.Change (Timeout.Infinite, 1000); // stop the timer - } - catch (Exception ex) - { - RaLog.Exception( "during CatalogClient timer", ex, RemactApplication.Logger ); - } - m_Running = false; + lock (ms_Lock) + { + foreach (RemactService s in m_ServiceList) + { + s.IsServiceRegistered = false; // reset in case CatalogService has been restarted + } + } + }//------------------------------- + else if (m_CatalogClient.IsOutputConnected) + { + m_nConnectTries = -1; + if (m_ServiceList.Count <= m_nCurrentSvc) + { + m_nCurrentSvc = 0; + } + else + { + lock (ms_Lock) + { + ActorInfo req = new ActorInfo (m_ServiceList[m_nCurrentSvc].ServiceIdent, ActorInfo.Use.ServiceEnableRequest); + RemactService svc = m_ServiceList[m_nCurrentSvc]; + if (!svc.IsServiceRegistered) + { + svc.NextEnableMessage = DateTime.Now.AddSeconds(20); + svc.IsServiceRegistered = true; + InputIsOpen(req); + RaLog.Info(_latestSentMessage.CltSndId, "Sent to Remact.Catalog: " + req.ToString(), RemactApplication.Logger); + } + else if (m_ServiceList[m_nCurrentSvc].NextEnableMessage < DateTime.Now) + { + m_ServiceList[m_nCurrentSvc].NextEnableMessage = DateTime.Now.AddSeconds(20); + InputIsOpen(req); + //RaLog.Info (_latestSentMessage.CltSndId, "Alive "+req.ToString ()); + } + m_nCurrentSvc++; // next Svc on next timer event + } + } + }//connected + + if (ms_DisableCatalogClient && m_Timer != null) m_Timer.Change (Timeout.Infinite, 1000); // stop the timer + } + catch (Exception ex) + { + RaLog.Exception( "during CatalogClient timer", ex, RemactApplication.Logger ); + } + + m_Running = false; }// OnTimerTick // Response callback from Remact.CatalogService private void OnMessageReceived (ActorMessage rsp) { - if (rsp.Payload is ErrorMessage) - { - if (m_nConnectTries % 20 == 1) { - ErrorMessage err = rsp.Payload as ErrorMessage; - if (err.Error == ErrorMessage.Code.ServiceNotRunning - || err.Error == ErrorMessage.Code.CatalogServiceNotRunning) - { - RaLog.Warning( rsp.CltRcvId, "Remact catalog service not running at '" + rsp.Source.Uri + "'", RemactApplication.Logger ); - } - else - { - RaLog.Warning( rsp.CltRcvId, err.ToString(), RemactApplication.Logger ); - } + if (rsp.Payload is ErrorMessage) + { + if (m_nConnectTries % 20 == 1) { + ErrorMessage err = rsp.Payload as ErrorMessage; + if (err.Error == ErrorMessage.Code.ServiceNotRunning + || err.Error == ErrorMessage.Code.CatalogServiceNotRunning) + { + RaLog.Warning( rsp.CltRcvId, "Remact catalog service not running at '" + rsp.Source.Uri + "'", RemactApplication.Logger ); + } + else + { + RaLog.Warning( rsp.CltRcvId, err.ToString(), RemactApplication.Logger ); + } + } + } + else if (rsp.Payload is ActorInfo && m_ServiceList != null) + { + m_Timer.Change (20, 1000); // 20ms warten und nächsten ActorMessage senden, bis alle erledigt sind + } + else + { + RaLog.Info( rsp.CltRcvId, rsp.ToString(), RemactApplication.Logger ); } - } - else if (rsp.Payload is ActorInfo && m_ServiceList != null) - { - m_Timer.Change (20, 1000); // 20ms warten und nächsten ActorMessage senden, bis alle erledigt sind - } - else - { - RaLog.Info( rsp.CltRcvId, rsp.ToString(), RemactApplication.Logger ); - } }// OnMessageReceived @@ -176,13 +182,16 @@ private void OnMessageReceived (ActorMessage rsp) /// (static) Get or Create the Remact.CatalogClient singleton /// /// singleton instance - internal static RemactCatalogClient Instance () + internal static RemactCatalogClient Instance { - if (ms_Instance == null) - { - ms_Instance = new RemactCatalogClient (); - } - return ms_Instance; + get + { + if (ms_Instance == null) + { + ms_Instance = new RemactCatalogClient(); + } + return ms_Instance; + } } @@ -193,14 +202,15 @@ internal RemactCatalogClient () { m_ServiceList = new List (20); m_ClientList = new List (20); - var clientIdent = new ActorOutput("Remact.CatalogClt", OnMessageReceived); - - m_CatalogClient = new RemactClient(clientIdent); - m_CatalogClient.ClientIdent.IsMultithreaded = true; - m_CatalogClient.ClientIdent.TraceConnect = false; - m_Timer = new Timer (OnTimerTick, this, 1000, 1000); // startet in 1s, Periode=1s + m_CatalogClient = new ActorOutput("Remact.CatalogClient", OnMessageReceived); + m_CatalogClient.IsMultithreaded = true; // all other clients will send LookupInput requests through this client + m_CatalogClient.TraceConnect = false; + m_Timer = new Timer (OnTimerTick, this, 0, 1000); // startet immediately, Periode=1s }// CTOR + + internal bool IsConnected { get { return m_CatalogClient.IsOutputConnected; } } + /// /// (static) Close all incoming network connections and send a ServiceDisable messages to Remact.CatalogService. @@ -226,39 +236,24 @@ internal static void DisconnectAll () /// internal void Disconnect () { - if (m_Timer != null) - { - m_Timer.Dispose(); - int n = m_CatalogClient.OutstandingResponsesCount; - if (!ms_DisableCatalogClient || n != 0 || !m_CatalogClient.IsDisconnected) + if (m_Timer != null) { - if (m_CatalogClient.IsConnected) - { - try + m_Timer.Dispose(); + int n = m_CatalogClient.OutstandingResponsesCount; + if (!ms_DisableCatalogClient || n != 0 || m_CatalogClient.OutputState != PortState.Disconnected) { - m_CatalogClient.Disconnect(); // send last messages, contrary to AbortCommunication(); - m_CatalogClient = null; - RaLog.Info("Remact", "Catalog client disconnected.", RemactApplication.Logger); + m_CatalogClient.Disconnect(); // send last messages, contrary to AbortCommunication(); + m_CatalogClient = null; + RaLog.Info("Remact", "Catalog client disconnected.", RemactApplication.Logger); } - catch - { - } - } - - if( m_CatalogClient != null && !m_CatalogClient.IsDisconnected ) - { - m_CatalogClient.AbortCommunication(); - RaLog.Error("Remact", "Catalog client aborted, outstanding responses = " + n, RemactApplication.Logger); - } - } - m_Timer = null; - m_CatalogClient = null; - m_ServiceList.Clear(); - m_ServiceList = null; - ms_Instance = null; - } - }// Disconnect + m_Timer = null; + m_CatalogClient = null; + m_ServiceList.Clear(); + m_ServiceList = null; + ms_Instance = null; + } + } #endregion @@ -271,12 +266,12 @@ internal void Disconnect () /// the local RemactService internal void AddService (RemactService svc) { - lock (ms_Lock) - { - m_ServiceList.Add (svc); - svc.IsServiceRegistered = false; // triggert EnableMessage - } - }// AddService + lock (ms_Lock) + { + m_ServiceList.Add (svc); + svc.IsServiceRegistered = false; // triggert EnableMessage + } + } /// @@ -285,20 +280,19 @@ internal void AddService (RemactService svc) /// the local RemactService internal void RemoveService (RemactService svc) { - lock (ms_Lock) - { - int n = m_ServiceList.IndexOf (svc); - if (n < 0) return; // already removed - if (m_CatalogClient != null && m_CatalogClient.IsConnected) + lock (ms_Lock) { - ActorInfo req = new ActorInfo (m_ServiceList[n].ServiceIdent, - ActorInfo.Use.ServiceDisableRequest); - ActorMessage id = m_CatalogClient.SendActorInfo(req); - RaLog.Info( id.CltSndId, "Disable " + req.ToString(), RemactApplication.Logger ); - m_ServiceList[n].IsServiceRegistered = false; + int n = m_ServiceList.IndexOf (svc); + if (n < 0) return; // already removed + if (m_CatalogClient != null && m_CatalogClient.IsOutputConnected) + { + ActorInfo req = new ActorInfo (m_ServiceList[n].ServiceIdent, ActorInfo.Use.ServiceDisableRequest); + InputIsClosed(req); + RaLog.Info(_latestSentMessage.CltSndId, "Disabled " + req.ToString(), RemactApplication.Logger); + m_ServiceList[n].IsServiceRegistered = false; + } + m_ServiceList.RemoveAt(n); } - m_ServiceList.RemoveAt(n); - } } @@ -308,7 +302,7 @@ internal void RemoveService (RemactService svc) /// the local RemactClient internal void AddClient (RemactClient clt) { - if (clt == m_CatalogClient) return; // do not add the CatalogClient itself + if (clt.ClientIdent == m_CatalogClient) return; // do not add the CatalogClient itself lock (ms_Lock) { m_ClientList.Add (clt); @@ -322,15 +316,40 @@ internal void AddClient (RemactClient clt) /// the local RemactClient internal void RemoveClient (RemactClient clt) { - lock (ms_Lock) - { - int n = m_ClientList.IndexOf (clt); - if (n < 0) return; // already removed - m_ClientList.RemoveAt (n); - } + lock (ms_Lock) + { + int n = m_ClientList.IndexOf (clt); + if (n < 0) return; // already removed + m_ClientList.RemoveAt (n); + } } #endregion + #region IRemactCatalog implementation + private ActorMessage _latestSentMessage; + + public Task> InputIsOpen(ActorInfo actorInput) + { + return m_CatalogClient.Ask("InputIsOpen", actorInput, out _latestSentMessage, false); + } + + public Task> InputIsClosed(ActorInfo actorInput) + { + return m_CatalogClient.Ask("InputIsClosed", actorInput, out _latestSentMessage, false); + } + + public Task> LookupInput(string actorInputName) + { + ActorMessage sentMessage; + return m_CatalogClient.Ask("LookupInput", actorInputName, out sentMessage, false); + } + + public Task> SynchronizeCatalog(ActorInfoList serviceList) + { + throw new InvalidOperationException(); + } + + #endregion }//class Remact.CatalogClient }//namespace diff --git a/src/Remact.Net/Remote/RemactClient.cs b/src/Remact.Net/Remote/RemactClient.cs index f77d419..3706ee6 100644 --- a/src/Remact.Net/Remote/RemactClient.cs +++ b/src/Remact.Net/Remote/RemactClient.cs @@ -9,6 +9,8 @@ using Remact.Net.Protocol; using Remact.Net.Protocol.Wamp; using System.Collections.Generic; +using System.Threading.Tasks; +using Remact.Net.Contracts; namespace Remact.Net.Remote { @@ -18,25 +20,25 @@ namespace Remact.Net.Remote /// Responses are asynchroniously received on the same thread as the request was sent /// (only when sent from a thread with message queue (as WinForms), but not when sent from a threadpool-thread). /// - internal class RemactClient : IRemoteActor, IRemactProtocolDriverCallbacks + internal class RemactClient : IRemoteActor, IRemactProtocolDriverCallbacks, IRemactService { //---------------------------------------------------------------------------------------------- #region Properties /// /// Detailed information about this client. May be a ActorOutput<TOC> object containing application specific "OutputContext". /// - public ActorOutput ClientIdent {get; private set;} + public ActorOutput ClientIdent {get; private set;} /// /// The last request id received in a response from the connected service. /// It is used to calculate outstandig responses. /// - protected int LastRequestIdReceived; + protected int LastRequestIdReceived; /// /// Detailed information about the connected service. Contains a "UserContext" object for free use by the client application. /// - public ActorInput ServiceIdent {get; private set;} + public ActorInput ServiceIdent {get; private set;} /// /// The lower level client. @@ -67,7 +69,7 @@ internal class RemactClient : IRemoteActor, IRemactProtocolDriverCallbacks /// /// True, when connecting or connected to catalog service, not to the original service. /// - protected bool m_boTemporaryCatalogConn; + private bool _connectViaCatalog; /// /// True, when connecting and not yet connected. @@ -89,26 +91,6 @@ internal class RemactClient : IRemoteActor, IRemactProtocolDriverCallbacks /// protected bool m_boFirstResponseReceived; - /// - /// The default message handler to use, when connected to the target service. - /// - protected MessageHandler m_DefaultInputHandlerForApplication; - - /// - /// The hostname of the catalog service. - /// - protected string m_CatalogHostToLookup; - - /// - /// The TCP port of the catalog. - /// - protected int m_RemactCatalogPort; - - /// - /// True, when traces of the connect process to the target service should be written. - /// - protected bool m_TraceConnectBefore; - /// /// Outstanding requests, key = request ID. /// @@ -125,7 +107,6 @@ internal class RemactClient : IRemoteActor, IRemactProtocolDriverCallbacks /// Link this ActorOutput to the remote service. internal RemactClient (ActorOutput clientIdent) { - m_DefaultInputHandlerForApplication = clientIdent.DefaultInputHandler; m_OutstandingRequests = new Dictionary(); ClientIdent = clientIdent; ServiceIdent = new ActorInput(); // not yet defined @@ -142,25 +123,12 @@ internal RemactClient (ActorOutput clientIdent) /// By default TCP port 40000 is used for Remact.CatalogService, but you can specify another TCP port for the catalog eg. "host:3333" /// A unique name of the service. This service may run on any host that has been registered at the Remact.CatalogService. /// Plugin your own client configuration instead of RemactDefaults.ClientConfiguration. - internal void LinkToService(string catalogHost, string serviceName, IActorOutputConfiguration clientConfig = null) + internal void LinkToService(string serviceName, IActorOutputConfiguration clientConfig = null) { - m_RemactCatalogPort = RemactConfigDefault.Instance.CatalogPort; - m_CatalogHostToLookup = catalogHost; - try - { - int i = catalogHost.LastIndexOf(':'); - if (i > 0) - { - m_CatalogHostToLookup = catalogHost.Substring(0, i); - m_RemactCatalogPort = Convert.ToInt32(catalogHost.Substring(i + 1)); - } - } - catch - { - } - m_CatalogHostToLookup = NormalizeHostName(m_CatalogHostToLookup); + _connectViaCatalog = true; + m_ClientConfig = clientConfig; m_ServiceNameToLookup = serviceName; - ServiceIdent.PrepareServiceName(m_CatalogHostToLookup, m_ServiceNameToLookup); + ServiceIdent.PrepareServiceName(RemactConfigDefault.Instance.CatalogHost, m_ServiceNameToLookup); }// LinkToService (URI) @@ -173,9 +141,9 @@ internal void LinkToService(Uri websocketUri, IActorOutputConfiguration clientCo { // this link method does not read the App.config file (it is running on mono also). if (!IsDisconnected) Disconnect (); - m_CatalogHostToLookup = null; - websocketUri = NormalizeHostName(websocketUri); - m_RequestedServiceUri = websocketUri; + _connectViaCatalog = false; + m_ClientConfig = clientConfig; + m_RequestedServiceUri = NormalizeHostName(websocketUri); } private Uri NormalizeHostName( Uri uri ) @@ -223,27 +191,74 @@ public virtual bool TryConnect() try { - if (m_CatalogHostToLookup != null) + ServiceIdent.IsMultithreaded = ClientIdent.IsMultithreaded; + ServiceIdent.TryConnect(); // internal, from ServiceIdent to ClientIdent + ClientIdent.PickupSynchronizationContext(); + ClientIdent.m_Connected = true; // internal, from ActorOutput to RemactClient + + if (!_connectViaCatalog) { - // connect to catalog first - var uri = new Uri("http://" + m_CatalogHostToLookup + ':' + m_RemactCatalogPort + "/" + RemactConfigDefault.WsNamespace + "/" + RemactConfigDefault.Instance.CatalogServiceName); - TryConnectVia(uri, OnResponseFromCatalogService, toCatalog: true); - return true; + return ConnectToRemoteInput(m_RequestedServiceUri); } - else + + if (RemactCatalogClient.Instance.DisableCatalogClient) + { + throw new InvalidOperationException("cannot open " + ClientIdent.Name + ", RemactCatalogClient is disabled"); + } + + if (!RemactCatalogClient.Instance.IsConnected) { - // do not connect to catalog - LinkToService(m_RequestedServiceUri); - m_protocolClient = new WampClient(m_RequestedServiceUri); - // Let now the library user change binding and security credentials. - // By default RemactDefaults.OnClientConfiguration is called. - var websocketUri = m_RequestedServiceUri; - DoClientConfiguration(ref websocketUri, forCatalog:false); - ServiceIdent.PrepareServiceName(websocketUri); - ClientIdent.PickupSynchronizationContext(); - OpenConnectionToService(); - return true; // Connecting now + Thread.Sleep(100); // initial connection } + + if (!RemactCatalogClient.Instance.IsConnected) + { + return false; + } + + // TODO Timeout and Fault messages + Task> task = RemactCatalogClient.Instance.LookupInput(m_ServiceNameToLookup); + task.ContinueWith(t => + { + try + { + if (ClientIdent.TraceSend) RaLog.Info(t.Result.CltRcvId, "Received response from catalog: '" + t.Result.Source.Name + "' on '" + t.Result.Source.HostName + "'", ClientIdent.Logger); + ServiceIdent.UseDataFrom(t.Result.Payload); + if (ClientIdent.TraceSend) + { + string s = string.Empty; + if (t.Result.Payload.AddressList != null) + { + string delimiter = ", IP-adresses = "; + foreach (var adr in t.Result.Payload.AddressList) + { + s = string.Concat(s, delimiter, adr.ToString()); + delimiter = ", "; + } + } + RaLog.Info(t.Result.CltRcvId, "ServiceAddressResponse: " + t.Result.Payload.Uri + s, ClientIdent.Logger); + } + m_addressesTried = 0; + OnConnectionResponseFromService(null); // try first address + } + catch (ActorException ex) + { + //RaLog.Warning (rsp.CltRcvId, "Catalog "+rsp.ToString()); + if (ex.ActorMessage.Payload.Error == ErrorMessage.Code.ServiceNotRunning) + { + ex.ActorMessage.Payload.Error = ErrorMessage.Code.CatalogServiceNotRunning; + } + EndOfConnectionTries(ex.ActorMessage); // failed at catalog + } + catch (ActorException ex) + { + RaLog.Error(ex.ActorMessage.CltRcvId, "Receiving unexpected response from Remact.CatalogService: " + ex.ActorMessage.ToString(), ClientIdent.Logger); + ex.ActorMessage.Payload = new ErrorMessage(ErrorMessage.Code.CouldNotConnectCatalog, "Unexpected response from Remact.CatalogService"); + EndOfConnectionTries(ex.ActorMessage); // failed at catalog + } + }); + + return true; } catch (Exception ex) { @@ -254,45 +269,28 @@ public virtual bool TryConnect() }// TryConnect - /// - /// Connect this client to a catalog or to the requested service, without configuration from App.config file. - /// - /// fully specified URI of the service - /// The callback method when a response arrives - /// True, when the connection to a catalog is made. - internal virtual void TryConnectVia(Uri websocketUri, MessageHandler viaResponseHandler, bool toCatalog) + public virtual bool ConnectToRemoteInput(Uri uri) { - if (!IsDisconnected) Disconnect (); - try - { - if (toCatalog) - { - m_TraceConnectBefore = ClientIdent.TraceConnect; - ClientIdent.TraceConnect = ClientIdent.TraceSend; - m_boTemporaryCatalogConn = true; - ServiceIdent.Uri = null; // yet unknown - } - else if( m_boTemporaryCatalogConn ) - { - ClientIdent.TraceConnect = m_TraceConnectBefore; - m_boTemporaryCatalogConn = false; - } + m_RequestedServiceUri = NormalizeHostName(uri); + m_protocolClient = new WampClient(m_RequestedServiceUri); + // Let now the library user change binding and security credentials. + // By default RemactDefaults.OnClientConfiguration is called. + var websocketUri = m_RequestedServiceUri; + DoClientConfiguration(ref websocketUri, forCatalog: false); + ServiceIdent.PrepareServiceName(websocketUri); - ClientIdent.DefaultInputHandler = viaResponseHandler; - websocketUri = NormalizeHostName(websocketUri); - m_RequestedServiceUri = websocketUri; - m_protocolClient = new WampClient(websocketUri); - // Let now the library user change binding and security credentials. - // By default RemactDefaults.OnClientConfiguration is called. - DoClientConfiguration(ref websocketUri, toCatalog); // TODO: changes in uri are not reflected in a new m_protocolClient - OpenConnectionToService(); - } - catch (Exception ex) - { - RaLog.Exception("Cannot open Remact connection(2)", ex, ClientIdent.Logger); - m_boTimeout = true; // enter 'faulted' state when eg. configuration is incorrect - } - }// TryConnectVia + ClientIdent.OutputClientId = 0; + ClientIdent.LastRequestIdSent = 9; + LastRequestIdReceived = 9; + m_boFirstResponseReceived = false; + m_boTimeout = false; + m_boConnecting = true; + ActorMessage msg = new ActorMessage(ClientIdent, ClientIdent.OutputClientId, ClientIdent.NextRequestId, + ServiceIdent, null, null); + m_protocolClient.OpenAsync(msg, this); + // Callback to OnOpenCompleted when channel has been opened locally (no TCP connection opened on mono). + return true; // Connecting now + } /// @@ -344,8 +342,8 @@ public void Disconnect() { try { - RemactCatalogClient.Instance().RemoveClient(this); - SendDisconnectMessage(); + RemactCatalogClient.Instance.RemoveClient(this); + Remact_ActorInfo_ClientDisconnectNotification(new ActorInfo(ClientIdent, ActorInfo.Use.ClientDisconnectNotification)); } catch { @@ -355,8 +353,7 @@ public void Disconnect() if (m_protocolClient != null) { m_protocolClient.Dispose(); - RemactCatalogClient.Instance().RemoveClient (this); - //TraceState("Abortd"); + RemactCatalogClient.Instance.RemoveClient (this); } } catch (Exception ex) @@ -368,26 +365,12 @@ public void Disconnect() m_boConnecting = false; m_boFirstResponseReceived = false; m_boTimeout = false; - m_boTemporaryCatalogConn = false; ServiceIdent.m_Connected = false; // internal, from ServiceIdent to ClientIdent ClientIdent.m_Connected = false; // internal, from ActorOutput to RemactClient }// Disconnect - /// - /// internal: Overloaded by ClientAsyncAwait - /// - internal protected virtual void SendDisconnectMessage() - { - bool traceSend = ClientIdent.TraceSend; - ClientIdent.TraceSend = ClientIdent.TraceConnect; - SendActorInfo(new ActorInfo(ClientIdent, ActorInfo.Use.ClientDisconnectRequest)); - ClientIdent.TraceSend = traceSend; - Thread.Sleep(30); - } - - /// /// Abort all messages, go from any state to Disconnected state. /// Makes it possible to restart the client with TryConnect. @@ -396,60 +379,40 @@ public void AbortCommunication() { m_boTimeout = true; Disconnect(); - }// AbortCommunication + } #endregion //---------------------------------------------------------------------------------------------- #region Message handling - /// - /// Trace internal state of this client - /// - /// 6 char mark: Opened, Abortd, ... - public void TraceState (string mark) + + // Eventhandler, running on threadpool thread, sent from m_protocolClient. + void IRemactProtocolDriverCallbacks.OnOpenCompleted(ActorMessage request) { - if (m_protocolClient != null) - { - RaLog.Info("RemactClt", "["+mark.PadRight(6)+"] "+ ClientIdent.Name+"["+ClientIdent.OutputClientId+"]" - +", PortState=" + m_protocolClient.PortState.ToString() - , ClientIdent.Logger ); - } - }// TraceState - - - private void OpenConnectionToService(object dummy) - { - OpenConnectionToService(); // 2. try on the right sync context - } - - - /// - /// Connect this Client to the prepared m_protocolClient. Running on the user thread. - /// - private void OpenConnectionToService() - { - ClientIdent.OutputClientId = 0; - ClientIdent.LastRequestIdSent = 9; - LastRequestIdReceived = 9; - m_boFirstResponseReceived = false; - m_boTimeout = false; - m_boConnecting = true; - if (ServiceIdent.Uri == null) ServiceIdent.PrepareServiceName(m_protocolClient.ServiceUri); - ServiceIdent.IsMultithreaded = ClientIdent.IsMultithreaded; - ServiceIdent.TryConnect(); // internal, from ServiceIdent to ClientIdent + if (m_protocolClient == null) + { + return; + } - ClientIdent.PickupSynchronizationContext(); - ClientIdent.m_Connected = true; // internal, from ActorOutput to RemactClient - ActorMessage msg = new ActorMessage(ClientIdent, ClientIdent.OutputClientId, ClientIdent.NextRequestId, - ServiceIdent, null, null); - m_protocolClient.OpenAsync(msg, this); - // Callback to OnOpenCompleted when channel has been opened locally (no TCP connection opened on mono). + if (ClientIdent.IsMultithreaded) + { + OnOpenCompletedOnUserThread(request); // Test1.ClientNoSync, CatalogClient + } + else if (ClientIdent.SyncContext == null) + { + RaLog.Error("Remact", "No synchronization context to open " + ClientIdent.Name, ClientIdent.Logger); + OnOpenCompletedOnUserThread(request); + } + else + { + ClientIdent.SyncContext.Post(OnOpenCompletedOnUserThread, request); + } } - // Eventhandler, running on user thread, sent from m_protocolClient. - void IRemactProtocolDriverCallbacks.OnOpenCompleted(object obj) + // Eventhandler, running on user thread. + private void OnOpenCompletedOnUserThread(object obj) { ActorMessage msg = obj as ActorMessage; msg.DestinationLambda = null; @@ -472,18 +435,25 @@ void IRemactProtocolDriverCallbacks.OnOpenCompleted(object obj) } else { - string serviceAddr = GetSetServiceAddress(); - msg.Payload = new ActorInfo(ClientIdent, ActorInfo.Use.ClientConnectRequest); - msg.DestinationMethod = string.Concat(ActorInfo.MethodNamePrefix, ActorInfo.Use.ClientConnectRequest); - - if (ClientIdent.TraceConnect) { - if (m_boTemporaryCatalogConn) RaLog.Info(msg.CltSndId, string.Concat("Temporary connecting .....: '", serviceAddr, "'"), ClientIdent.Logger); - else RaLog.Info(msg.CltSndId, string.Concat("Connecting svc: '", serviceAddr, "'"), ClientIdent.Logger); - } - - // send first connection request to RemactService - m_OutstandingRequests.Add(msg.RequestId, msg); - m_protocolClient.MessageFromClient(msg); + var task = Remact_ActorInfo_ClientConnectRequest(new ActorInfo(ClientIdent, ActorInfo.Use.ClientConnectRequest)); + task.ContinueWith(t => + { + if (t.Result.Payload.Usage == ActorInfo.Use.ServiceConnectResponse) + { // First message received from Service + t.Result.Payload.Uri = ServiceIdent.Uri; // keep the Uri used to request the message (maybe IP address instead of hostname used) + ServiceIdent.UseDataFrom (t.Result.Payload); + ClientIdent.OutputClientId = t.Result.Payload.ClientId; // defined by server + t.Result.ClientId = t.Result.Payload.ClientId; + RemactCatalogClient.Instance.AddClient(this); + m_boFirstResponseReceived = true; // IsConnected --> true ! + m_boConnecting = false; + if (ClientIdent.TraceConnect) RaLog.Info(t.Result.CltRcvId, ServiceIdent.ToString("Connected svc", 0), ClientIdent.Logger); + } + else + { + RaLog.Error( t.Result.CltRcvId, "unexpeced connect response: " + t.Result.ToString(), ClientIdent.Logger ); + } + }); } } catch (Exception ex) @@ -496,6 +466,35 @@ void IRemactProtocolDriverCallbacks.OnOpenCompleted(object obj) }// OnOpenCompleted + #region IRemactService implementation + + public Task> Remact_ActorInfo_ClientConnectRequest(ActorInfo actorOutput) + { + ActorMessage sentMessage; + var task = ClientIdent.Ask(string.Concat(ActorInfo.MethodNamePrefix, "ClientConnectRequest"), actorOutput, out sentMessage, throwException: false); + + if (ClientIdent.TraceConnect) + { + string serviceAddr = GetSetServiceAddress(); + RaLog.Info(sentMessage.CltSndId, string.Concat("Connecting svc: '", serviceAddr, "'"), ClientIdent.Logger); + } + return task; + } + + public void Remact_ActorInfo_ClientDisconnectNotification(ActorInfo actorOutput) + { + bool traceSend = ClientIdent.TraceSend; + ClientIdent.TraceSend = ClientIdent.TraceConnect; + var msg = new ActorMessage(ClientIdent, ClientIdent.OutputClientId, 0, // creates a notification + ServiceIdent, string.Concat(ActorInfo.MethodNamePrefix, "ClientDisconnectNotification"), actorOutput, null); + PostInput(msg); + ClientIdent.TraceSend = traceSend; + Thread.Sleep(30); + } + + #endregion + + /// /// Called before opening a connection. Prepares endpointaddress for tracing. /// @@ -644,20 +643,6 @@ private void OnIncomingMessageOnActorThread(object obj) if (m != null) m.IsSent = true; if (msg.DestinationMethod == null) msg.DestinationMethod = string.Empty; - - if (msg.IsResponse) - { - LastRequestIdReceived = msg.RequestId; - if (msg.DestinationMethod.StartsWith(ActorInfo.MethodNamePrefix)) - { - ActorInfo actorInfo; - if (msg.TryConvertPayload(out actorInfo) - && HandleActorInfo(msg, actorInfo)) - { - return; - } - } - } } ClientIdent.DispatchMessage(msg); @@ -669,114 +654,6 @@ private void OnIncomingMessageOnActorThread(object obj) }// OnIncomingMessageOnActorThread - /// - /// This function is normally only used internally by OnRequestCompleted. It checks whether the response has to be handled by application code. - /// - /// received response. - /// response is of type ActorInfo. - /// True if response has been handled internally; False, when response must be handled by application - protected bool HandleActorInfo(ActorMessage result, ActorInfo rsp) - { - if (rsp.Usage == ActorInfo.Use.ServiceConnectResponse) - { // First message received from Service - rsp.Uri = ServiceIdent.Uri; // keep the Uri used to request the message (maybe IP address instead of hostname used) - ServiceIdent.UseDataFrom (rsp); - ClientIdent.OutputClientId = rsp.ClientId; // defined by server - result.ClientId = rsp.ClientId; - OnConnectMessage (result); - } - else if (rsp.Usage == ActorInfo.Use.ServiceDisconnectResponse) - { - RaLog.Info( result.CltRcvId, rsp.ToString(), ClientIdent.Logger ); // this is unexpected, response is swallowed by service - return true; - } - else if (rsp.Usage == ActorInfo.Use.ServiceAddressResponse - || rsp.Usage == ActorInfo.Use.ServiceEnableResponse - || rsp.Usage == ActorInfo.Use.ServiceDisableResponse) - { - // service address management - } - else - { - RaLog.Error( result.CltRcvId, "Unknown use of " + rsp.ToString(), ClientIdent.Logger ); - } - - return false; // Message must be handled by application - } - - - // Implements the connect message handling for RemactClientAsync. - // 1. call from Remact.CatalogService - // 2. call from looked up service - // the same message will be sent to OnResponseFromCatalogService or to application later on. - internal void OnConnectMessage(ActorMessage id) - { - if (m_CatalogHostToLookup == null || ServiceIdent.Name == m_ServiceNameToLookup) - { - RemactCatalogClient.Instance ().AddClient (this); - m_boFirstResponseReceived = true; // IsConnected --> true ! - m_boConnecting = false; - if( ClientIdent.TraceConnect ) RaLog.Info( id.CltRcvId, ServiceIdent.ToString( "Connected svc", 0 ), ClientIdent.Logger ); - //TraceState("Opened"); - } - } - - - // Response callback from Remact.CatalogService - private void OnResponseFromCatalogService(ActorMessage rsp) - { - ActorInfo svcRsp = rsp.Payload as ActorInfo; - if (svcRsp != null && svcRsp.Usage == ActorInfo.Use.ServiceConnectResponse) - { - if (ClientIdent.TraceSend) RaLog.Info(rsp.CltRcvId, "Temporary connected catalog: '" + svcRsp.Name + "' on '" + svcRsp.HostName + "'", ClientIdent.Logger); - ActorPort lookup = new ActorPort(); - lookup.HostName = m_CatalogHostToLookup; - lookup.Name = m_ServiceNameToLookup; - lookup.IsServiceName = true; - ActorInfo req = new ActorInfo(lookup, ActorInfo.Use.ServiceAddressRequest); - SendActorInfo(req); // lookup the service URI (especially the TCP port) - } - else if (svcRsp != null && svcRsp.Usage == ActorInfo.Use.ServiceAddressResponse) - { - ServiceIdent.UseDataFrom( svcRsp ); - if( ClientIdent.TraceSend ) - { - string s = string.Empty; - if( svcRsp.AddressList != null ) - { - string delimiter = ", IP-adresses = "; - foreach( var adr in svcRsp.AddressList ) - { - s = string.Concat( s, delimiter, adr.ToString() ); - delimiter = ", "; - } - } - RaLog.Info( rsp.CltRcvId, "ServiceAddressResponse: " + svcRsp.Uri + s, ClientIdent.Logger ); - } - m_addressesTried = 0; - OnConnectionResponseFromService( null ); // try first address - } - else - { - ErrorMessage err = rsp.Payload as ErrorMessage; - if (err != null) - { - //RaLog.Warning (rsp.CltRcvId, "Catalog "+rsp.ToString()); - if (err.Error == ErrorMessage.Code.ServiceNotRunning) - { - err.Error = ErrorMessage.Code.CatalogServiceNotRunning; - } - } - else - { - RaLog.Error( rsp.CltRcvId, "Receiving unexpected response from Remact.CatalogService: " + rsp.ToString(), ClientIdent.Logger ); - rsp.Payload = new ErrorMessage(ErrorMessage.Code.CouldNotConnectCatalog, - "Unexpected response from Remact.CatalogService"); - } - EndOfConnectionTries(rsp); // failed at catalog - } - }// OnResponseFromCatalogService - // Response callback from real service private void OnConnectionResponseFromService( ActorMessage rsp ) @@ -812,19 +689,14 @@ private void OnConnectionResponseFromService( ActorMessage rsp ) b.Host = ServiceIdent.AddressList[m_addressNumber - 1].ToString(); // an IP address } b.Host = b.Uri.DnsSafeHost; - TryConnectVia(b.Uri, OnConnectionResponseFromService, toCatalog: false); + ConnectToRemoteInput(b.Uri); } private void EndOfConnectionTries( ActorMessage rsp ) { m_boTimeout = !m_boFirstResponseReceived; // Fault state when not correct response in OnConnectMessage - if( m_boTemporaryCatalogConn ) - { - ClientIdent.TraceConnect = m_TraceConnectBefore; - } - ClientIdent.DefaultInputHandler = m_DefaultInputHandlerForApplication; try { ClientIdent.DefaultInputHandler(rsp); // pass the negative or positive feedback from catalog or real service to the application @@ -833,12 +705,6 @@ private void EndOfConnectionTries( ActorMessage rsp ) { RaLog.Exception( "Connect message to " + ClientIdent.Name + " cannot be handled by application", ex, ClientIdent.Logger ); } - - if( m_boTimeout && m_boTemporaryCatalogConn ) - { - ServiceIdent.PrepareServiceName( m_CatalogHostToLookup, m_ServiceNameToLookup ); // prepare for next connect try - } - m_boTemporaryCatalogConn = false; } @@ -892,7 +758,7 @@ public PortState OutputState public void PostInput (ActorMessage msg) { ErrorMessage err = null; - if (!IsFaulted && ClientIdent.OutputClientId > 0) // Send() may be used during connection buildup as well + if (!IsFaulted && m_protocolClient != null) // PostInput() may be used during connection buildup as well { try { @@ -924,21 +790,6 @@ public void PostInput (ActorMessage msg) } } - - /// - /// Asynchronously send an ActorInfo to the service. - /// - /// The message to send. - public ActorMessage SendActorInfo(ActorInfo request) - { - var method = string.Concat(ActorInfo.MethodNamePrefix, request.Usage); - ActorMessage msg = new ActorMessage(ClientIdent, ClientIdent.OutputClientId, ClientIdent.NextRequestId, - ServiceIdent, method, request, null); - PostInput(msg); - return msg; - } - - /// /// Gets the Uri of a linked service. /// diff --git a/src/Remact.Net/Remote/RemactService.cs b/src/Remact.Net/Remote/RemactService.cs index 481aa1c..0f11ffc 100644 --- a/src/Remact.Net/Remote/RemactService.cs +++ b/src/Remact.Net/Remote/RemactService.cs @@ -5,6 +5,8 @@ using System.Threading; using System.Collections.Generic; using Remact.Net.Protocol; +using System.Net.Sockets; +using System.Net; namespace Remact.Net.Remote { @@ -42,8 +44,8 @@ public class RemactService /// public static bool DisableCatalogClient { - get { return RemactCatalogClient.Instance ().DisableCatalogClient; } - set { RemactCatalogClient.Instance ().DisableCatalogClient = value; } + get { return RemactCatalogClient.Instance.DisableCatalogClient; } + set { RemactCatalogClient.Instance.DisableCatalogClient = value; } } /// @@ -148,7 +150,7 @@ private RemactService (ActorInput serviceIdent, int firstClientId, int maxClient }// CTOR1 - /// + /*/ /// Create a RemactService object, used by AsyncRemact.Catalog (only). /// /// a unique service name. @@ -163,7 +165,7 @@ internal RemactService(string serviceName, Uri serviceUri, int firstClientId, in ServiceIdent.InputClientList = new List (maxClients); if (firstClientId > 0) m_FirstClientId = firstClientId; else m_FirstClientId = 1; - }// CTOR2 + }// CTOR2*/ /// @@ -192,64 +194,45 @@ internal bool OpenService() try { if (_networkPortManager != null) Disconnect(); -/* - // Do we have to add a dynamically generated endpoint ? - if (m_ServiceHost.Description.Endpoints.Count == 0 - || (m_ServiceHost.Description.Endpoints.Count == 1 && m_ServiceHost.Description.Endpoints[0].Name.ToLower() == "mex")) + + if (_tcpPort == 0) { - if (_tcpPort == 0) + if (ms_nSharedTcpPort==0 || ms_nSharedTcpPortCount==0) { - if (ms_nSharedTcpPort==0 || ms_nSharedTcpPortCount==0) - { - // Find the next free local TCP-port: - Socket socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - IPEndPoint endpoint = new IPEndPoint (0, 0); // Local Address, dynamic port assignment - socket.Bind (endpoint); - endpoint = socket.LocalEndPoint as IPEndPoint; // a free port has been assigned by windows - #if !MONO - ms_nSharedTcpPort = endpoint.Port; // a free dynamic assigned, local port - #else - // Portsharing does not work for Mono, last checked on Mono 2.10.8.1 - _tcpPort = endpoint.Port; - #endif - socket.Close(); // socket.Shutdown is not allowed as we are not yet connected - } - #if !MONO - m_nTcpPort = ms_nSharedTcpPort; - ms_nSharedTcpPortCount++; - #endif + // Find the next free local TCP-port: + Socket socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + IPEndPoint endpoint = new IPEndPoint (0, 0); // Local Address, dynamic port assignment + socket.Bind (endpoint); + endpoint = socket.LocalEndPoint as IPEndPoint; // a free port has been assigned by windows + //#if !MONO + ms_nSharedTcpPort = endpoint.Port; // a free dynamic assigned, local port + //#else + // // Portsharing does not work for Mono, last checked on Mono 2.10.8.1 + // _tcpPort = endpoint.Port; + //#endif + socket.Close(); // socket.Shutdown is not allowed as we are not yet connected } -*/ - // Set URI before the first request arrives (as in RemactService). - // The URI will be sent to Remact.Catalog for registration. - Uri uri = new Uri ("ws://" - + ServiceIdent.HostName // initialized with Dns.GetHostName() - +":"+_tcpPort - +"/"+RemactConfigDefault.WsNamespace+"/"+ServiceIdent.Name);// ServiceName, not the ServiceType - - // Open the ServiceHost to start listening for messages. - // Add the dynamically created endpoint. And let the library user add binding and security credentials. - // By default RemactDefaults.DoServiceConfiguration is called. - _networkPortManager = _serviceConfig.DoServiceConfiguration(this, ref uri, /*isCatalog=*/false); - ServiceIdent.Uri = uri; - //} - //else - //{ - // // Set configured URI so it can be sent to Remact.Catalog for registration. - // // TODO: ServiceIdent.Name is used as identification in Remact.Catalog - should it be changed now ??? - // // or should the Uri be changed / created from different fields ??? - // UriBuilder uri = new UriBuilder (m_ServiceHost.Description.Endpoints[0].ListenUri); - // uri.Host = base.ServiceIdent.HostName; // initialized with Dns.GetHostName(), replaces "localhost" - // base.ServiceIdent.Uri = uri.Uri; - //} + //#if !MONO + _tcpPort = ms_nSharedTcpPort; + ms_nSharedTcpPortCount++; + //#endif + } + + Uri uri = new Uri ("ws://" + + ServiceIdent.HostName // initialized with Dns.GetHostName() + +":"+_tcpPort + +"/"+RemactConfigDefault.WsNamespace+"/"+ServiceIdent.Name);// ServiceName, not the ServiceType + + // Let the library user add the service. By default RemactDefaults.DoServiceConfiguration is called. + _networkPortManager = _serviceConfig.DoServiceConfiguration(this, ref uri, /*isCatalog=*/false); + ServiceIdent.Uri = uri; if (_publishToCatalog) { // Start registering on Remact.Catalog - RemactCatalogClient.Instance().AddService(this); + RemactCatalogClient.Instance.AddService(this); } - // The service can now be accessed, but must be registered. RaLog.Info("Remact", "Opened service " + ServiceIdent.Uri, ServiceIdent.Logger); return true; } @@ -290,7 +273,7 @@ internal void Disconnect() _networkPortManager = null; } - RemactCatalogClient.Instance().RemoveService (this); // send disable message to Remact.CatalogService + RemactCatalogClient.Instance.RemoveService (this); // send disable message to Remact.CatalogService if (ServiceIdent.Uri != null) RaLog.Info("Remact", "Closed service " + ServiceIdent.Uri, ServiceIdent.Logger); else RaLog.Info("Remact", "Closed service " + ServiceIdent.Name, ServiceIdent.Logger); @@ -347,7 +330,7 @@ internal virtual RemactServiceUser AddNewSvcUser (ActorInfo receivedClientMsg, i /// the ActorMessage to be used for responses. /// Output the user object containing a "ClientIdent.UserContext" object for free application use /// Service info as response - private object ConnectPartner(ActorInfo client, ActorMessage req, ref RemactServiceUser svcUser) + private object ConnectPartner(ActorInfo client, ActorMessage req, ref RemactServiceUser svcUser, ref bool connectEvent) { if (req.ClientId != 0) {// Client war schon mal verbunden @@ -440,8 +423,8 @@ private object ConnectPartner(ActorInfo client, ActorMessage req, ref RemactServ // Connection state is kept in client object svcUser.ChannelTestTimer = 0; svcUser.SetConnected(); - //svcUser.OpenNotificationChannel(); HasConnectionStateChanged = true; + connectEvent = true; // reply ServiceIdent ActorInfo response = new ActorInfo (ServiceIdent, ActorInfo.Use.ServiceConnectResponse); @@ -458,14 +441,13 @@ private object ConnectPartner(ActorInfo client, ActorMessage req, ref RemactServ /// the ActorMessage to be used for responses. /// Output the user object containing a "ClientIdent.UserContext" object for free application use /// Service info as response - private object DisconnectPartner(ActorInfo client, ActorMessage req, ref RemactServiceUser svcUser) + private object DisconnectPartner(ActorInfo client, ActorMessage req, ref RemactServiceUser svcUser, ref bool disconnectEvent) { int i = req.ClientId - m_FirstClientId; if (i >= 0 && i < ServiceIdent.InputClientList.Count) { svcUser = ServiceIdent.InputClientList[i].SvcUser; svcUser.ChannelTestTimer = 0; - //req.CurrentSvcUser = svcUser; req.Source = svcUser.ClientIdent; HasConnectionStateChanged = true; if (client.IsEqualTo (svcUser.ClientIdent)) @@ -478,6 +460,7 @@ private object DisconnectPartner(ActorInfo client, ActorMessage req, ref RemactS ServiceIdent.InputClientList[i] = null; // will never be used again, the client has been shutdown } m_ConnectedClientCount--; + disconnectEvent = true; } else { @@ -492,9 +475,8 @@ private object DisconnectPartner(ActorInfo client, ActorMessage req, ref RemactS LastAction = "Disconnect unknown client"; } - // Note: This response will not really be sent to the client. The connection is already disconnected. - // See ActorPort.PostInput - ActorInfo response = new ActorInfo (ServiceIdent, ActorInfo.Use.ServiceDisconnectResponse); + // Note: This response will normally not be sent to the client. The disconnect message is a notification. + var response = new ErrorMessage(ErrorMessage.Code.CouldNotDisconnect, LastAction); return response; }// Disconnect @@ -562,7 +544,7 @@ internal bool FindPartnerAndCheck (ActorMessage req, ref RemactServiceUser svcUs /// /// null when the response has to be generated by the application. /// !null if the response already has been generated by this class. - internal object CheckBasicResponse(ActorMessage req, ref RemactServiceUser svcUser) + internal object CheckBasicResponse(ActorMessage req, ref RemactServiceUser svcUser, ref bool connectEvent, ref bool disconnectEvent) { if (m_boCurrentlyCalled) { @@ -584,8 +566,8 @@ internal object CheckBasicResponse(ActorMessage req, ref RemactServiceUser svcUs req.Payload = cltReq; // use converted payload later on switch (cltReq.Usage) { - case ActorInfo.Use.ClientConnectRequest: response = ConnectPartner(cltReq, req, ref svcUser); break; - case ActorInfo.Use.ClientDisconnectRequest: response = DisconnectPartner(cltReq, req, ref svcUser); break; + case ActorInfo.Use.ClientConnectRequest: response = ConnectPartner(cltReq, req, ref svcUser, ref connectEvent); break; + case ActorInfo.Use.ClientDisconnectNotification: response = DisconnectPartner(cltReq, req, ref svcUser, ref disconnectEvent); break; default: break;// continue below } } @@ -647,7 +629,6 @@ public bool DoPeriodicTasks() ServiceIdent.InputClientList[i] = null;// will never be used again, the client has been shutdown } m_ConnectedClientCount--; - //u.TraceState(""); } } diff --git a/src/Remact.Net/Remote/RemactServiceUser.cs b/src/Remact.Net/Remote/RemactServiceUser.cs index 8e9d127..eae9dd9 100644 --- a/src/Remact.Net/Remote/RemactServiceUser.cs +++ b/src/Remact.Net/Remote/RemactServiceUser.cs @@ -112,21 +112,6 @@ public bool IsConnected /// public bool IsFaulted {get{return m_boTimeout;}} - - /// - /// Trace internal state of the client connection to this service - /// - /// 6 char, eg. 'Connec', 'Discon', 'Abortd' - public void TraceState(string mark) - { - if (_protocolCallback != null) - { - RaLog.Info ("RemactSvc", "["+mark.PadRight (6)+"] "+ ClientMark - + ", ClientAddress=" + _protocolCallback.ClientUri.ToString() - , ClientIdent.Logger ); - } - } - /// /// Used for tracing messages from/to this client. /// @@ -181,7 +166,6 @@ internal void AbortNotificationChannel () try { Disconnect(); - //TraceState("Abortd"); //_protocolCallback.Dispose(); TODO _protocolCallback = null; } diff --git a/src/Remact.Net/Util/RaLog.cs b/src/Remact.Net/Util/RaLog.cs index b2ec224..2ae8e60 100644 --- a/src/Remact.Net/Util/RaLog.cs +++ b/src/Remact.Net/Util/RaLog.cs @@ -7,57 +7,57 @@ namespace Remact.Net { /// /// Use these static methods to write debugging trace or logs. - /// This way you are able to redirect your trace anywhere. + /// This way you are able to redirect your logs anywhere. /// How to run it: - /// 1. call RaLog.UsePlugin(x) when you don't want to use RaTrcPluginDefault - /// 2. call RaLog.Start(appInstance) to direct output to the correct file and write a trace-header + /// 1. call RaLog.UsePlugin(x) when you don't want to use RaLog.PluginConsole + /// 2. call RaLog.Start(appInstance) to direct output to the correct file and write a log-header /// 3. periodically call RaLog.Run() to write the filebuffer to disk - /// 4. calling RaLog.Stop() during shutdown writes the trace-footer + /// 4. calling RaLog.Stop() during shutdown writes the log-footer /// public partial class RaLog { //------------------------------------------------------------ /// - /// Set the trace plugin. + /// Set the logger plugin. /// - /// Plugin, implementing 'ITracePlugin' - public static void UsePlugin(ITracePlugin p) {ms_Plugin = p;} + /// Plugin, implementing 'ILogPlugin' + public static void UsePlugin(ILogPlugin p) {ms_Plugin = p;} /// - /// Write trace header and store 'ApplicationInstance' + /// Write log header and store 'ApplicationInstance' /// /// a number to identify the application instance, see RemactDefaults public static void Start (int appInstance) {ApplicationInstance = appInstance; if (ms_Plugin != null) ms_Plugin.Start (appInstance);} /// - /// Call it periodically (e.g. 5 sec.) to flush buffer to tracefile + /// Call it periodically (e.g. 5 sec.) to flush buffer to log file /// public static void Run() {if (ms_Plugin != null) ms_Plugin.Run();} /// - /// Write trace footer + /// Write log footer /// public static void Stop () {if (ms_Plugin != null) {ms_Plugin.Stop();}} //------------------------------------------------------------ /// - /// Write informative trace about program flow. + /// Write informative log about program flow. /// /// defining the object or object group - /// Trace text - /// Any optional logger framework object to write trace to. + /// log text + /// Any optional logger framework object to write log to. public static void Info( string group, string text, object logger = null ) { if( ms_Plugin != null ) ms_Plugin.Info( group, text, logger ); } /// - /// Write trace for unexpected, but still accepted, recoverable and tested condition. + /// Write log for unexpected, but still accepted, recoverable and tested condition. /// /// defining the object or object group - /// Trace text - /// Any optional logger framework object to write trace to. + /// log text + /// Any optional logger framework object to write log to. public static void Warning( string group, string text, object logger = null ) { WarningCount++; @@ -65,11 +65,11 @@ public static void Warning( string group, string text, object logger = null ) } /// - /// Write trace for unexpected condition that cannot be handled properly. + /// Write log for unexpected condition that cannot be handled properly. /// /// defining the object or object group - /// Trace text - /// Any optional logger framework object to write trace to. + /// log text + /// Any optional logger framework object to write log to. public static void Error( string group, string text, object logger = null ) { ErrorCount++; @@ -77,11 +77,11 @@ public static void Error( string group, string text, object logger = null ) } /// - /// Write trace about a handled program exception. + /// Write log about a handled program exception. /// - /// Trace text. + /// log text. /// The caugth exception object. - /// Any optional logger framework object to write trace to. + /// Any optional logger framework object to write log to. public static void Exception( string text, Exception ex, object logger = null ) { ErrorCount++; @@ -91,55 +91,55 @@ public static void Exception( string text, Exception ex, object logger = null ) //------------------------------------------------------------ /// - /// Trace plugins must implement this interface + /// Log plugins must implement this interface /// - public interface ITracePlugin + public interface ILogPlugin { /// - /// Write trace header and store 'ApplicationInstance' + /// Write log header and store 'ApplicationInstance' /// /// A number to identify the application instance, see RemactDefaults void Start (int appInstance); /// - /// Call it periodically (e.g. 5 sec.) to flush buffer to tracefile. + /// Call it periodically (e.g. 5 sec.) to flush buffer to log file. /// void Run (); /// - /// Write an info-trace statement. + /// Write an info log statement. /// - /// A mark to group trace source. - /// The trace line(s). - /// Any optional logger framework object to write trace to. + /// A mark to group log source. + /// The log line(s). + /// Any optional logger framework object to write log to. void Info( string group, string text, object logger ); /// - /// Write a warning-trace statement. + /// Write a warning log statement. /// - /// A mark to group trace source. - /// The trace line(s). - /// Any optional logger framework object to write trace to. + /// A mark to group log source. + /// The log line(s). + /// Any optional logger framework object to write log to. void Warning( string group, string text, object logger ); /// - /// Write an error-trace statement. + /// Write an error log statement. /// - /// A mark to group trace source. - /// The trace line(s). - /// Any optional logger framework object to write trace to. + /// A mark to group log source. + /// The log line(s). + /// Any optional logger framework object to write log to. void Error( string group, string text, object logger ); /// - /// Write an exception-trace statement. + /// Write an exception log statement. /// - /// The trace line(s). + /// The log line(s). /// The exception. - /// Any optional logger framework object to write trace to. + /// Any optional logger framework object to write log to. void Exception( string text, Exception ex, object logger ); /// - /// Write trace footer. + /// Write log footer. /// void Stop (); } @@ -150,21 +150,21 @@ public interface ITracePlugin /// public static int ApplicationInstance {get; private set;} - private static ITracePlugin ms_Plugin = null; + private static ILogPlugin ms_Plugin = null; //------------------------------------------------------------ /// - /// Count of warning traces (for unit tests). + /// Count of warning logs (for unit tests). /// public static int WarningCount; /// - /// Count of warning traces (for unit tests). + /// Count of error logs (for unit tests). /// public static int ErrorCount; /// - /// Reset the trace counters to zero. + /// Reset the log counters to zero. /// public static void ResetCount() { diff --git a/src/Remact.Net/Util/RaLogPluginConsole.cs b/src/Remact.Net/Util/RaLogPluginConsole.cs index 3f5661c..2c2a537 100644 --- a/src/Remact.Net/Util/RaLogPluginConsole.cs +++ b/src/Remact.Net/Util/RaLogPluginConsole.cs @@ -11,16 +11,16 @@ namespace Remact.Net public partial class RaLog { /// - /// The default implementation of a ITracePlugin + /// The default implementation of a ILogPlugin /// Writes to visual studio diagnostic console or to Terminal/Console. - /// You can easyly write a similar adapter class to redirect trace output to your own logging framework. + /// You can easely write a similar adapter class to redirect log output to your own logging framework. /// - public class PluginConsole: ITracePlugin + public class PluginConsole: ILogPlugin { //------------------------------------------------------------ - // implement ITracePlugin + // implement ILogPlugin /// - /// Write trace header and store 'ApplicationInstance' + /// Write log header and store 'ApplicationInstance' /// /// a number to identify the application instance, see RemactDefaults public void Start (int appInstance) @@ -29,68 +29,68 @@ public void Start (int appInstance) } /// - /// Call it periodically (e.g. 5 sec.) to flush buffer to tracefile. + /// Call it periodically (e.g. 5 sec.) to flush buffer to log file. /// public void Run () { } /// - /// Write trace footer. + /// Write log footer. /// public void Stop () { } /// public void Info( string group, string text, object logger ) { - Trace( "..", group, text ); + Log( "..", group, text ); } /// public void Warning( string group, string text, object logger ) { - Trace( "!!", group, text ); + Log("!!", group, text); } /// public void Error( string group, string text, object logger ) { - Trace( "##", group, text ); + Log("##", group, text); } /// public void Exception( string text, Exception ex, object logger ) { - Trace( "##", "EXCEPT", text + Log("##", "EXCEPT", text +"\r\n " + ex.Message +"\r\n" + ex.StackTrace ); } /// - /// Write a trace statement. + /// Write a log statement. /// - /// A mark to distiguish trace severity. - /// A mark to group trace source. - /// The trace line(s). - private void Trace (string severity, string group, string text) + /// A mark to distiguish log severity. + /// A mark to group log source. + /// The log line(s). + private void Log(string severity, string group, string text) { - string s = String.Format (m_TraceFormat, severity, DateTime.Now, group, text); + string s = String.Format (m_LogFormat, severity, DateTime.Now, group, text); if (OutputWriter == null) System.Diagnostics.Trace.Write (s); else OutputWriter.Write (s); // for MonoDevelop 2010-04 } //------------------------------------------------------------ // Class data - private static string m_TraceFormat; + private static string m_LogFormat; private static bool m_boDisplayDate = (DisplayDate = false); /// - /// use null, Console.Out or Console.Error to specify where the trace should go. + /// use null, Console.Out or Console.Error to specify where the log should go. /// null: output to the developments system diagnostic console. /// public static System.IO.TextWriter OutputWriter = null; ///-------------------------------------------------------------------------- /// - /// choose whether to trace date on each line or not + /// choose whether to log date on each line or not /// public static bool DisplayDate { @@ -102,8 +102,8 @@ public static bool DisplayDate // {1} = DateTime.Now // {2} = group, Format string: {Num,FieldLen:Format} // {3} = info - if (m_boDisplayDate) m_TraceFormat = "{0}{1:d} {1:HH:mm:ss.fff}, {2,-6}, {3}\r\n"; - else m_TraceFormat = "{0}{1:HH:mm:ss.fff}, {2,-6}, {3}\r\n"; + if (m_boDisplayDate) m_LogFormat = "{0}{1:d} {1:HH:mm:ss.fff}, {2,-6}, {3}\r\n"; + else m_LogFormat = "{0}{1:HH:mm:ss.fff}, {2,-6}, {3}\r\n"; } } diff --git a/src/Remact.Net/Util/RaLogPluginFile.cs b/src/Remact.Net/Util/RaLogPluginFile.cs index 7bb2b21..a86fc37 100644 --- a/src/Remact.Net/Util/RaLogPluginFile.cs +++ b/src/Remact.Net/Util/RaLogPluginFile.cs @@ -14,74 +14,74 @@ namespace Remact.Net public partial class RaLog { /// - /// The 'file' implementation of a ITracePlugin - /// Writes 2 tracefiles. - /// Switches to the next tracefile, when 1MB has been reached. - /// Finds default trace folder. - /// - public class PluginFile: ITracePlugin, IDisposable + /// The 'file' implementation of a ILogPlugin + /// Writes 2 log files (.log.txt and .log.old.txt). + /// Switches to the next log file, when 1MB has been reached. + /// Finds default log folder. + /// + public class PluginFile: ILogPlugin, IDisposable { //------------------------------------------------------------ /// - /// Write trace header and store 'ApplicationInstance' - /// You can easyly write a similar adapter class to redirect trace output to your own logging framework. See RaLog.PluginConsole. + /// Write log header and store 'ApplicationInstance' + /// You can easely write a similar adapter class to redirect log output to your own logging framework. See RaLog.PluginConsole. /// /// a number to identify the application instance, see RemactDefaults public void Start (int appInstance) { ApplicationInstance = appInstance; - SetTraceOutput(null); + SetLogOutput(null); } /// - /// Call it periodically (e.g. 5 sec.) to flush buffer to tracefile + /// Call it periodically (e.g. 5 sec.) to flush buffer to log file /// public void Run () { Refresh (); } /// - /// Write trace footer + /// Write log footer /// public void Stop () { Dispose (); } /// public void Info( string group, string text, object logger ) { - Trace( "..", group, text ); + Log( "..", group, text ); } /// public void Warning( string group, string text, object logger ) { - Trace( "!!", group, text ); + Log("!!", group, text); } /// public void Error( string group, string text, object logger ) { - Trace( "##", group, text ); + Log("##", group, text); } /// public void Exception( string text, Exception ex, object logger ) { - Trace( "##", "EXCEPT", text + Log("##", "EXCEPT", text + "\r\n " + ex.Message + "\r\n" + ex.StackTrace ); } /// - /// Write a trace statement. + /// Write a log statement. /// - /// A mark to distiguish trace severity. - /// A mark to group trace source. - /// The trace line(s). - private void Trace (string severity, string group, string text) + /// A mark to distiguish log severity. + /// A mark to group log source. + /// The log line(s). + private void Log(string severity, string group, string text) { - string s = String.Format (m_TraceFormat, severity, DateTime.Now, group, text); - if (m_TraceFile != null) System.Diagnostics.Trace.Write (s); - else TracingException (s); // Notfall Trace, um Aufstartprobleme zu sehen - m_boTraceReady = true; - ++m_nTraceLines; + string s = String.Format (m_LogFormat, severity, DateTime.Now, group, text); + if (m_LogFile != null) System.Diagnostics.Trace.Write (s); + else LoggingException (s); + m_boLogReady = true; + ++m_nLogLines; } @@ -90,16 +90,16 @@ private void Trace (string severity, string group, string text) /// /// Problem report of the tracing itself, for debugging purpose. /// - public static string sLastTraceProblem = ""; + public static string sLastLoggingProblem = ""; - private static TextWriterTraceListener m_TraceFile = null; - private static string m_TraceFileName=""; + private static TextWriterTraceListener m_LogFile = null; + private static string m_LogFileName=""; private static StreamWriter m_DirectStreamWriter = null; - private static bool m_boTraceReady; + private static bool m_boLogReady; private const int c_nMaxFileLength = 1000000; // 1MB - private static int m_nTraceLines = 0; - private static string m_TraceFormat; + private static int m_nLogLines = 0; + private static string m_LogFormat; private static bool m_boDisplayDate = (DisplayDate = false); //-------------------------------------------------------------------------- @@ -112,7 +112,7 @@ private void Dispose (bool calledByUser) { try { - if (m_TraceFile != null) + if (m_LogFile != null) { if (calledByUser) { @@ -130,23 +130,23 @@ private void Dispose (bool calledByUser) } catch (Exception ex) { - TracingException ("##,Error while disposing\r\n " + ex.Message); + LoggingException ("##,Error while disposing\r\n " + ex.Message); } } //-------------------------------------------------------------------------- /// - /// Set or get the current tracefile path and name. + /// Set or get the current log file path and name. /// public static string FileName { - get{return m_TraceFileName;} - set{SetTraceOutput (value);} + get{return m_LogFileName;} + set{SetLogOutput (value);} } ///-------------------------------------------------------------------------- /// - /// choose whether to trace date on each line or not + /// choose wether to log date on each line. /// public static bool DisplayDate { @@ -158,16 +158,16 @@ public static bool DisplayDate // {1} = DateTime.Now // {2} = group, Format string: {Num,FieldLen:Format} // {3} = info - if (m_boDisplayDate) m_TraceFormat = "{0}{1:d} {1:HH:mm:ss.fff}, {2,-6}, {3}\r\n"; - else m_TraceFormat = "{0}{1:HH:mm:ss.fff}, {2,-6}, {3}\r\n"; + if (m_boDisplayDate) m_LogFormat = "{0}{1:d} {1:HH:mm:ss.fff}, {2,-6}, {3}\r\n"; + else m_LogFormat = "{0}{1:HH:mm:ss.fff}, {2,-6}, {3}\r\n"; } } //-------------------------------------------------------------------------- /// - /// Set the current tracefile path and name. + /// Set the current log file path and name. /// - public static void SetTraceOutput (string i_FileName) + public static void SetLogOutput (string i_FileName) { bool boStartup = true; @@ -176,12 +176,10 @@ public static void SetTraceOutput (string i_FileName) if (i_FileName == null || i_FileName.Length == 0) { i_FileName = RemactConfigDefault.Instance.LogFolder; - // i_FileName += "/" + Path.GetFileNameWithoutExtension (RemactApplication.ExecutablePath); - // if (!Directory.Exists(i_FileName)) Directory.CreateDirectory(i_FileName); - // older files than 30 days will be deleted + // files older than 30 days will be deleted DateTime tooOld = DateTime.Now.AddDays(-30); - string[] files = Directory.GetFiles (i_FileName, "*.trace.*", SearchOption.TopDirectoryOnly); + string[] files = Directory.GetFiles (i_FileName, "*.log.*", SearchOption.TopDirectoryOnly); foreach (string filename in files) { try @@ -193,24 +191,24 @@ public static void SetTraceOutput (string i_FileName) } catch (Exception) {} } - i_FileName += "/" + RemactConfigDefault.Instance.AppIdentification + ".trace.txt"; + i_FileName += "/" + RemactConfigDefault.Instance.AppIdentification + ".log.txt"; } } catch (Exception ex) { - TracingException("##,Error while creating directory " + i_FileName +"\r\n " + ex.Message); + LoggingException("##,Error while creating directory " + i_FileName +"\r\n " + ex.Message); } - if (m_TraceFile != null) + if (m_LogFile != null) { boStartup = false; - System.Diagnostics.Trace.WriteLine (String.Format ("| {0:D} {0:T} Next trace file://{1}", DateTime.Now, i_FileName)); + System.Diagnostics.Trace.WriteLine (String.Format ("| {0:D} {0:T} Next log file://{1}", DateTime.Now, i_FileName)); System.Diagnostics.Trace.Flush (); - System.Diagnostics.Trace.Listeners.Remove (m_TraceFile); - m_TraceFileName = ""; - m_TraceFile.Close(); - m_TraceFile.Dispose(); + System.Diagnostics.Trace.Listeners.Remove (m_LogFile); + m_LogFileName = ""; + m_LogFile.Close(); + m_LogFile.Dispose(); } if (m_DirectStreamWriter != null) m_DirectStreamWriter.Dispose(); @@ -230,54 +228,53 @@ public static void SetTraceOutput (string i_FileName) } catch (Exception ex) { - TracingException("##,Error while renaming trace file://" + i_FileName +"\r\n " + ex.Message); + LoggingException("##,Error while renaming log file://" + i_FileName +"\r\n " + ex.Message); } // Create or Append file try { - m_TraceFileName = i_FileName; + m_LogFileName = i_FileName; m_DirectStreamWriter = File.AppendText(i_FileName); //Create a new text writer using the output stream, and add it to the trace listeners. - m_TraceFile = new TextWriterTraceListener(m_DirectStreamWriter); - System.Diagnostics.Trace.Listeners.Add (m_TraceFile); + m_LogFile = new TextWriterTraceListener(m_DirectStreamWriter); + System.Diagnostics.Trace.Listeners.Add (m_LogFile); System.Diagnostics.Trace.WriteLine ("\n+-------------------------------------------------------------------------------------------------"); if (boStartup) { - System.Diagnostics.Trace.WriteLine (String.Format ("| TRACE {0:D} {0:T}: Starting application", DateTime.Now)); + System.Diagnostics.Trace.WriteLine (String.Format ("| LOG {0:D} {0:T}: Starting application", DateTime.Now)); } else { - System.Diagnostics.Trace.WriteLine (String.Format ("| TRACE {0:D} {0:T}: Continue tracing ", DateTime.Now)); + System.Diagnostics.Trace.WriteLine (String.Format ("| LOG {0:D} {0:T}: Continue logging ", DateTime.Now)); } } catch (Exception ex) { // Wenn die Trace.Listener Property null zurückgibt ist ev. kein EXE.config file vorhanden - TracingException("##,Error while opening trace listener file://" + i_FileName +"\r\n " + ex.Message); - if (m_TraceFile != null) m_TraceFile.Dispose(); // schliesst auch den m_DirectStreamWriter - m_TraceFile = null; // Trace Listener funktioniert nicht + LoggingException("##,Error while opening trace listener file://" + i_FileName +"\r\n " + ex.Message); + if (m_LogFile != null) m_LogFile.Dispose(); // schliesst auch den m_DirectStreamWriter + m_LogFile = null; // Trace Listener funktioniert nicht try { m_DirectStreamWriter = File.AppendText(i_FileName); // Notbetrieb direkt ins File } catch (Exception ex2) { - TracingException("##,Error while opening trace file://" + i_FileName +"\r\n " + ex2.Message); + LoggingException("##,Error while opening log file://" + i_FileName +"\r\n " + ex2.Message); } } String s = AppInfo (); - s += "\r\n| CurrentDir \t: " + Environment.CurrentDirectory; - s += "\r\n| Tracefile \t: " + m_TraceFileName; + s += "\r\n| Log file \t: " + m_LogFileName; s += "\r\n+-------------------------------------------------------------------------------------------------"; System.Diagnostics.Trace.WriteLine (s); - m_boTraceReady = true; - m_nTraceLines = 0; + m_boLogReady = true; + m_nLogLines = 0; - }// SetTraceOutput (i_FileName) + }// SetLogOutput (i_FileName) ///-------------------------------------------------------------------------- @@ -288,17 +285,17 @@ public static void Refresh () { try { - if (m_boTraceReady) + if (m_boLogReady) { System.Diagnostics.Trace.Flush (); - m_boTraceReady = false; - if (m_nTraceLines > 200) + m_boLogReady = false; + if (m_nLogLines > 200) { - m_nTraceLines = 0; - FileInfo fi = new FileInfo(m_TraceFileName); + m_nLogLines = 0; + FileInfo fi = new FileInfo(m_LogFileName); if (fi.Length > c_nMaxFileLength) { - SetTraceOutput (m_TraceFileName); + SetLogOutput (m_LogFileName); } } } @@ -306,16 +303,16 @@ public static void Refresh () catch (Exception ex) { // Can't do anything against some other process locking the file ! - sLastTraceProblem = ex.Message; + sLastLoggingProblem = ex.Message; } }// Refresh //-------------------------------------------------------------------------- /// - /// Get tracefile header, may be used for Help - About box. + /// Get log file header, may be used for Help - About box. /// - /// tracefile header + /// log file header public static String AppInfo () { string s = "| "; @@ -355,18 +352,18 @@ public static String AppInfo () if (RemactApplication.ServiceName.Length > 0) s += ", running as service "+RemactApplication.ServiceName; string[] arg = Environment.GetCommandLineArgs(); - s += "\r\n|"; - s += "\r\n| Executable \t: " + arg [0]; // = Application.ExecutablePath + s += "\r\n|"; + s += "\r\n| Current dir\t: " + Environment.CurrentDirectory; + s += "\r\n| Executable \t: " + arg[0]; // = Application.ExecutablePath for (int i=1; i < arg.GetLength(0); i++) { if (i == 1) s += "\r\n| Commandline\t: "; s += arg[i] + " "; } - s += "\r\n|"; } catch (Exception ex) { - TracingException("##,Error while generating application info:" + s + ex.Message); + LoggingException("##,Error while generating application info:" + s + ex.Message); } return s; }// static AppInfo @@ -389,10 +386,10 @@ private static string AssemblyVersion (Assembly A) if (s2!=null)s2=null; // remove a warning } return s; - }// static AssemblyVersion + } - private static void TracingException (string Message) + private static void LoggingException (string Message) { if (m_DirectStreamWriter != null) try { @@ -404,8 +401,7 @@ private static void TracingException (string Message) "Exception while tracing for "+RemactConfigDefault.Instance.AppIdentification, MessageBoxButtons.OK, MessageBoxIcon.Error); } - }// static TracingExeception + } }//class PluginFile -}// partial class RaLog -}// namespace +}} diff --git a/src/Remact.Net/Util/RemactApplication.cs b/src/Remact.Net/Util/RemactApplication.cs index d67f962..04557fb 100644 --- a/src/Remact.Net/Util/RemactApplication.cs +++ b/src/Remact.Net/Util/RemactApplication.cs @@ -40,9 +40,9 @@ public static string ExecutablePath /// /// Set your logging object here (null by default). - /// It is passed to the logging methods of RaLog.ITracePlugin. - /// You will use it when writing your own adapter class based on RaLog.ITracePlugin. - /// The adapter class is needed to redirect trace output to your own logging/tracing framework. + /// It is passed to the logging methods of RaLog.ILogPlugin. + /// You will use it when writing your own adapter class based on RaLog.ILogPlugin. + /// The adapter class is needed to redirect log output to your own logging/tracing framework. /// public static object Logger { get; set; } @@ -51,8 +51,8 @@ public static string ExecutablePath /// Handle the UI exceptions by showing a dialog box, and asking the user whether /// or not they wish to abort execution. /// - /// Before 'Application.Run' add the event handler for handling UI thread exceptions: - /// Application.ThreadException += new ThreadExceptionEventHandler(RaLog.DefaultTracePlugin.WinForms_ThreadException); + /// In 'InstallExitHandler' add the event handler for handling UI thread exceptions: + /// Application.ThreadException += new ThreadExceptionEventHandler(WinForms_ThreadException); /// Set the unhandled exception mode to force all Windows Forms errors to go through our handler. /// Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); /// @@ -85,9 +85,8 @@ public static void WinForms_ThreadException (object sender, ThreadExceptionEvent /// NOTE: This exception cannot be kept from terminating the application - it can only /// log the event, and inform the user about it. /// - /// Before 'Application.Run' add the handler for handling non-UI thread exceptions to the event: - /// AppDomain.CurrentDomain.UnhandledException - /// += new UnhandledExceptionEventHandler(RaLog.DefaultTracePlugin.CurrentDomain_UnhandledException); + /// In 'InstallExitHandler' add the handler for handling non-UI thread exceptions to the event: + /// AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); /// public static void CurrentDomain_UnhandledException (object sender, UnhandledExceptionEventArgs e) { @@ -216,14 +215,6 @@ private static void Console_CancelKeyPress (object sender, ConsoleCancelEventArg /// public static void InstallExitHandler () { - // not working ??? -// Process p = Process.GetCurrentProcess(); -// p.Exited += new EventHandler (ExitEventHandler); -// p.EnableRaisingEvents = true; -// p.Dispose(); -// AppDomain.CurrentDomain.DomainUnload += new EventHandler (ExitEventHandler); -// AppDomain.CurrentDomain.ProcessExit += new EventHandler (ExitEventHandler); - // Windows+Unix: Add the event handler for Console CTRL+C input, this interrupt may end the application // slows down exit to windows: Console.CancelKeyPress += new ConsoleCancelEventHandler (Console_CancelKeyPress); @@ -253,7 +244,7 @@ public static void InstallExitHandler () AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler (CurrentDomain_UnhandledException); // Add the event handler for System.Windows.Forms exit and thread exceptions: - //Application.ApplicationExit += new EventHandler (ExitEventHandler); + //Application.ApplicationExit += new EventHandler (ExitEventHandler); Application.ThreadException += new ThreadExceptionEventHandler (WinForms_ThreadException); Application.SetUnhandledExceptionMode (UnhandledExceptionMode.CatchException); diff --git a/test/BrowserTestClient/_startService.Mono.cmd b/test/BrowserTestClient/_startService.Mono.cmd index 8ca24f1..40a3ac0 100644 --- a/test/BrowserTestClient/_startService.Mono.cmd +++ b/test/BrowserTestClient/_startService.Mono.cmd @@ -1,3 +1,3 @@ -start ../ConsoleTestApps/Mono/bin/Debug/Test1.Service.exe +start ../ConsoleTestApps/Mono/bin/Release/Test1.Service.exe diff --git a/test/ConsoleTestApps/Mono/Test1.Service.cs b/test/ConsoleTestApps/Mono/Test1.Service.cs index 5833a4d..7374f43 100644 --- a/test/ConsoleTestApps/Mono/Test1.Service.cs +++ b/test/ConsoleTestApps/Mono/Test1.Service.cs @@ -26,7 +26,7 @@ static void Main (string[] args) tcpPort = 40001; } - ActorInput.DisableCatalogClient = true; // Test1.Client does not use Remact.Catalog.exe, but we publish this service anyway. + RemactConfigDefault.Instance.DisableCatalogClient = false; // Test1.Client does not use Remact.Catalog.exe, but we publish this service to the catalog anyway. Console.WriteLine ("Commandline arguments: ServiceInstance="+RemactConfigDefault.Instance.ApplicationInstance +" ServiceTcpPort="+tcpPort+"\r\n"); diff --git a/test/SpeedTestApp/Mono/_startTest2.cmd b/test/SpeedTestApp/Mono/_startTest2.cmd index d186063..c8c9eef 100644 --- a/test/SpeedTestApp/Mono/_startTest2.cmd +++ b/test/SpeedTestApp/Mono/_startTest2.cmd @@ -1,2 +1,3 @@ +start ../../../src/Remact.Catalog/bin/Release.Mono/Remact.Catalog.exe start bin/Release/Test2.Service.exe -start bin/Release/Test2.Client.exe \ No newline at end of file +start bin/Release/Test2.Client.exe diff --git a/test/SpeedTestApp/src/Client/FrmClient.Designer.cs b/test/SpeedTestApp/src/Client/FrmClient.Designer.cs index 4935e1d..6e24db8 100644 --- a/test/SpeedTestApp/src/Client/FrmClient.Designer.cs +++ b/test/SpeedTestApp/src/Client/FrmClient.Designer.cs @@ -158,7 +158,7 @@ private void InitializeComponent () this.tbCatalogHost.Name = "tbCatalogHost"; this.tbCatalogHost.Size = new System.Drawing.Size(86, 20); this.tbCatalogHost.TabIndex = 7; - this.tbCatalogHost.Text = "localhost:40002"; + this.tbCatalogHost.Text = "localhost"; // // FrmClient // diff --git a/test/SpeedTestApp/src/Client/FrmClient.cs b/test/SpeedTestApp/src/Client/FrmClient.cs index afbc1a6..f97aae0 100644 --- a/test/SpeedTestApp/src/Client/FrmClient.cs +++ b/test/SpeedTestApp/src/Client/FrmClient.cs @@ -117,9 +117,8 @@ private void Timer1_Tick (object sender, EventArgs e) tbService1.Text = string.Empty; lbService1.Text = string.Empty; lbState1.Text = "connecting ..."; - //Client1.LinkOutputToRemoteService (tbCatalogHost.Text, "Test2.Service"); - ActorInput.DisableCatalogClient = true; // prov. - Client1.Output.LinkOutputToRemoteService(new Uri("ws://" + tbCatalogHost.Text + "/Remact/Test2.Service")); + RemactConfigDefault.Instance.CatalogHost = tbCatalogHost.Text; + Client1.Output.LinkOutputToRemoteService ("Test2.Service"); Client1.TryConnect(); Client1.ResponseCount = 0; } diff --git a/test/SpeedTestApp/src/Service/FrmService.cs b/test/SpeedTestApp/src/Service/FrmService.cs index 020bb6b..3e43492 100644 --- a/test/SpeedTestApp/src/Service/FrmService.cs +++ b/test/SpeedTestApp/src/Service/FrmService.cs @@ -36,6 +36,7 @@ static void Main (string[] args) { RaLog.Exception ("Svc1: Fatal error", ex); } + RemactConfigDefault.Instance.Shutdown(); RaLog.Info( "Svc1", "Stop" ); RaLog.Stop (); @@ -58,7 +59,7 @@ public FrmService () InitializeComponent (); m_Service = new Test2Service (); - m_Service.Input.LinkInputToNetwork ("Test2.Service", 40002); + m_Service.Input.LinkInputToNetwork ("Test2.Service"); this.Text = m_Service.Input.AppIdentification; _milliseconds = Environment.TickCount; } diff --git a/test/SpeedTestApp/src/Service/Test2Service.cs b/test/SpeedTestApp/src/Service/Test2Service.cs index cb15cc9..a2953fb 100644 --- a/test/SpeedTestApp/src/Service/Test2Service.cs +++ b/test/SpeedTestApp/src/Service/Test2Service.cs @@ -59,7 +59,8 @@ public void OnConnectDisconnect(ActorMessage msg) // Remact service method for unknown messages void OnUnhandledRequest(ActorMessage msg) { - msg.SendResponse(new ErrorMessage(ErrorMessage.Code.ReqOrRspNotSerializableOnService)); + // will send ErrorMessage.Code.NotImplementedOnService + throw new NotImplementedException("unknown message on service "+msg.ToString()); }