Help

I'm the creator of Hibernate, a popular object/relational persistence solution for Java, and Seam, an application framework for enterprise Java. I've also contributed to the Java Community Process standards as Red Hat representative for the EJB and JPA specifications and as spec lead of the CDI specification. At Red Hat, I'm currently working on Ceylon, a new programming language for Java and JavaScript VMs.

I now blog at the Ceylon blog.

I also post stuff on G+.

Location: Barcelona, Spain
Occupation: Fellow at JBoss, a Division of Red Hat
Archive 'XML Hell'
My Books
Java Persistence with Hibernate
with Christian Bauer
November 2006
Manning Publications
841 pages (English), PDF ebook
Hibernate in Action
with Christian Bauer
August 2004
Manning Publications
408 pages (English), PDF ebook

In my previous little rant, I showed you how to use @Alternative and alternative stereotypes to easily change bean implementations based upon deployment scenario. But that's not the end of the story. There's two other things I would sometimes like to be able to change at deployment time:

  • interceptors
  • decorators

Let's consider security. Suppose I have an interceptor that implements method-level role-based security for beans in the business layer.

@Secure @Interceptor
class SecurityInterceptor {
    @Inject User user;

    @AroundInvoke
    Object checkRole(InvocationContext ctx) throws Exception {
        String[] roles = ctx.getMethod().getAnnotation(Secure.class).value();
        for (String role: role) {
            if ( !user.getRoles().contains(role) ) throw new AuthorizationException(role);
        }
        return ctx.proceed();
    }
}

I also have a separate data access layer, consisting of beans that implement the following interface:

public interface DataAccess<T, V> {
    public V getId(T object);
    public T load(V id); 
    public void save(T object); 
    public void delete(T object);
    public Class<T> getDataType();
    ...
}

And I have a decorator that adds row-level security to my data access layer.

@Decorator
class DataSecurityDecorator<T, V> implements DataAccess<T, V> {
    @Inject @Delegate DataAccess<T, V> delegate;
    @Inject User user;

    public void save(T object) { 
        authorize(SecureAction.SAVE, object); 
        delegate.save(object);
    }

    public void delete(T object) { 
        authorize(SecureAction.DELETE, object); 
        delegate.delete(object);
    }

    private void authorize(SecureAction action, T object) { 
        V id = delegate.getId(object); 
        Class<T> type = delegate.getDataType(); 
        if ( !user.getPermissions().contains( new Permission(action, type, id) ) ) {
            throw new AuthorizationException(action);
        }
    }
}

Well, that's all very well, but in my test environment I don't want these interceptors and decorators to be enabled. And that's why, by default, they aren't.

We need to expilicity enable the interceptor and decorator for all deployments that require security access controls by declaring them in beans.xml.

<beans>
    <interceptors>
        <class>org.mycompany.security.SecurityInterceptor</class>
    </interceptors>
    <decorators>
        <class>org.mycompany.security.DataSecurityDecorator</class>
    </decorators>
</beans>

Suppose we have an external resource, a database, let's say, that we want to be able to change depending upon the deployment environment. In CDI, we declare resources using a producer field declaration.

class CustomerDatabase {
   static 
   @Resource(lookup="java:global/env/jdbc/CustomerDatasource")
   @Produces @CustomerDatabase
   Datasource customerDatabase;
}

The purpose of this field declaration is to associate the bean type Datasource and binding type @CustomerDatabase with the Java EE resource with the global JNDI name java:global/env/jdbc/CustomerDatasource.

Now we can easily inject our Datasource into any bean that needs it:

public class Bean {
   private Datasource customerDatabase;

   @Inject public Bean(@CustomerDatabase Datasource ds) {
       customerDatabase = ds;
   }
   ...
}

Now, this injected resource reference is already extremely configurable. The JNDI name is just a logical name. The actual datasource it points to can easily change depending upon the deployment. We certainly haven't hardcoded a JDBC URL, username and password! But let's say that's not enough. CDI lets you define alternative implementations of a bean.

class TestCustomerDatabase {
   static 
   @Resource(lookup="java:global/env/jdbc/TestDatasource") 
   @Produces @Alternative @CustomerDatabase 
   Datasource testCustomerDatabase;
}
@Alternative @CustomerDatabase
class MockCustomerDatabase implements Datasource {
   //operations of Datasource go here!
   ...
}

These classes both provide an alternative implementation of @CustomerDatabase Datasource. By default, CDI will ignore these beans, and keep using the original implementation, since it is the only implementation not declared @Alternative.

However, if we declare one of the alternatives in beans.xml, CDI will use that implementation instead:

<beans>
   <alternatives>
      <class>org.mycompany.resources.TestCustomerDatabase</class>
   </alternatives>
</beans>

That's great, and straightforward, when we only have one or two beans that vary depending upon the deployment scenario. Well, I usually consider that to be the case in most applications. However, developers whose brains have been turned to mush by overexposure to the Spring framework think that they have hundreds of beans which might vary depending upon the deployment. That's how they justify writing great gobs of XML to explicitly list out all the classes in their application. We certainly don't want to follow them down that path!

Therefore, CDI provides the ability to define an annotation that represents a deployment scenario. These annotations are called alternative stereotypes. For example:

@Alternative @Stereotype
@Target(TYPE) @Retention(RUNTIME)
public @interface Mock {}
@Alternative @Stereotype
@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
public @interface Test {}

We can now use these annotations to indicate which of our beans belong to which deployment scenario:

class TestCustomerDatabase {
   static 
   @Resource(lookup="java:global/env/jdbc/TestDatasource") 
   @Produces @Test @CustomerDatabase 
   Datasource testCustomerDatabase;
}
@Mock @CustomerDatabase
class MockCustomerDatabase implements Datasource {
   //operations of Datasource go here!
   ...
}

Now, with just a single line of XML, we can collectively enable all @Test beans, or all @Mock beans.

<beans>
   <alternatives>
      <stereotype>org.mycompany.deployment.Test</stereotype>
   </alternatives>
</beans>

Don't you think that's a better way?