Enterprise Java Development@TOPIC@
In the previous chapters we covered several techniques that can be used to
configure EJBs with different types of resources. In this chapter we will
limit the number of options but have you configure a new type of property --
a dependency EJB. EJBs can be injected with the @EJB
annotation.
If there is only one choice for the type of EJB and it is available within the
local application, then all we need is the annotation.
If there are multiple choices, we must identify which choice to make using either:
beanName -- this is the name of the EJB to use
lookup -- JNDI name of the EJB to use
name -- ENC name of the EJB to use
If the EJB is only available from a remote application, then we must use some form of JNDI lookup.
We will primarily be working with the following files
src/test/java/.../ConfigBeanIT.java
src/main/java/.../SampleLocal.java
src/main/java/.../SampleRemote.java
src/main/java/.../SampleNoIfaceEJB.java
src/main/java/.../Choice1EJB.java
src/main/java/.../Choice2EJB.java
src/main/java/.../ConfigBeanEJB.java
src |-- main | |-- java | | `-- org | | `-- myorg | | `-- encconfig | | `-- ejb | | |-- Choice1EJB.java | | |-- Choice2EJB.java | | |-- ConfigBeanEJB.java | | |-- ConfigBeanRemote.java | | |-- SampleLocal.java | |-- SampleNoIfaceEJB.java | `-- SampleRemote.java `-- test |-- java | `-- org | `-- myorg | `-- encconfig | `-- auditor | `-- ejb | `-- it | `-- ConfigBeanIT.java
Start JBoss. If you run JBoss within Eclipse, you may want to start it in debug mode.
Deploy the EJB module to JBoss using the maven cargo plugin
$ mvn pre-integration-test ... [INFO] --- cargo-maven2-plugin:1.4.3:redeploy (cargo-prep) @ encconfig-labex-ejb --- ... [INFO] BUILD SUCCESS
Run all IT tests within Eclipse or at the command line. They should all pass, but if you look closely -- many are still being ignored until we fix some things.
In this section we are going to demonstrate how we can simply use an @EJB annotation and type information for the container to inject a dependency EJB instance into our parent EJB.
Remove the @Ignore on the ConfigBeanIT
test case.
//TODO: enc-config 29
@Ignore
public class ConfigBeanIT {
Re-run the IT tests. You should get a failure for the test case you just activated.
java.lang.IllegalStateException: EJBCLIENT000025: No EJB receiver available for handling [appName:, moduleName:encconfig-labex-ejb, distinctName:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext@5000cc80
The error basically states there is no EJB posted to the given JNDI name. This is because the EJB module was deployed without adding @Stateless to the EJB we are trying to lookup.
Activate the EJB within the EJB module by adding @Stateless
to the ConfigBeanEJB
.
//TODO: enc-config 30
//@Stateless
public class ConfigBeanEJB implements ConfigBeanRemote {
Re-deploy the EJB module to the server.
$ mvn pre-integration-test
Re-run the JUnit tests. The following error should occur.
no interface EJB not injected
The error is caused by no injection defined for the EJB. In this case we want to use a "no interface" EJB that has no local interface -- so we inject the EJB class itself.
private SampleNoIfaceEJB noIface;
Add injection of the no interface EJB by adding an @EJB annotation. This
will trigger the container to look for a SampleNoIfaceEJB
,
locate it, and inject it.
@Stateless
public class ConfigBeanEJB implements ConfigBeanRemote {
//TODO: enc-config 31
//@EJB
private SampleNoIfaceEJB noIface;
Re-deploy the EJB module to the server.
$ mvn pre-integration-test
Re-run the JUnit tests. The test case should now pass.
You have finished injecting a dependency EJB using just an @EJB annotation and type information from the Java variable. We happen to use a no interface bean in this example but the same can be done with @Local and @Remote interfaces as long as the dependency EJB is deployed within the application.
In this section we are going to demonstrate what can happen when there are multiple valid choices and how we can distinguish the right one by using its EJB name.
Update the Choice2EJB
to also implement the
SampleLocal interface.
@Stateless
//TODO: enc-config 32
public class Choice2EJB implements /*SampleLocal, */ SampleRemote {
This will cause an ambiguity conflict now that we have two EJBs that implement the same interface.
@EJB
private SampleLocal localEJB;
Attempt to re-deploy the EJB module to the server. This will fail.
2014-10-22 02:17:28,981 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-4) MSC000001: Failed to start service jboss.deployment.unit."encconfig-labex-ejb.jar".INSTALL: ... ... Caused by: org.jboss.as.server.deployment.DeploymentUnitProcessingException: JBAS011058: Failed to install component ConfigBeanEJB ... Caused by: org.jboss.as.server.deployment.DeploymentUnitProcessingException: JBAS014546: More than one EJB found with interface of type 'org.myorg.encconfig.ejb.SampleLocal' for binding org.myorg.encconfig.ejb.ConfigBeanEJB/localEJB. Found: ...
Fix the ambiguity by explicitly referencing Choice1EJB using its EJB name in the @EJB annotation.
@Stateless
public class ConfigBeanEJB implements ConfigBeanRemote {
//TODO: enc-config 33
@EJB//(beanName="Choice1EJB")
private SampleLocal localEJB;
Re-deploy the application. This should succeed.
$ mvn clean pre-integration-test
Re-run the JUnit tests. This should also work.
You have finished injecting a dependency EJB using an EJB beanName. The beanName (or other techniques) is required when the type-alone is not clear enough to locate the right dependency EJB.
In this section we are going to again demonstrate an ambiguity for an interface type except this time we will use a @Remote interface and dis-ambiguate the choices using the JNDI name of the target EJB using the lookup property of @EJB.
Update the Choice1EJB
to also implement the
SampleRemote interface.
@Stateless
//TODO: enc-config 34
public class Choice1EJB implements SampleLocal/*, SampleRemote*/ {
This will again cause an ambiguity conflict now that we have two EJBs that implement the same interface.
@EJB
private SampleRemote remoteEJB;
Attempt to re-deploy the EJB module to the server. This will fail.
More than one EJB found with interface of type 'org.myorg.encconfig.ejb.SampleRemote' for binding org.myorg.encconfig.ejb.ConfigBeanEJB/remoteEJB. Found: ...
Fix the ambiguity by explicitly referencing Choice2EJB using one of its remote JNDI lookup names in the @EJB annotation. Since the EJB is local to the module -- we have the choice of several JNDI names to locate the EJB. The remote name in the "java:jboss/exported" namespace is the only one available to remote clients outside of the server.
java:global/encconfig-labex-ejb/Choice2EJB!org.myorg.encconfig.ejb.SampleRemote java:app/encconfig-labex-ejb/Choice2EJB!org.myorg.encconfig.ejb.SampleRemote java:module/Choice2EJB!org.myorg.encconfig.ejb.SampleRemote java:jboss/exported/encconfig-labex-ejb/Choice2EJB!org.myorg.encconfig.ejb.SampleRemote
@Stateless
public class ConfigBeanEJB implements ConfigBeanRemote {
//TODO: enc-config 35
@EJB//(lookup="java:module/Choice2EJB!org.myorg.encconfig.ejb.SampleRemote")
private SampleRemote remoteEJB;
Re-deploy the application. This should succeed.
$ mvn clean pre-integration-test
Re-run the JUnit tests. This should also work.
You have finished injecting a dependency EJB using a JNDI lookup.
In this chapter you successfully injected a dependency EJB into a parent EJB thru a few different but simple techniques. The simplest technique was to use @EJB and the type information of the Java variable to completely wire-up the injection. That works until there is an ambiguity of choices or if the EJB is not deployed within the local application. Ambiguity can be solved using the beanName, JNDI lookup, or ENC name (we did not demonstrate this option). If the EJB is not deployed within the same module or application, we must use the lookup name to reference the JNDI name in a separate deployment.