Search code examples
ejbjava-ee-6

How do I override many EJB references in one shot scattered throughout an .ear file?


I have an .ear file with dozens of EJB jar files in it.

Each EJB jar file has dozens of EJBs in it.

Each EJB has @EJB references in it with no other information (no name attribute, etc.).

At the moment everything Just Works: it just so happens that for any given @EJB-annotated injection target there is exactly one concrete EJB that is present in the .ear file to fill the slot.

I need for various reasons to introduce a few more EJB jar files that contain EJBs that could also fill some of these slots.

Hence, I need to tell my Java EE container which EJB to inject into which slot where there are conflicts.

I am familiar with the ejb-jar.xml deployment descriptor mechanism for overriding EJB references but if that is my only recourse I'm looking at changing possibly thousands of these entries.

I noticed in the application.xml schema version 6 that there is support for <ejb-local-ref> elements in there, too, which is in some abstract way what I want. But I cannot figure out how to make use of these elements to do what I want.

In some abstract perfect world, I'd like to somehow say, in one place, "Hey, Java EE container, when you see an @EJB-annotated field like this:

@EJB
SomeType bean;

...please inject the EJB whose beanName is fred into it. Everywhere in this application." How do I do that?


Solution

  • There's no standard way to do it and as far as I know, no common vendor-specific way to do it.

    This is something i tried to get in in EJB 3.1 for EJB and JPA refs: if an injection point works using the default rules because there's only one SomeType bean, but then some day a second SomeType bean is added... to be blunt, you're hosed :) This problem also exists for @PersistenceContext and @PersistenceUnit refs. The idea was/is to have some "this is the default impl of SomeType option in the standard deployment descriptors and bail people out of these situations.

    We should still get this into the specification. If this is your problem, I'll repost that proposal and if you can please follow up with information on how much of a nightmare this is for you, I can almost guarantee we'll get this fixed for EJB.next.

    Get your canonical xml

    For now, one possible approach is to temporarily deploy this ear in TomEE using the flag openejb.descriptors.output=true. TomEE/OpenEJB will actually give you all the effective deployment descriptors for your application. Could save you several hours or days if your application is as large as you state. Internally we 1) read in the deployment descriptors, 2) use the annotations to "fill out" the deployment descriptors. The result is we have a tree which is the canonical set of "metadata" for each EJB module. With the openejb.descriptors.output=true flag set, we will write each of these ejb-jar.xml files to disk and log the location.

    Once you have your ejb-jar.xml files in hand, you can modify at will and go back to GlassFish and perhaps have better luck.

    Overriding: Control your reference counts

    In terms of how to actually manage this in Java EE, you have to understand how to be successful in overriding references. There's a critical bit of info one needs to know in how annotation references translate to xml. The goal is to have 1 reference and not N.

    When your reference is unnamed like so:

    package org.some.awesome.code;
    
    public class Foo {
    
        @EJB
        SomeType orange;
    

    ... there's a fixed rule that the default name of this must be org.some.awesome.code.Foo/orange. The class name and package become part of the name so as to give you the most fine grained control in overriding. The following xml will override this reference:

    <ejb-local-ref>
      <ejb-ref-name>org.some.awesome.code.Foo/orange</ejb-ref-name>
      <local>org.some.awesome.code.SomeType</local>
      <ejb-link>Fred</ejb-link>
    </ejb-local-ref>
    

    This reference will now link to the EJB of type SomeType with the beanName 'Fred'.

    The disadvantage of this is that if you have 100 references like this and you want to change them all, you will need 100 declarations in xml to do so.

    There's no way out of this problem that doesn't involve changing your code -- this is where my "use this as the default" proposal has value, it would allow one xml change to handle all 100 cases with no code change. It's as simple as being able to say "when I'm not specific in my code, I really mean use X" via the deployment descriptor.

    Perhaps by making it legal to declare overrides in xml using just the type, like so:

    <ejb-local-ref>
      <local>org.some.awesome.code.SomeType</local>
      <ejb-link>Fred</ejb-link>
    </ejb-local-ref>
    

    As mentioned, this is currently not legal.

    Changing your code

    There are a few code change approaches available, but one of them is to give each @EJB ref the same name. Then you will end up with just 1 reference rather than N.

    package org.some.awesome.code;
    
    public class Foo {
    
        @EJB(name "purple")
        SomeType orange;
    

    Here the name of the reference is explicitly given so overriding it in xml can be done more simply as follows:

    <ejb-local-ref>
      <ejb-ref-name>purple</ejb-ref-name>
      <local>org.some.awesome.code.SomeType</local>
      <ejb-link>Fred</ejb-link>
    </ejb-local-ref>
    

    If there are 100 @EJB(name="purple") references, all of them would be overridden by the above declaration.

    Conclusion

    Short answer, there's currently no easy way out. You're looking at either extensive xml or extensive code change.

    Long term, we can hopefully improve this part of the specification so people aren't punished when they grow out of the default matching rules.