In this post I will be considering applying business logic transparently thru interception for some specific scenarios and discuss the pros and cons of such an approach.
Let’s say that you have a content-management system (CMS) and a forum application, the CMS being written in Java. The CMS is used for managing an online paper and the forum is used for managing a community of users interested in your content.
This is the original CMS model:
public interface PublishingBean{
// this is the method that publishes an item
public void publishItem(Item item) throws PublishingException;
}
public class DefaultPublishingBean implements PublishingBean{
public void publishItem(Item item) throws PublishingException{
// format the item
formatItem(item);// save it hard-disk
saveItem(item);
// syndicate it
populateRSSFeed(item);
}
// some formatting: adding menus, putting up banners, etc…
protected void formatItem(Item) throws PublishingException{
// format the item
}
}
DefaultPublishingBean is registered in Spring to implement the publishing bean:
<bean id=”publishingBean” class=”DefaultPublishingBean”/>
Now let’s say that you want to integrate the CMS with the forum. The integration will be very simple, at the end of each item you may or may not put a link which would create or update a thread on the forum related to that item. Part of this integration is a new publishing bean which contains logic interacting with the forum:
public class ForumPublishingBean implements PublishingBean extends DefaultPublishingBean{
// this method prepares the bean so that it doesn’t display the link to the forum
public void donotDisplayForumLink (boolean bool){
// set a flag
}// this method prepares the bean so that it links to the forum in ‘moderated’ mode
public void displayModeratedForumLink(boolean bool){
// set a flag
}
protected void formatItem(Item) throws PublishingException{
// if ‘do not display forum link’ mode is on then do not display the item
// if ‘display moderated forum link’ mode is on then put-up then display a moderated link
// else display the link at the bottom of the item.
}
}
ForumPublishingBean just needs to re-format the item, it delegates saving to disk, syndication, etc… to the super-class because this functionality didn’t change.
Right now the ForumPublishingBean replaces DefaultPublishingBean for publishing beans by the means of this bean definition:
<bean id=”publishingBean” class=”ForumPublishingBean”/>
OK, this was the set-up, now we are ready to start on the topic of this post. Everything is running OK, editors are happy,the community keeps talking on the forum when, all of a sudden, you hire Ann Coulter and Charles Kas an editor ;-), both in the ‘Politics’ section of your newspaper.
The result of hiring Ann is that a flame war starts raging on the forum any time Ann posts an article. The business starts getting fidgety and decide that Ann’s articles should not have the link towards the forum, this would limit the flames. This is business requirement #1: that Ann’s, and only Ann’s, articles should not provide a link towards the forum.
Next, Charles is not the flamethrower that Ann is, but he is not small potatoes either, as a result a lot of his items are getting flamed on the forum. The business decides that Charles’s articles should link to the forum in ‘moderated’ mode. This is business requirement #2.
Next, let’s leave politics aside for a while ;-), Vista is about to launch and the technology section of the paper is getting flamed on the forum by both Linux and MS die-hards. The business decides that for a while every article containing the words Vista should have moderated links to the forum and it doesn’t know when this rule will expire. This is business requirement #3.
So you have 3 requirements, quite different one from the other, very limited in scope and which should not pollute in any way either the CMS or the forum’s code base. Moreover, you are not sure in what direction these requirements will evolve, they are pretty specific. Do these requirements actually mean that you need to integrate the authors into the forum-CMS integration? Or do they mean that some sections, ‘Politics’ in this case, need moderation? It is pretty early to draw any assumptions from these business requirements because they are very poor.
One way to implement these business requirements without making any broader assumptions is to intercept the calls to the Publishing Bean and inject this very specific business logic. This could be done with 3 around advices as shown below:
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
// this interceptor will check if the author of the Item that is getting published
// should have its forum link removed and remove its forum link
public class class BlockForumLinkByAuthorInterceptor implements MethodInterceptor{
private List blockedAuthors = new ArrayList();
public void setBlockedAuthors(List blockedAuthors){..}public Object invoke(MethodInvocation invocation){
// get the cmsItem that is getting published
Item cmsItem = (Item)invocation.getArguments()[0];
if(blockedAuthors.contains(cmsItem.getAuthor())){
ForumPublishingBean publishBean = (ForumPublishingBean)invocation.getThis();
// block this author’s forum link
publishBean.donotDisplayForumLink(true);// publish the item
publishBean.publish(cmsItem);
}else{
// go ahead with the original method
invocation.proceed();
}
}
}
// this interceptor will check if the author of the Item that is getting published
// should have its forum link moderated and moderate it
public class class ModerateForumLinkByAuthorInterceptor implements MethodInterceptor{
private List moderatedAuthors = new ArrayList();
public void setModeratedAuthors(List authors){..}public Object invoke(MethodInvocation invocation){
// get the cmsItem that is getting published
Item cmsItem = (Item)invocation.getArguments()[0];
if(moderatedAuthors.contains(cmsItem.getAuthor())){
ForumPublishingBean publishBean = (ForumPublishingBean)invocation.getThis();
// moderate this author’s forum link
publishBean.displayModeratedForumLink(true);// publish the item
publishBean.publish(cmsItem);
}else{
// go ahead with the original method
invocation.proceed();
}
}
}
// this interceptor will check if the section of the Item that is getting published
// should have its forum link moderated and moderate its forum link
public class class ModerateForumLinkByKeywordsInterceptor implements MethodInterceptor{
private List moderatedKeywords = new ArrayList();
public void setModeratedKeywords(List moderatedKeywords){..}public Object invoke(MethodInvocation invocation){
boolean moderateFlag = false;
// get the cmsItem that is getting published
Item cmsItem = (Item)invocation.getArguments()[0];
foreach(moderatedKeyword in moderatedKeywords){
if(cmsItem.getText().indexOf(moderatedKeyword) != -1){
moderateFlag = true;
}
}
if(moderateFlag){
ForumPublishingBean publishBean = (ForumPublishingBean)invocation.getThis();
// moderate this story’s forum link
publishBean.displayModeratedForumLink(true);
// publish the item
publishBean.publish(cmsItem);
}else{
// go ahead with the original method
invocation.proceed();
}
}
}
The above examples are woven into the application via this XML configuration:
<!– The previous bean becomes the target of the advices above –>
<bean id=”publishingBeanTarget” class=”ForumPublishingBean”/>
<!– Register the 3 advices –>
<bean id=”blockForumLinkByAuthorInterceptor” class=”BlockForumLinkByAuthorInterceptor”>
<property name=”blockedAuthors” value=”Ann Coulter”/>
</bean>
<bean id=”moderateForumLinkByAuthorInterceptor” class=”ModerateForumLinkByAuthorInterceptor”>
<property name=”moderatedAuthors” value=”Charles Krauthammer”/>
</bean>
<bean id=”moderateForumLinkByKeywordsInterceptor” class=”ModerateForumLinkByKeywordsInterceptor”>
<property name=”moderatedKeywords” value=”Vista”/>
</bean>
<!– Set-up the proxy which replaces the original ForumPublishingBean –>
<bean id=”publishingBean” class=”com,springframework.aop.framework.ProxyFactoryBean”>
<property name=”proxyInterfaces” value=”ForumPublishingBean”/>
<property name=”interceptorNames”>
<list>
<value>blockForumLinkByAuthorInterceptor</value>
<value>moderateForumLinkByAuthorInterceptor</value>
<value>moderateForumLinkByKeywordsInterceptor</value>
</list>
</property>
<property name=”target”>
<!– point this proxy to the original ForumPublishingBean –>
<ref=”publishingBeanTarget”/>
</property>
</bean>
And this is pretty much it. The code above was using around advices, depending on the use-case you could have used other types of advices. Well, so we have implemented these very specific business rules transparently and without any intrusion into the application’s original code, which is not too bad. Now comes the hard part: when should you take this approach? (You know, the hard question is not how, but rather why ;-))
I think this approach should be taken when you are given some special business rules which you cannot generalize. In the above example it was pretty hard to determine what was driving the editing of the forum links: the author’s type, the words used in the CMS Item, etc… In a sense this approach buys you time during which you can watch the demand for a particular business requirement grow or stagnate and then decide whether to make an investement in handling it or not. In our example the interceptors that moderates forum links by author lets you see if this behavior is needed before modeling and implementing it: if you have 20 authors that need to be moderated then it probably pays off to model the moderation process, if you have only one it doesn’t make sense to invest time in it.
You could also take this approach when you don’t see the benefit of setting up a whole infrastructure in order to implement this business rules, when for example it doesn’t make sense to change the user management module in order to change the behavior of 2 or 3 users. In my experience I had a few cases where I needed to change a whole user module because one user was not supposed to click on a button, I wish I could have simply intercepted his calls and inject this very specific behavior.
You could also use this approach if you need to implement a simple business requirement that is set to expire soon. In our case we decided to moderate every CMS item that contains the word Vista in it. This business rule will probably be dropped in a few months once the excitement over Vista wears off. It doesn’t make sense to make a significant investment of code into something that will expire so soon, if you can get by with a small hack you may consider it. The beauty of this example is that you can shed this hack very easily when the need for it expires .
You should note that this example doesn’t scale very well and that it uses values hard-coded in configuration files. If this causes problems in your application, then probably you should not follow this example. Getting into scale problems is actually an indication of the fact that this interception framework is not servicing your needs anymore and that you should probably do something different about implementing this requirement.
One potential source of problems is the mushrooming of these business-related interceptors. Mushrooming code usually points to bad design, mushrooming interceptors points pretty much to the same thing. You should watch out if these interceptors are getting out of whack and once a certain threshold is met find a way to consolidate them, probably by making some generalizations about their roles.
One interesting thing about these interceptors is that they are not cross-cutting concerns, rather they are very specific about their target. I initially thought about using the title ‘Customizing applications transparently thru AOP’ but then I realized that I am not talking about aspects (which are cross-cutting concerns), but about very specific joinpoints.
Another interesting thing is that these are business-related interceptors and not infrastructure interceptors.
Well, this concludes this post. I wanted to have more time to talk about this, but I do not have the time. Feel free to drop some comments, I find this type of development pretty interesting.
P.S. I chose Ann Coulter and Charles Krauthammer because I needed an example of a business concern very limited in scope yet very important to the business. I didn’t have time to cook-up a better example so I just thought this one out. It is a bit off the wall, but again, I simply didn’t have the time. I chose some controversial authors at random in order to set-up a very specific business rule.
Later Edit As far as I am concerned I think the this approach is just another way of getting your development cycle closer to the business needs. This approach avoids building a whole infrastructure for very small business requirements and rather it lets you watch these business requirements grow before making an IT investment in them.