Hi Nazariy,
I was able to remove the default ErrorAction and replace it with my own without changing the RSB code base. I'll show you the code below, but first a caveat: this code is for RSB 1.9. I know there has been a lot of work done since then on containers, etc, so some of this may not work exactly the same way depending on what version you are using.
First, I had to create a new DefaultHost so I can change the way the container gets configured. The methods in the DefaultHost are private, so I just copied the DefaultHost code into a new class. When you start up RSB, you now have specify this new class as the host: /Host:"MagicBus.Utils.RhinoServiceBus.MyDefaultHost,MagicBus.Utils". The only code that needs to change in the class is in the InitializeContainer() method:
private void InitializeContainer()
{
bootStrapper.InitializeContainer(container);
if (hostConfiguration != null)
container.Kernel.ConfigurationStore.AddFacilityConfiguration("rhino.esb", hostConfiguration);
// This is the only line which is different than the DefaultHost that comes with RSB.
var facility = new MyServiceBusFacility();
bootStrapper.ConfigureBusFacility(facility);
container.Kernel.AddFacility("rhino.esb", facility);
}
MyDefaultHost will now be configured with MyServiceBusFacility instead of the default implementation. MyServiceBusFacility inherits from RhinoSerivceBusFacility to modify some configuration to include your custom ErrorAction. First, I copied the Init method (actually from the AbstractRhinoServiceBusFacility) and change the line:
Kernel.Register(
AllTypes.FromAssembly(typeof(IBusConfigurationAware).Assembly)
.BasedOn<IBusConfigurationAware>()
);
To:
// Register our custom MsmqTransport configuration and exclude the
// version provided by RSB
Kernel.Register(
Component.For<IBusConfigurationAware>().ImplementedBy<MyMsmqTransportConfigurationAware>(),
AllTypes.FromAssembly(typeof (IBusConfigurationAware).Assembly)
.BasedOn<IBusConfigurationAware>()
.Unless(x => x == typeof (MsmqTransportConfigurationAware)));
You will have to override ReadConfiguration() as well. Be sure to call base.ReadConfiguration first, then add any additional logic to Register the configuration information in the Kernel:
/* Do stuff to set up your configuration here */
// Register the custom error action here so it will be
// first in the registrations and therefore the first
// to receive the events we care about later
Kernel.Register(
Component.For<IMsmqTransportAction>()
.ImplementedBy<ErrorHandlingAction>()
.DependsOn(new
{
numberOfRetries = NumberOfRetries,
messageOwners,
BusEndpoint = Endpoint.OriginalString
}));
The code above will register ErrorHandlingAction to handle errors in place of the default Action in RSB. My custom error handling will send an email to alert that an error has occurred.
I went farther with my changes to configure the MSMQTransport to handle retries based on configuration values. I had to make the following changes so that I could override the default behavior of using the numberOfRetries from the bus configuration. You may or may not need to do this, depending on what you are trying to accomplish.
I created MyMsmqTransportConfigurationAware by copying the MsmqTransportConfigurationAware class and changing this:
Component.For<ITransport>()
.LifeStyle.Is(LifestyleType.Singleton)
.ImplementedBy(typeof(MsmqTransport))
.DependsOn(new
{
threadCount = facility.ThreadCount,
endpoint = facility.Endpoint,
queueIsolationLevel = facility.IsolationLevel,
numberOfRetries = facility.NumberOfRetries,
transactional = facility.Transactional,
consumeInTransaction = facility.ConsumeInTransaction,
}),
To:
Component.For<ITransport>()
.LifeStyle.Is(LifestyleType.Singleton)
.ImplementedBy(typeof(MyMsmqTransport))
.DependsOn(new
{
threadCount = facility.ThreadCount,
endpoint = facility.Endpoint,
queueIsolationLevel = facility.IsolationLevel,
numberOfRetries = facility.NumberOfRetries,
transactional = facility.Transactional,
consumeInTransaction = facility.ConsumeInTransaction,
}),
MyMsmqTransport is what determines what to do with an errored message: retry or move to the Errors queue. I'm only using MSMQ for transport, so I ignored other transports for my case.
In the finally{} block of the ProcessMessage method I made changes to determine how often to retry the error, and how long the delay should be before retrying the message.
It took me a while to get this all working right. I learned a lot about RSB, and Castle configuration along the way. I had to copy code and create my own classes in places where the original code didn't allow access to its methods to inherit from the class, or override the methods. I suspect there are more elegant ways to accomplish some of what I did using better configuration of the container, or something similar. I would be happy to see improvements in this.
I hope it helps. I'm happy to provide more detail if you need it.
-Scott
On Friday, October 12, 2012 2:14:22 AM UTC-6, Nazariy Kotylo wrote:
Hi Scott
Do you know if it is possible to replace the ErrorAction with a custom one without modifying the RhinoServiceBus source code?
Пʼятниця, 24 серпня 2012 р. 19:18:53 UTC+3 користувач Scott написав: