There are times in just about any Software Architects’ or Lead Developers’ careers when they wish that they had enforced a pattern more effectively – usually this comes when they find some code that has been around for ages that, for example, accesses a Data Access Object directly from a Servlet, a JSF Managed Bean, or (*gulp*) a JSP page… It usually leads to about a week of brainstorming and discussing ways to do better code reviews, and promises that each line of code will be painstakingly inspected before each commit – usually these efforts fall flat before long…
Before I go any further, let me make it clear that nothing can take the place of solid processes that include code reviews at every level, but let’s face it, we don’t all have an infrastructure like Google that will support this level of rigor – in these cases, we’ll take all the help we can get, and there are ways that we can both prevent the situation listed above, and (more importantly) remind the project developers to think about what they’re doing before it's done… That’s primarily what the following Architectural Pattern is about – a little technical help to try and keep things in line until someone gets a chance to do that code review…
So here I present the Layering Enforcement pattern, the purpose of which is to help ensure that certain classes, or types of classes are only called in certain situations… there could be many places where this will be useful in a given project – the example that follows involves a situation where the Generic CRUD Components from a recent blog entry by Adam Bien should only be called by Repository classes, so that database access is confined to places where it is expected, rather than spread around the classes in the project…
Many different implementations and variations of this are possible – my choice here has been to model a simple user management system with EJB 3 components… in addition, we get some help from a custom Java 5 Annotation, and an EJB Interceptor – this example could just as effectively be implemented with a Spring-based framework as well (probably better, as the EJB 3 Interception functionality is pretty weak – this effort was just as much about proving that EJB Interceptors could be useful as it was about proving the pattern itself :) )…
First off, we have the CRUD Component – I take no credit for this, it is purely from an example by Adam Bien:
public interface GenericCrudService {
public Object create(Object t);
public Object find(Object id,Class type);
public void delete(Object t);
public Object update(Object t);
public Collection findByNamedQuery(String queryName);
public Collection findByNamedQuery(String queryName,int resultLimit);
}
And the implementation:
@Stateless
@Local(GenericCrudService.class)
@Interceptors(RepositoryChecker.class)
public class GenericCrudServiceBean implements GenericCrudService {
@PersistenceContext
private EntityManager em;
public Object create(Object t) {
this.em.persist(t);
return t;
}
@SuppressWarnings("unchecked")
public Object find(Object id, Class type) {
return (Object) this.em.find(type, id);
}
public void delete(Object t) {
t = this.em.merge(t);
this.em.remove(t);
}
public Object update(Object t) {
return this.em.merge(t);
}
public Collection findByNamedQuery(String queryName) {
return this.em.createNamedQuery(queryName).getResultList();
}
public Collection findByNamedQuery(String queryName, int resultLimit) {
return this.em.createNamedQuery(queryName).setMaxResults(resultLimit).getResultList();
}
}
Next, we need a way to identify that a class is a Repository – this may be done in any number of ways, including a properties file that lists each Repository class, a specific class naming pattern (i.e. – MyFavoriteRepository.class), but my favorite is to use a custom Annotation – this particular one has no ‘data’ attached to it, and could potentially be considered a Marker Annotation (thanks again, Adam)… at any rate, any class annotated with @Repository will be able to access the CRUD Component either directly or indirectly (It also serves as a self-documenting annotation, which is another great benefit):
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
}
Finally, we need our EJB 3 Interceptor – as you saw above, the GenericCrudServiceBean listed the ‘RepositoryChecker’ Interceptor… the implementation, listed below, will be executed ‘around’ all method calls against the business interface of the GenericCrudServiceBean – it will inspect the StackTrace on the current thread, and use reflection to try and find any class that is annotated with @Repository… as soon as it finds one, it will allow processing to continue – if it does not find one, it will throw an IllegalStateException, indicating the problem for the developer:
public class RepositoryChecker {
public RepositoryChecker() {
}
@AroundInvoke
public Object verifyRepositoryInStack(InvocationContext ctx) throws Exception {
List<StackTraceElement> trace = Arrays.asList(Thread.currentThread().getStackTrace());
boolean foundRepository = false;
for(StackTraceElement element: trace) {
Class currentClass = Class.forName(element.getClassName());
Repository rep = (Repository)currentClass.getAnnotation(Repository.class);
if(rep != null) {
foundRepository = true;
break;
}
}
if(foundRepository)
return ctx.proceed();
else
throw new IllegalStateException("The target class "+ctx.getTarget().getClass().getName()+" must be called by a class marked with @Repository");
}
}
As I said, there are many ways that this could be implemented – I experimented with the idea of using another Interceptor that would place a variable into the InvocationContext when executing around a class marked with @Repository, and simply looking for the existence of that variable in the RepositoryChecker class, but decided against it, as I would need to either explicitly add the new Interceptor on each Repository class, or add it to all EJB 3 components (I could use it instead of @Repository, but I like the self-documenting nature of the annotation)… This alternative implementation would be simpler to build with Spring, as you could use a pointcut to specifically intercept calls on classes with the @Repository annotation…
As for using reflection for this solution – it may become a concern from a performance point of view, but I’m not inclined to worry about it unless there is a real problem… we’re not likely to be spending enormous amounts of time here, and if we are seeing a problem, then we can simply turn off the Interceptor in our staging and production environments, but leave it on for development (or integration, or whatever you call it) – we still get all of the benefits of the pattern (assuming that you actually use the development environment :) ), and most importantly, any developers that break the layering rule will hit it on their own machines, which will make them think twice about what they’re trying to do – ultimately, this is where the most value comes from when using this pattern, as it will help your team to better understand the patterns and policies that are in place on the project…
M
7 comments:
Nice work.
I had a similar problem and came up with a different solution.
I started a project called Architecture Rules which extends JUnit and jdepend.
This test suite leverages an xml configuration file to test your code's architecture via unit tests.
It is able to assert that specific packages do not depend on each other and able to check for cyclic redundancies among your packages.
Its open and hosted by google code, if anyone wants to try it out and or contribute. Its in a beta stage in that it works, but hasn't been used by any individuals other than myself, so it still needs testing. The documentation is pretty good too.
Architecture Rules
I've seen tools like that before as well, and they can certainly be used for similar purposes... Is Architecture Rules a runtime tool, or would it be used during testing only? In some cases, I could see the runtime capabilities being advantageous, although most projects could likely get on fine without it...
I do like the flexibility that an annotations-based techniques gives you, though -- using packages to determine this type of thing is extremely rigid, and sometimes I think it makes sense to have a simple 'domain' package, which might hold your domain objects, service objects, repository objects, etc... if that's the case, then a package-based solution won't do the job, but Annotations could do the trick nicely...
Thanks for the pointer, though, I will be checking that product out as well...
M
Matt and Mike, thanks for the nice tips...
One reason for preferring the runtime check is the ability to do dynamic checking to catch cases where classes and objects are created on the fly (suing forName() rather than new()).
Regarding the performance overhead, I think that it should be possible to optimize by using some form of a cache or a lookup table. In this case, it would be enough to check for class names, rather than creating the class and checking for annotations (this might be only bring a marginal improvement since I guess that the biggest hit will be in capturing the stack trace.
Architecture Rules runs as a unit test, so that a continuous integration instance would pickup the problem, and because everyone is familiar with unit tests.
Architecture Rules
Looking at this type of problem, it appears that a more appropriate and efficient solution would be to use a static analysis tool like FindBugs or PMD (e.g. build a custom rule that detects this type of pattern). The thing is that a violation of this architecture pattern is quite obvious and is well known at compile time (well in advance of runtime). In a way, when you enable this type of checking at runtime, other than in the development environment, there is really nothing to be done - you're probably not going to stop the application and say "This is not going to run until you apply the pattern. Until then, no orders will be processed".
Good point on PMD -- at any rate, I would not recommend having this filter active in any environment other than Development, for exactly the reason you specify... most frameworks that this could be implemented with (Spring, EJB3, etc) can be turned off with a configuration change...
Assuming you have some sort of processing in place, though, any broken functionality would be found much earlier than your production deployment
M
Great and Useful Article.
J2EE Training
Java EE course
Java EE training
J2EE training in chennai
Java J2EE Training Institutes in Chennai
Java J2EE Training in Chennai
Java EE training
Java Interview Questions
Post a Comment