Sample Device with Retry (Sending)

Sample Device with Retry (Sending)

This example shows the code for a sending device. These types of devices are dependent on a network resource which may have varying availability. In these cases, you typically want to retry sending your message many times before moving to the error queue. Generally, when a device depends on a resource being available, you want to have retry logic. Conversely, many devices have no dependencies, and there is no point in having retry logic since no variables will change between retries (and therefore the processing will never succeed).

Connexion uses retry logic in the built-in devices for network access, disk access, database access etc.

The following is a dummy device which illustrates the code for a sending device with retry logic (As of RC2). Please note that this will not compile - it's simply an example.

[DevicePlugin("Dummy Sender Device", "A description of the device goes here", DeviceDefinitionFlags.None, typeof(object), typeof(object), typeof(DummyDeviceConfigurationFactory))] public class DummySenderDevice : InnerHarbourBaseDevice<DummyDeviceConfiguration> { // our connection manager would be responsible for establishing the connection private ConnectionManager m_ConnectionManager; private Uri m_EndPoint; // sending devices often don't change the message. By returning false here, we can improve performance since no execution history needs to be recorded. public override bool DeviceChangesMessage { get { return false; } }   public override void Start() { if (string.IsNullOrWhiteSpace(Configuration.Destination)) throw new Exception("Destination is empty"); m_ConnectionManager = new ConnectionManager(Logger); m_EndPoint = new UriBuilder("net.tcp", Configuration.Destination, Configuration.Port, "/ConnexionV14/SomeServiceEndpoint.svc").Uri; RealtimeDeviceDisplayStatus.Set(string.Format("{0}:{1}", Configuration.Destination, Configuration.Port), Colors.White); } public override void Stop() { if(m_ConnectionManager != null) { m_ConnectionManager.Disconnect(); m_ConnectionManager = null; }   // remove the status information from the device icon RealtimeDeviceDisplayStatus.Set(string.Format("{0}:{1}", Configuration.Destination, Configuration.Port), Colors.White); }   // Use the 'async' ProcessMessage method if any of your method calls are async. Async uses resources more efficiently. public override async Task ProcessMessageAsync(IMessageContext context, CancellationToken cancellationToken) { await GetOrCreateConnectionAsync(); // send to the receiving system await m_ConnectionManager.SendMessageAsync(message); } private async Task GetOrCreateConnectionAsync() { if(m_ConnectionManager.IsConnected) return; // this throws if the connection is unavailable await m_ConnectionManager.ConnectAsync(new EndpointAddress(m_EndPoint)); // this will display some information overlayed on the device icon (showing that we're connected) RealtimeDeviceDisplayStatus.Set(string.Format("{0}:{1}", Configuration.Destination, Configuration.Port), Colors.LightGreen); }   // What action to take when the main ProcessMessageAsync method encounters an error public override void OnError(IMessageContext context, ErrorEventArgs args) { // close the current connection so it gets created on the next process message attempt m_ConnectionManager.Disconnect(); // retry? args.ShouldRetry = true;   // log a message to let the user know this message has been retried. In this case we'll retry at least once before // logging a message. if (args.TotalRetries > 0 && args.TotalRetries % 2 == 0) Logger.ErrorFormat(0, "Unable to send message (retry {0}): {1}", args.TotalRetries, args.Exception.Message);   // how long to wait before retrying the message args.SleepTime = TimeSpan.FromSeconds(args.TotalRetries > 2 ? 30 : 5); } }