Help

Matt Corey has blogged about CDI interceptor bindings, showing a simple example of how you can implement your own @RequiredTx annotation. He also lightly criticizes the use of beans.xml for interceptor enablement. (This was also discussed in the Weld forum.) I really think we have the design just right here, and I'll explain why. But first let me remember why CDI interceptor bindings are much better than the @Interceptors annotation from EJB 3.0.

In EJB 3.0, you could declare the interceptors of a bean by specifying them directly on the bean class. Using Matt's example:

public class UserGetterer {

   @Interceptors({RequiredTxInterceptor.class, SecurityInterceptor.class})
   public User loadUser() { ... }

}

Now, I count three problems with this:

  • It's very ugly to have the bean class depend directly upon the interceptor implementation class.
  • Interceptors should be able to vary depending upon the deployment environment - I don't need my transaction interceptor in tests, and I definitely don't want my security interceptor!
  • The interceptor ordering is defined by the order that the interceptors are listed - which I suppose is fine if the interceptors are truly orthogonal, but this is rarely the case. Interceptor ordering should be defined more centrally, reducing the possibility for subtle bugs.

(EJB 3.0 also defined an XML-based approach to binding interceptors to beans, which is less problematic, but I prefer to see information like this in my code.)

So, in CDI, we introduce an indirection, called an interceptor binding. An interceptor binding is an annotation - you can see an example in Matt's post - that we use instead of the interceptor class:

public class UserGetterer {

   @RequiredTx @Secure
   public User loadUser() { ... }

}

Now, our bean class doesn't depend directly upon the interceptor implementation classes, and doesn't specify the ordering in which the interceptors are called. To specify the connection between the bean and the interceptor classes we need to do two more things:

  • apply the interceptor bindings to the interceptor classes, and
  • enable the interceptors by listing them in the beans.xml file of the bean's module.

Matt objects to the second requirement:

Yeah, I know -- it feels kind of like we're exposing your implementation detail by asking the user to enable the interceptor instead of the annotation... kind of annoying, but I can deal with it...

Well, not really - on the contrary, you're explicitly not doing that. The beans.xml file contains deployment-specific metadata - just the stuff that depends upon your deployment. So in your test environment, you don't specify any interceptors at all in beans.xml. In your production deployment you can enable RequiredTransactionInterceptor. In some other deployment, you can enable DummyRequiredTransactionInterceptor, a different implementation of the @RequiredTx semantic or aspect. It's even possible to bind multiple interceptors to the same interceptor binding! So we have truly decoupled the semantic annotation @RequiredTx from its implementation, RequiredTransactionInterceptor.

So we've got an extra level of indirection here that we would not have if it was the annotation we were specifying in beans.xml, as suggested by Matt.

In fact, beans.xml serves two different roles here. It lets you specify:

  • any, or no, or multiple implementations of the interceptor binding, for the particular deployment, and
  • the interceptor ordering.

Ordering is important. It's very likely that you'll be using interceptors defined by various different portable extensions in your application. These portable extensions don't know anything about each other, so they don't know what order they should be called in. We can't specify the interceptor ordering in the portable extensions that define the interceptors. It's your job, as application developer, to specify the ordering of these interceptors.

Matt says:

another thing to note is that if you pack your interceptors into a shareable library file, all other library files in your application will still need to do this, even if you enable it in your .jar file -- that's because there's no concept of a global interceptor in CDI, but to be safe, each library is forced to enable all interceptors in use, so that they can enforce the ordering of interceptors in a very intuitive manner... I can accept this reasoning simply because they have defined it, rather than letting it be an undefined mess, so again, it's more of annoyance for me -- hopefully there will be other options in the future...

OK, so let's step back a second. If my application is deployed as a single module, there is a global ordering, the ordering defined by the beans.xml file of that module. Now, back in Java EE 5 days, it wasn't possible to deploy an application that used EJBs as a single module. But it is now. A war can now directly contain EJBs. So single-module applications are going to be very common. So that's the simple case out of the way.

