I’m In Your Objects, Intercepting Your Calls
I have an interface.
I’m sure you have some interfaces as well. If you don’t use interfaces you’re probably doing it wrong anyway.
My interface is a bit of a monolith, which happens sometimes. It’s not so big that I can justify investing the time in splitting it apart, but it’s large enough that its hard to implement and just kind of feels wrong. I don’t need to implement this interface manually very often (yay for mocking frameworks like NSubstitute) so I can live with it for now. Not everything can be perfect alas.
This particular interface allows a user to access a RESTful web service, and comes with some supporting data transfer objects.
I recently had the desire/need to see what would happen to the user experience of an application using this interface (and its default implementation) if the internet connection was slow, i.e. the calls to the service were delayed or timed out.
Obviously I could have implemented the interface with a wrapper and manually added slowdown/timeout functionality. As I mentioned previously though, there were enough methods in this interface that that sounded like a time consuming proposition. Not only that, but it would mean I would be tightly coupled to the interface, just to introduce some trivial slowdown code. If the interface changed, I would need to change my slowdown code. That’s bad, as the functionality of my code is distinctly separate from the functionality of the interface, and should be able to reuse that (admittedly trivial) code anywhere I like.
Plus I’m a lazy programmer. I’ll always go out of my way to write as little code as possible.
Aspect Oriented Programming
What I wanted to do was to be able to describe some behaviour about all of the calls on my interface, without actually having to manually write the code myself.
Luckily this concept has already been invented by people much smarter than me. Its generally referred to as Aspect Oriented Programming (AOP). There’s a lot more to AOP than just adding functionality unobtrusively to calls through an interface, but fundamentally it is about supporting cross cutting concerns (logging, security, throttling, auditing, etc) without having to rewrite the same code over and over again.
In this particular case I leveraged the IInterceptor interface supplied by the Castle.DynamicProxy framework. Castle.DynamicProxy is included in the Castle.Core package, and is part of the overarching Castle Project. It is a utility library for generating proxies for abstract classes and interfaces and is used by Ninject and NSubstitute, as well as other Dependency Injection and mocking/substitution frameworks.
Castle.DynamicProxy provides an interface called IInterceptor.
public interface IInterceptor { void Intercept(IInvocation invocation); }
Of course, that definition doesn’t make a lot of sense without the IInvocation interface (trimmed of all comments for brevity).
public interface IInvocation { object[] Arguments { get; } Type[] GenericArguments { get; } object InvocationTarget { get; } MethodInfo Method { get; } MethodInfo MethodInvocationTarget { get; } object Proxy { get; } object ReturnValue { get; set; } Type TargetType { get; } object GetArgumentValue(int index); MethodInfo GetConcreteMethod(); MethodInfo GetConcreteMethodInvocationTarget(); void Proceed(); void SetArgumentValue(int index, object value); }
You can see from the above definition that the IInvocation provides information about the method that is being called, along with a mechanism to actually call the method (Proceed).
You can implement this interface to do whatever you want, so I implemented an interceptor that slowed down all method calls by some configurable amount. You can then use your interceptor whenever you create a proxy class.
public class DelayingInterceptor : IInterceptor { private static readonly TimeSpan _Delay = TimeSpan.FromSeconds(5); public DelayingInterceptor(ILog log) { _Log = log; } private readonly ILog _Log; public void Intercept(IInvocation invocation) { _Log.DebugFormat("Slowing down invocation of [{0}] by [{1}] milliseconds.", invocation.Method.Name, _Delay.TotalMilliseconds); Thread.Sleep(_Delay); invocation.Proceed(); } }
Proxy classes are fantastic. Essentially they are automatically implemented wrappers for an interface, often with a concrete implementation to wrap supplied during construction. When you create a proxy you can choose to supply an interceptor that will be automatically executed whenever a method call is made on the interface.
This example code shows how easy it is to setup a proxy for the fictitious IFoo class, and delay all calls made to its methods by the amount described above.
IFoo toWrap = new Foo(); var generator = new ProxyGenerator(); var interceptor = new DelayingInterceptor(log4net.LogManager.GetLogger("TEST")); var proxy = generator.CreateInterfaceProxyWithTarget(toWrap, interceptor);
As long as you are talking in interfaces (or at the very least abstract classes) you can do just about anything!
Stealthy Interception
If you use Ninject, it offers the ability to add interceptors to any binding automatically using the optional Ninject.Extensions.Interception library.
You still have to implement IInterceptor, but you don’t have to manually create a proxy yourself.
In my case, I wasn’t able to leverage Ninject (even though the application was already using it), as I was already using a Factory that had some logic in it. This stopped me from simply using Ninject bindings for the places where I was using the interface. I can see Ninjects support for interception being very useful though, now that I understand how interceptors work. In fact, since my slowdown interceptor is very generic, I could conceivably experiment with slowdowns at various levels in the application, from disk writes to background processes, just to see what happens. Its always nice to have that sort of power to see how your application will actually react when things are going wrong.
Other Ways
I’m honestly not entirely sure if Interceptors fit the classic definition of Aspect Oriented Programming. They do allow you to implement cross cutting concerns (like my slowdown), but I generally see AOP referred to in the context of code-weaving.
Code-weaving is where code is automatically added into your classes at compile time. You can use this to automatically add boilerplate code like null checking on constructor arguments as whatnot without having to write the code yourself. Just describe that you want the parameters to be null checked and the code will be added at compile time. I’m not overly fond of this approach personally, as I like having the code in source control represent reality. I can imagine using code-weaving might lead to situations where it is more difficult to debug the code because the source doesn’t line up with the compiled artefacts.
I don’t have any experience here, I’m just mentioning it for completeness.
Conclusion
In cases where you need to be able to describe some generic piece of code that occurs for all method calls of an interface, Interceptors are fantastic. They raise the level that you are coding at in my opinion, moving beyond writing code that directly tells the computer what to do and into describing the behaviour that you want. This leads to less code that needs to be maintained and less hard couplings the interfaces (as you would get if you implemented the wrapper yourself). Kind of like using an IoC container with your tests (enabling you to freely change your constructor without getting compile errors) you can freely change your interface and not have it impact your interceptors.
I’m already thinking of other ways in which I can leverage interceptors. One that immediately comes to mind is logging calls to the service, and timing how long they take, which is invaluable when investigating issues at the users end and for monitoring performance.
Interceptors provide great modular and decoupled way to accomplish certain cross cutting concerns, and I’m going to try and find more ways to leverage them in the future now that I know they exist.
You should to.
Unless you aren’t using interfaces.
Then you’ve got bigger problems.