users@glassfish.java.net

_at_EJB(name = "foo") injection question (in a _at_ManagedBean)

From: Laird Nelson <ljnelson_at_gmail.com>
Date: Wed, 31 Jul 2013 20:19:36 -0700

Following GlassFish team recommendations many moons ago, I have created a
bunch of @ManagedBeans. Why have I done this?

Each @ManagedBean lives in my .ear file's lib directory. Each is a JAX-RS
endpoint. JAX-RS applications are capable (by spec) of scanning the
classpath (which includes ear/lib) for endpoints. So that's why. Now I
can customize my app by adding and removing endpoints to the .ear file, not
by going through the satanic dance of cracking the ear, cracking the .war,
adding something to the .war's lib directory, sealing the .war, then
re-sealing the .ear.

So then: the JAX-RS web application that nominally serves as their host is
a generic empty Application class, in case it ends up mattering.

The result is that all these endpoints are dynamically discovered. So far
so good; everything works great.

Now, each of these @ManagedBean classes has references in it like this:

@EJB(name = "ejb/Fred")
private Fred fred;

As long as my .ear file is packaged up with only one implementation of Fred
in it, everything works great (the name() attribute of the @EJB annotation
in this case is kind of superfluous).

Now, I am faced with the necessity of packing up my .ear file with several
EJB jar files, each of which may have a Fred implementation in it. One
Fred slot, many Fred implementations.

To get around this, I'm using the application.xml-level <ejb-local-ref>
entries to specify for sure which EJB I would like injected into named @EJB
references.

Works great—in my other EJB classes that, themselves, have @EJB(name =
"ejb/Fred") references in them: my application.xml's <ejb-local-ref> that
defines a <ejb-link> for references named "ejb/Fred" picks out the
implementation I want, and everything is great.

However, this does NOT work for these @ManagedBean outliers. Why not?

I get exceptions that make it clear that GlassFish thinks that--although
this is not the case--my @ManagedBean classes have just:

@EJB
private Fred fred; // will fail; there are two Fred implementations at
runtime

(Again, that's not what's actually in there; I specify a name in my @EJB
reference and then override that slot in my .ear-level deployment
descriptor.)

How can I make it so that a reference like this:

@EJB(name = "ejb/Fred")
private Fred fred;

...in a @ManagedBean in the lib directory is "filled" with the following
entry defined in application.xml:

<ejb-local-ref>
    <description>A Fred bean.</description>
    <ejb-ref-name>ejb/Fred</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <local>com.foobar.Fred</local>
    <ejb-link>some-ejb-jar.jar#FredBean</ejb-link>
  </ejb-local-ref>

...?

While this works for overriding such references within EJBs themselves,
this causes the following exception when trying to find the right EJB to
inject into a similarly named slot in one of these JAX-RS/ManagedBean
hybrids:

Caused by: java.lang.IllegalArgumentException: Cannot resolve reference
Remote ejb-ref name=ejb/Fred,Remote 3.x interface
=com.foobar.FredBean,ejb-link=null,lookup=,mappedName=,jndi-name=,refType=Session
because there are 2 ejbs in the application with interface com.foobar.Fred.
Some of the possible causes:
1. The EJB bean class was packaged in an ear lib library (or through any
other library mechanism which makes the library visible to all component
modules), this makes all the component modules include this bean class
indirectly.
2. The EJB bean class was packaged in a component module which references
the EJB, either directly or indirectly through Manifest, WEB-INF/lib.
The EJB bean class should only be packaged in the declaring ejb module and
not the referencing modules. The referencing modules should only include
EJB interfaces.
at
com.sun.enterprise.deployment.util.EjbBundleValidator.accept(EjbBundleValidator.java:602)
at
com.sun.enterprise.deployment.ManagedBeanDescriptor.visit(ManagedBeanDescriptor.java:389)
at
com.sun.enterprise.deployment.ManagedBeanDescriptor.validate(ManagedBeanDescriptor.java:379)
at
com.sun.enterprise.container.common.impl.managedbean.ManagedBeanManagerImpl.loadManagedBeans(ManagedBeanManagerImpl.java:225)

Stuck hard until I can figure this out.

Given that the @ManagedBeans are at least somehow affiliated/associated
with the web application that "hosts" them, maybe I could duplicate these
ejb-local-ref elements in its web.xml?

Best,
Laird

-- 
http://about.me/lairdnelson