Adam Bien's Weblog

Thursday Dec 03, 2009

Simplest Possible EJB 3.1 + JSR-299/JSR-330 Combination

 JSR-299 / JSR-330 beans can be easily combined with EJB 3.1 without any friction. The @Stateless bean:

 @Path("message")

@Stateless

public class ServiceFacade {

    @Inject @Message(Say.GOOD_BYE) MessageService message;

    @Inject DateService dateService;

    @GET public String getMessage(){

      return message.getMessage() + " " + dateService.getCurrentTime();

    }

is exposed via JSR-311 (REST) and a @GET method in particular. Two others JSR-299 beans are directly injected to the @Stateless instance. The CurrentDateService is just a class, which implements the interface DateService, without any additional annotations or configuration:

public class CurrentDateService implements DateService{

  public Date getCurrentTime(){

        return new Date();

    }

The only implementation of the interface is injected with the @Inject annotation. It only works in case there is only one implementation of a particular interface (just like the @EJB annotation).  The MessageService has two implementations - so a particular implementation has to be qualified. A custom annotation Message is used for this purpose:

@Qualifier

@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

public @interface Message {

    enum Say{

        HELLO, GOOD_BYE

   }

    Say value();

Every injectable implementation of the interface has to be annotated as well:

@Message(Message.Say.GOOD_BYE)

public class GoodByeMessageService implements MessageService{

    public String getMessage() {

        return "Good Bye";

    }

 

EJB 3.1 are still the easiest possible choice for the implementation of services / boundaries. Convention over configuration makes any configuration really optional, and the default aspects like transactions, concurrency and request scope kill the superfluous bloat. JSR-299/JSR-330 DI exceed the EJB injection capabilities - both can be nicely combined. 

The working project with source was packaged as a WAR (EJB31AndJSR299.war with the size of 16kB) and pushed into: http://kenai.com/projects/javaee-patterns/. To make the enhanced injection work, you will have to create a configuration file beans.xml located in WEB-INF with the content <beans></beans>. This actually pollutes the cleanness and conciseness of EJB 3.1, so I opened an issue :-). 

An average deployment took < 300 ms. The project was tested with NetBeans 6.8 RC 1 and bundled Glassfish v3 b73.

[See also Dependency Injection Extender pattern, page 231 in "Real World Java EE Patterns Rethinking Best Practices" book for more in-depth discussion]  


Special Events: Java 8 with Java EE 7: "More Power with Less Code", 13th October, 2014 and Java EE 7: "Testing and Code Quality", 14th October, 2014

A book about rethinking Java EE Patterns

Comments:

I think the requirement of a beans.xml file is likely made by Weld itself and not Glassfish.

Since Weld has it's roots in JBoss Seam I know JBoss Seam uses a "marker" file to know to scan for annotations. The idea is to not scan every JAR which would decrease performance.

Posted by Stuart Smith on December 03, 2009 at 08:38 PM CET #

Hi Stuart,

you are absolutely right - Glassfish uses Weld. Glassfish, however, is very intuitive (it writes warnings / hints to the logs) - the beans.xml behavior is the exception from the rule.

thanks for your feedback!,

adam

Posted by Adam Bien on December 03, 2009 at 10:29 PM CET #

Yes, this is a spec requirement, not specific to Weld or GlassFish.

There are actually two reasons for the requirement for a beans.xml file. CDI lets you put beans in any library jar, war, rar or EJB jar. And these beans do not require any special annotation. A class with no annotations at all might be a bean.

So the first reason is that for performance we don't want to require the container to scan every single library jar for beans, and build its internal metamodel for every class it finds there.

The second reason is that we do need to give the user a way to control what classes are detected as things that could possibly match an injection point. Otherwise, they might need to use qualifiers to disambiguate between a bean, and a class that was never intended to be used as an injectable object.

So I think this requirement is a Good Thing.

Posted by Gavin on December 04, 2009 at 05:59 AM CET #

Hi Gavin,

in the context of Weld it may be a Good Thing. In the overall Java EE 6 / Glassfish context it is not very consistent with the remaining APIs - we don't need an empty ejb-jar.xml deployment descriptor either. I would at least expect GF to write some warnings to the log...

thanks for your feedback!,

adam

Posted by Adam Bien on December 04, 2009 at 10:33 PM CET #

Adam, as has been noted, I don't think GlassFish is at fault here, as we simply bundle Weld. I don't think Weld is at fault either. As Gavin noted, beans.xml marks an archive as having CDI artifacts so it doesn't have to scan every class in the classpath. That's a good thing. The only way for GlassFish/Weld to know that there are CDI annotations in use where there is no beans.xml would be too scan the classpath, which negates the performance aspects. For what it's worth, JSF 2 does the same thing with jars in WEB-INF/lib. If there is no faces-config.xml (even an "empty" one), then the jar isn't scanned.

Having to remember the file can lead to hard-to-diagnose bugs, but the performance costs of always scanning everything were deemed to great.

Posted by Jason Lee on December 07, 2009 at 12:10 AM CET #

Perhaps what Glassfish (and Jboss) could do, then, is to spit out a warning message when there is no beans.xml in WEB-INF. Something like "CDI Dependency Injection Services will not use classes in WEB-INF/classes; if this is unintended, add an empty beans.xml file to WEB-INF."

Posted by Matt Drees on December 07, 2009 at 04:00 AM CET #

I should have read your submitted bug description before posting. :-) Nevermind my comment, then.

Posted by Matt Drees on December 07, 2009 at 04:04 AM CET #

@Jason,

from the server-implementor perspective you are absolutely right. From user-perspective it's a bit different :-).

Probably it would be viable to perform a full-scan with a warning in the log files in case the empty file is not presents.

Just like Matt suggested. Otherwise it could be really hard for a Java EE 6 to find the actual problem,

thanks for your feedback!,

adam

Posted by Adam Bien on December 08, 2009 at 12:30 AM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
realworldpatterns.com
...the last 150 posts
...the last 10 comments
Links
License