Recently reading the Web Beans Manifesto, I was intrigued by the use of stereotypes through annotation. Stereotypes are not a new concept. In fact the UML used them as extensibility mechanisms for defining new model elements that have specific properties suitable to your problem domain. Sound familiar?

The following diagram demonstrates some typical stereotypes:

uml-stereotype-example

Obviously, this indicates certain semantics specific to each class we see in the diagram. Generally the semantics of the stereotype are described in some appended documentation and have to be incorporated into the code through patterns.

In the diagram above, you see an example of an existing Java stereotype Entity. Classes can simply be annotated with @Entity to gain the cross-cutting behavior associated with Entities. In addition, controllers are generally annotated as @Stateful or @Stateless, which also implies cross-cutting behavior (transactional, state maintenance, etc.). The difficulty has been implementing domain-specific stereotypes.

Up to this point, there has been no simple approach to implementing this concept in a domain-specific setting with Java. Sure, you could use inheritance along with the Template method pattern or other patterns to capture commonalties in components, but there was no simple way to inject the cross-cutting behavior that a stereotype implies. Through the Web Beans stereotype approach this behavior can be specified declaratively, just as it is within the model and the properties can be applied through interception.

So building on Gavin’s singleton example, for the <<dao>> shown in the example above, we could specify the following:

@Singleton
@Transactional
@Stereotype(requiredTypes=AbstractDAO.class)
@Target(TYPE)
@Retention(RUNTIME)
public @interface DAO {}

Now our DAO would be implemented as follows:

public @DAO CourseDAO extends AbstractDAO

No additional configuration necessary as the interceptors will take care of our transactions and ensure that the instance is singleton. Implementation of patterns suddenly becomes simple, even for junior developers.

Now lets discuss the requirements associated with the RegistrationAction. There are likely to be a number of methods in the RegistrationAction that are typical to the domain as well. For example, the registerStudent() method is essentially a commit of the changes by the user. What if we would like to audit any commits by the user?

We can first create our domain-specific stereotype, again building on Gavin’s example:

@ConversationScoped
@Named
@Secure
@Auditable
@Transactional
@Component
@Stereotype(requiredTypes=BaseAction.class)
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action {}

Here we’ve specified @Auditable which is a domain-specific requirement. Now we can implement our class:

public @Action class RegistrationAction extends BaseAction {
  ...
  @Audit
  public void register(Student, Course)
      throws CourseFullException {
    // check that course is not full,
    // perform registration, and invoke DAO
  }
  ...

Here you notice we annotated the register method with @Audit. Since our @Action is @Auditable, we can specify which methods we want to @Audit and the interceptor will take care of the auditing for us.

So you say, this is great, but I want this behavior today! Well, the AuditInterceptor is actually very simple to implement with Seam. Here is annotation definition:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Interceptors(AuditInterceptor.class)
@Inherited
public @interface Auditable {}

and the interceptor would look something like:

@Interceptor
public class AuditInterceptor implements Serializable {
  ...
  @AroundInvoke
  public Object aroundInvoke(InvocationContext invocation)
    throws Exception
  {
    	Method method = invocation.getMethod(); 

	if(method.isAnnotationPresent(Audit.class))
	{
		// handle auditing and proceed with
                // invocation and return results
	}
  }
  ...

As an improvement we could also add an exception handling interceptor that automatically performs our business exception handling and displays the appropriate messages to the user:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Interceptors(ExceptionMessagingInterceptor.class)
@Inherited
public @interface ExceptionMessaging {}

and the interceptor would look something like:

@Interceptor
public class ExceptionMessagingInterceptor
    implements Serializable {
  ...
  @AroundInvoke
  public Object aroundInvoke(InvocationContext invocation)
    throws Exception
  {
        Method method = invocation.getMethod(); 

        try
        {
              invocation.proceed();
        }
        catch(BusinessException e)
        {
            FacesMessages.instance()
              .addMessage(e.getMessage());
        }
  }
  ...

In today’s world with Seam you can simply annotate your base class with the appropriate annotations:

@Scope(ScopeType.CONVERSATION)
@Auditable @ExceptionMessaging
public class BaseAction implements Serializable {
  ...

Simply ensure that all of your custom annotations are @Inherited and now all domain-specific requirements will be handled by your custom interceptors. In addition, should you migrate to the WebBeans approach once the specification is complete, this makes it simple to swap out the necessary annotations.

So what does all this buy us? Well for one, the implementation speaks to you in the same way the UML diagram does above. If I look at the RegistrationAction class, I know that it is an @Action which indicates certain intrinsic behavior associated with the class. This intrinsic behavior is then associated in a type-safe manner through an interceptor invoked based on the annotation. In addition, I gain the ability to easily test my components as the cross-cutting behavior will be introduced in a container-environment. I can test only my logic and not worry about the cross-cutting aspects knowing they will be introduced at run-time.