Tutorial
So you think your project might benefit from some AOP approach. This tutorial introduces the Aspect# approach for AOP. Basically we are going to talk about the Aspect# built-in language for declaring aspects, mixins and interceptors.
The final sourcecode for this tutorial is included in the Aspect# distribution under the AspectSharpExample directory. It can be browsed in SVN here: http://svn.castleproject.org/svn/castle/trunk/AspectSharp/AspectSharp.Example/
The application starts in: AspectSharpExample/Application.cs
An hypothetical situation - not so much
To make things more interesting, lets suppose you're working on a specific application. You're creating a revolutionary Content Management System, and you probably end up with a big although nice and simple object model. The most important components implement IContentProvider and IView. IContentProvider is obviously responsible for gathering content from some source like database, Xml, RSS, Excel files and IView is responsible for displaying it in a specific way for a specific channel.
Everything is fine and your almost done with your four thousand ContentProviders and Views that cover all existing communications channels available in the world today. Suddenly, the sales guy - always blame the sales guy - comes, with from his standpoint, an non-important requirement. He needs security checking for providers and views, and he already sold it as done for an important customer, lets say the BBC. Your company's future depends on having it completed by the end of the day!
The available solutions
You need to expose your objects as a securable resource for your security framework. To ease the burden of having to change every single ContentProvider and View you think about a few possibilities:
- Create a property on IContentProvider and on IView exposing the IResource information.
- Create an 'IResourceable' and making IContentProvider and IView extend from it.. Then modify its base classes, although you're not sure whether such a base class exists.
- You're on the ninth floor, so jumping out the window could solve things for a while...
The problem with these possibilities is that they all will bungle your nice and clean object model. In your conception security doesn't have anything to do with content providers and views, but for some of them it makes sense. So, for those that make sense you'd like to introduce the ISecurityResource interface and implementation.
Well, c'mon! You only have a few hours, start to modify those components now! Not so fast, let's use AOP for it.
The Mixin solution
Nowadays, many AOP frameworks implement the mixin functionality. Though it's not really an AOP concept, while this is introducing something to a class of a set of classes, then it's all right, we can call it AOP.
The idea here is to make all ContentProvider in a given namespace implement the ISecurityResource interface with a valid implementation, of course. We can do this like this:
public class SecurityResourceImpl : ISecurityResource { public SecurityResourceImpl() { } public String ResourceName { get { return "Content"; } } }
Now we need to apply this to a particular class or to a set of classes in our project.
Describing your aspect configuration
We use a built-in language (Ruby like) to configure the aspects. You can keep this configuration in a external file, in your code (not recommended) or in the .config file associated with your application.
import YourCompany.CMS.ContentProviders in YourCompanyAssembly
aspect SecurityAspect for RSSContentProvider
include Mixins.SecurityResourceImpl in MyMixinsAssembly
end
This aspect targets the RSSContentProvider class and includes the SecurityResourceImpl class. What does it mean? Well, when you get your RSSContentProvider instance it will have the ISecurityResource interface implemented by the SecurityResourceImpl.
You mixed them, hence Mixin :-)
Instead of targeting a specific class, you can targets a set of classes like all the classes in the given namespace:
import YourCompany.CMS.ContentProviders in YourCompanyAssembly aspect SecurityAspect for [ YourCompany.CMS.ContentProviders ] include Mixins.SecurityResourceImpl in MyMixinsAssembly end
Now we need to create an Aspect# engine to do this magic:
StreamReader reader = new StreamReader( configfile ); AspectEngineBuilder builder = new AspectLanguageEngineBuilder(reader); AspectEngine engine = builder.Build(); RSSContentProvider provider = engine.Wrap( new RSSContentProvider() );But wait a minute! This is a very naive implementation of ISecurityResource. What if the security resource needs to access something from the content provider or the view? Gotcha
Not really. If your mixin needs to access the underlying component it must implement the IProxyAware interface:
public class SecurityResourceImpl : ISecurityResource, IProxyAware { private String _name; public SecurityResourceImpl() { } public void SetProxy(object proxy) { if (proxy is IContentProvider) { Name = (proxy as IContentProvider).Name; } else if (proxy is IView) { Name = (proxy as IView).Name; } } public String Name { get { return _name; } set { _name = value; } } public String ResourceName { get { return Name; } } }
Your mixin doesn't need to implement or expose anything, but if it does implement some interface then the Wrap'ed instance will expose them and forward the calls. Your mixin must have a default constructor, though.
Intercepting invocations
The most sensible method in IContentProvider is the RetrieveContent method, so for every content provider, which implements the ISecurityResource interface, it is a good idea to invoke the ISecurityResource.Demand() to fire all security checks.
Time to change the content provider code... well wait! Maybe Aspect# can help us implementing this check for us.
You're right! All we need to do is intercept the methods we want and perform the check. First we need a pointcut which will select the methods or properties. Within a pointcut you can add advices that will perform some action on the resulting Joinpoints.
What a lot of new words! Ok, so lets get things clear:
- Pointcut: selects methods and|or properties within a type (including the mixins)
- Joinpoints: method or properties that matched the pointcut
- Advice: some code that will be performed before|after the joinpoint.
Aspect# supports only one type of advice: MethodInterceptor. MethodInterceptors allow you to execute some code before and|or after a target method. Lets do it:
import YourCompany.CMS.ContentProviders in YourCompanyAssembly
import YourCompany.CMS.Aop.Interceptors
aspect SecurityAspect for RSSContentProvider
include Mixins.SecurityResourceImpl in MyMixinsAssembly
pointcut method(* RetrieveContent(*))
advice(SecurityCheckInterceptor)
end
end
Our pointcut states 'I don't care about the return value, just match all methods named RetrieveContent and I don't care about its arguments either'. So we don't have to worry about other methods being checked unnecessarily.
And now for something completely different: our MethodInterceptor implementation:
public class SecurityCheckInterceptor : IMethodInterceptor { public object Invoke(IMethodInvocation invocation) { ISecurityResource target = invocation.GetThis() as ISecurityResource; target.Demand(); // Can throw a SecurityException return invocation.Proceed(); // All right, get on with it } }
This implementation is pretty straightforward. Please note that the GetThis returns the proxy, so if you, for instance, invoke RetrieveContent within the interceptor then your interceptor will be called again and you can end up with a stack overflow.
Conclusion
Its easy to solve several common problems in an application with AOP. Aspect#
tries to ease the burden of using AOP and do it propertly. Now our two minute tutorial is over. If you're complaining that you spent more than two minutes reading this, well.. I'm a slow reader myself :-)
Where to go from here?
- [[AspectSharp Language Documentation]]
- [[AspectSharp Reference Documentation]]