1
votes

Is it possible whilst an application (of any type e.g. console, web, web role in azure etc.) to switch castle windsor interceptors on or off to avoid an application restart?

My scenario is to use an Interceptor like that below, to log method entry and exit with arguments.

public class ExampleInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Debug.WriteLine(string.Format("Before method: {0}", invocation.Method.Name));
        invocation.Proceed();
        Debug.WriteLine(string.Format("After method: {0}", invocation.Method.Name));
    }
}

My initial approach was to do something like this:

public class InterceptorSelector : IModelInterceptorsSelector
{
    public static bool InterceptorOn = false;

    public bool HasInterceptors(ComponentModel model)
    {
        return
            typeof(ExampleInterceptor) != model.Implementation
            && InterceptorOn
            && model.Implementation.Namespace.StartsWith("MvcApplication1");
    }

    public InterceptorReference[] SelectInterceptors(ComponentModel model, InterceptorReference[] interceptors)
    {
        return new[]
        {
            InterceptorReference.ForType<ExampleInterceptor>()
        };
    }
}

However I found that once the interceptor has handled a method once, there must be some internal caching (for performance benefit no doubt) as the method does not get hit again in the InterceptorSelector.

The primary use case I have for this is a WebApi hosted in azure serving hundreds (if not 1000s) of requests per second over multiple VM instances. In the event of needing to debug the live environment I want to enable verbose logging on one of these instances but at the same time not want to take the VM instance out of action when enabling this logging.

3
Did you check that both methods HasInerceptors, but also SelectInterceptors are not called ? I would expect that the latter is called on every creation of a component. I thought that the first method is mererly called once, so that windsor will not call the second for components that never need interceptors selected.Marwijn

3 Answers

1
votes

Switching logic in runtime can be easily implemented with configuration switch or you can do it like K.Kozmic as shown in his tutorial on dynamic proxy here.

0
votes

You could create a static event which you'll suscribe the interceptors to, then disabling the interceptors would be done by manipulating the controller class

public class ChangeInterceptorSettingsEventArgs : EventArgs {}

public class MyInterceptorsController
{
    public delegate void ChangeInterceptorSettingsEvent(ChangeInterceptorSettingsEventArgs args);
    public static event ChangeInterceptorSettingsEvent ChangeInterceptorSettings;

    public void ChangeInterceptorsSettings(ChangeInterceptorSettingsEventArgs args)
    {
        if (ChangeInterceptorSettings != null)
        {
            ChangeInterceptorSettings(args);
        }
    }
}

Then create the interceptor and register an event handler

public class ExampleInterceptor : IInterceptor
{
    public bool WriteThings { get; set; }
    public ExampleInterceptor()
    {
        MyInterceptorsController.ChangeInterceptorSettings += MyInterceptorsController_ChangeInterceptorSettings;
    }

    void MyInterceptorsController_ChangeInterceptorSettings(ChangeInterceptorSettingsEventArgs args)
    {
        WriteThings = !WriteThings; // this is not very smart, 
        // but I don't want to populate the args in this example
    }

    public void Intercept(IInvocation invocation)
    {
        if (WriteThings) {Debug.WriteLine(string.Format("Before method: {0}", invocation.Method.Name));}
        invocation.Proceed();
        if (WriteThings) {Debug.WriteLine(string.Format("After method: {0}", invocation.Method.Name));}
    }
}

Finally to use it, just call the event from the controller

MyInterceptorsController.ChangeInterceptorsSettings(new ChangeInterceptorSetingsEventArgs())
// populate the args parameters with what you need

So, if you want to go and rip some interceptors out of the object you have resolved, you could always get the private field that contains the interceptors and change it at runtime. I don't really recommend that before making sure that the existence of an interceptor has such a performance hit:

resolvedObject
    .GetType()
    .GetField("__interceptors")
    .SetValue(resolvedObject, new IInterceptor[0]);
// or whatever interceptor array you want to set
0
votes

You can exclude/add interceptors at runtime this way:

public class MyController : Controller
{
    public IResolver iResolver {get;set;}
    public IService someService {get;set;}  

    private UsageMethod()
    {
        ExcludeInterceptors(someService, new [] {typeof(Interceptor1),typeof(Interceptor2)};
        AddInterceptor(someService, typeof(Interceptor1), 0};
    }

    private Castle.DynamicProxy.IInterceptor[] getInterceptorsField(object service)
    {
        var field = service.GetType().GetField("__interceptors");
        return field.GetValue(service) as Castle.DynamicProxy.IInterceptor[];
    }

    private void ExcudeInterceptors(object service, params Type[] interceptorTypes2exclude)
    {           
        var newInterceptors = getInterceptorsField(service).Where(x => !interceptorTypes2exclude.Contains(x.GetType())).ToArray();
        field.SetValue(service, newInterceptors);
    }

    private void AddInterceptor(object service, Castle.DynamicProxy.IInterceptor interceptorType2add, int position)
    {           
        var newInterceptors = getInterceptorsField(service).ToList();
        newInterceptors.Insert(position, iResolver.Resolve(interceptorType2add));
        field.SetValue(service, newInterceptors.ToArray());
    }
}

P.S. Was tested with Castle.Windsor 3.3.0