Now, for the more complex case of an application that really, truly needs to be deployed, for example, as an ear with multiple war and EJB jar modules, or as a war containing multiple jar modules, it's quite likely that the various modules are going to each define a different list of interceptors, decorators and alternatives. I mean, the whole point of modularizing the application is to decouple the modules.

I suppose I accept that we could allow toplevel beans.xml file, that defined a global, or default, list of interceptors, decorators and alternatives. This would be very easy to add to the CDI spec, but I think it's a pretty advanced case. Historically, Java EE has not allowed you to put module-level metadata in the ear.

3 comments:
 
10. Dec 2009, 08:49 CET | Link

Hey Gavin -- thanks for the comments... a couple of thoughts I had today that I'm hopefully going to touch upon in the next day or so:

-Regarding the enabling of Interceptors directly -- another interesting use here, referring to the Transactional samples we've been using, would be a JDBCRequiredTxInterceptor, for those systems which don't make use of JTA (which is what I used), or an XARequiredTxInterceptor, for systems required distributed transactions... it would be feasible for each of these interceptors to share the same bindings, which would mean you could write your code, annotate it with a simple @RequiredTx, and adjust the transactional properties of the system by modifying the enabled interceptors... interesting...

-Where I still have a bit of trouble with this is if we implement each of the six EJB TransactionAttributeTypes with separate interceptors and bindings, and package them in a .jar file... I'm ok with having some kind of enablement in the beans.xml file for my .war, but to have the user enter all six interceptors is awkward for a library developer to force on someone... it would be interesting if the beans.xml from my .jar file could export some kind of name or alias to the group of all six interceptors, and the users of the library could use that one name instead of the six class names... that seems like it would be an easy thing to add to the spec... in the samples above, I could see the following names exported: JTATxInterceptors, JDBCTxInterceptors and XATxInterceptors, and if collisions are a concerns between libraries, they could follow a package scheme, or some other qualifier... so a beans.xml might look something like

<beans>
  <interceptors>
    <class>com.my.application.AuthorizeUserInterceptor</class>
    <alias>com.tx.JTATxInterceptors</alias>
  </interceptors>
</beans>

Interestingly enough, I suppose an alias could be assigned to just a single interceptor as well -- this way the end user could use the alias, and the library writers would be free to change what sits behind it as they see fit... basically, it would act as another layer of indirection...

M

ReplyQuote
 
10. Dec 2009, 11:54 CET | Link
Regarding the enabling of Interceptors directly -- another interesting use here, referring to the Transactional samples we've been using, would be a JDBCRequiredTxInterceptor, for those systems which don't make use of JTA (which is what I used), or an XARequiredTxInterceptor, for systems required distributed transactions... it would be feasible for each of these interceptors to share the same bindings, which would mean you could write your code, annotate it with a simple @RequiredTx, and adjust the transactional properties of the system by modifying the enabled interceptors... interesting...

Yes, exactly. That's a much more practical example than what I came up with ;-)

Where I still have a bit of trouble with this is if we implement each of the six EJB TransactionAttributeTypes with separate interceptors and bindings, and package them in a .jar file... I'm ok with having some kind of enablement in the beans.xml file for my .war, but to have the user enter all six interceptors is awkward for a library developer to force on someone...

Well, y'know, that's a pretty good usecase. You got me there :-)

Of course you could solve the problem by using a single @Transactional annotation with a member annotated @Nonbinding, and then you would have just one interceptor class. But that might not be what you want.

it would be interesting if the beans.xml from my .jar file could export some kind of name or alias to the group of all six interceptors, and the users of the library could use that one name instead of the six class names... that seems like it would be an easy thing to add to the spec

Alternatively, the group could be modeled as a stereotype, instead of as a string-based name. That's more consistent with what we do for alternatives.

 
10. Dec 2009, 18:31 CET | Link
Post Comment