Session EJB and Servlet on Different JBoss Instances: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(21 intermediate revisions by the same user not shown)
Line 7: Line 7:
This example describes how a client component (servlet) deployed on a different JBoss instance looks up an EJB and makes a remote invocation.
This example describes how a client component (servlet) deployed on a different JBoss instance looks up an EJB and makes a remote invocation.


=GitHub Example=
=GitHub Examples=


{{External|https://github.com/NovaOrdis/playground/tree/master/jee/ejb/stateless-and-servlet-different-jboss-instances}}
{{External2|A servlet that calls into an EJB https://github.com/NovaOrdis/playground/tree/master/jee/ejb/stateless-and-servlet-different-jboss-instances|An EJB that calls into another EJB  https://github.com/NovaOrdis/playground/tree/master/jee/ejb/stateless-to-stateless-different-jboss-instances}}
 
=The Business Interface Must be Market as @Remote=
 
The business interface must be marked with the [[EJB_Annotations#.40javax.ejb.Remote|@Remote]] annotation, otherwise the EJB won't be accessible remotely. If the business interface is not annotated with @Remote, the JNDI lookup on the client side, even if using the correct EJB  client context, will fail to locate the EJB.


=Business Interface Type Access=
=Business Interface Type Access=


The calling servlet needs to business interface type to make the call, so the classes related to the business interface will have to be packaged within the WAR. Since those classes are also needed in the EJB deployment unit, the best strategy is to develop those as part of shared "common-types" Maven module, and pull its artifact both in the WAR and the EJB JAR. The example linked above showcases this approach.
The calling servlet needs the business interface type to make the call, so the classes related to the business interface will have to be packaged within the WAR. Since those classes are also needed in the EJB deployment unit, the best strategy is to develop those as part of shared "common-types" Maven module, and pull its artifact both in the WAR and the EJB JAR. The example linked above showcases this approach. A Maven "recipe" to include some dependencies in a simple JAR artifact is described here: [[Maven Include Dependencies in JAR Artifact|Include Dependencies in JAR Artifact]].


=EJB Lookup=
=EJB Lookup=


The consuming component, the servlet in this case, needs a way to look up the EJB reference that has been deployed on a remote JBoss instance. This is done using a JBoss-specific mechanism named [[EJB_Concepts#EJB_Client_Context|EJB client context]]. In order to use the EJB client context that knows how to invoke into the remote JBoss instance, and not the default one, the WAR must contain a JBoss-specific deployment descriptor named jboss-ejb-client.xml in its /WEB-INF directory.  
==Configure EJB Client Context by deploying jboss-ejb-client.xml==
 
The consuming component, the servlet in this case, needs a way to look up the EJB reference that has been deployed on a remote JBoss instance. This is done internally by JBoss instance using a JBoss-specific mechanism named [[EJB_Concepts#EJB_Client_Context|EJB client context]]. In order to use the EJB client context that knows how to invoke into the remote JBoss instance, and not the default EJB client context, the WAR must contain a JBoss-specific deployment descriptor named jboss-ejb-client.xml in its /WEB-INF directory.  


<pre>
<pre>
<jboss-ejb-client xmlns:xsi="urn:jboss:ejb-client:1.2" xsi:noNamespaceSchemaLocation="jboss-ejb-client_1_2.xsd">
<jboss-ejb-client xmlns:xsi="urn:jboss:ejb-client:1.2"  
                  xsi:noNamespaceSchemaLocation="jboss-ejb-client_1_2.xsd">
     <client-context>
     <client-context>
         <ejb-receivers>
         <ejb-receivers>
Line 29: Line 36:
</pre>
</pre>


More details about jboss are available here:
Upon deployment, the presence of the jboss-ejb-client.xml deployment descriptor triggers a specific runtime configuration that allows the servlet to invoke into the remote EJB. The JBoss instance the servlet is deployed into must be configured in a specific way to support jboss-ejb-client.xml content. The configuration is described here:


{{Internal|Jboss-ejb-client.xml|jboss-ejb-client.xml}}
{{Internal|JBoss_Instance_Configuration_to_Support_Deployed_EJB_Client_Contexts|JBoss Instance Configuration to Support Deployed EJB Client Contexts}}
 
 
 
<pre>
            <outbound-connections>
                <remote-outbound-connection name="remote-ejb-container-1" outbound-socket-binding-ref="remote-ejb-container-socket-1" protocol="http-remoting">
                    <properties>
                        <property name="SASL_POLICY_NOANONYMOUS" value="false"/>
                        <property name="SSL_ENABLED" value="false"/>
                    </properties>
                </remote-outbound-connection>
            </outbound-connections>
 
</pre>


More details about jboss-ejb-client.xml are available here:


{{Internal|Jboss-ejb-client.xml|jboss-ejb-client.xml}}


More details about the EJB client context are available here:


{{Internal|EJB_Concepts#EJB_Client_Context|EJB Client Context}}


==Use a Specific JNDI Name==


Use a JNDI name that complies with the following pattern:


<pre>
ejb:[/<application-name>/]<module-name>/<bean-name>!<view-class>
</pre>


Example:


<pre>
ejb:/stateless-ejb-example/SimpleStatelessBean!io.novaordis.playground.jee.ejb.stateless.SimpleStateless
</pre>


The "[[EJB_Concepts#ejb:|ejb:]]" name space is essential, it is what seems to be engaging the correct EJB client context, if "java:global" is used, the look up will fail.


==JNDI==
The !<view-class> part is also essential, if not used, the JNDI lookup will return an <tt>org.jboss.ejb.client.naming.ejb.EjbNamingContext</tt> instance, instead of a business interface type.


The EJB reference is looked up in JNDI using the portable JNDI EJB naming scheme.
==JNDI Lookup Code==
 
In this case, the EJB is named SimpleStatelessBean, it is deployed as part of the stateless-ejb-example.jar deployment unit (JEE module). Considering the fact there is no JEE application (EAR), the simplest possible portable JNDI name the servlet can use to look up the EJB is "java:global/stateless-ejb-example/SimpleStatelessBean".
 
For more details on the JNDI EJB naming scheme, see:
 
{{Internal|EJB_Concepts#EJB_and_JNDI|EJB Concepts - EJB and JNDI}}


The code that does the JNDI lookup is similar to:
The code that does the JNDI lookup is similar to:
Line 77: Line 78:


   InitialContext  ic = new InitialContext();
   InitialContext  ic = new InitialContext();
   SimpleStateless bean = (SimpleStateless)ic.lookup("java:global/stateless-ejb-example/SimpleStatelessBean");
   SimpleStateless bean = (SimpleStateless)ic.lookup(
      "ejb:/stateless-ejb-example/SimpleStatelessBean!io.novaordis.playground.jee.ejb.stateless.SimpleStateless");
}
}
catch(Exception e) {
catch(Exception e) {
Line 89: Line 91:


<pre>
<pre>
@EJB(mappedName = "java:global/stateless-ejb-example/SimpleStatelessBean")
@EJB(mappedName = "ejb:/stateless-ejb-example/SimpleStatelessBean!io.novaordis.playground.jee.ejb.stateless.SimpleStateless")
private SimpleStateless bean;
</pre>
 
"mappedName" and "lookup" can be used interchangeably:
 
<pre>
@EJB(lookup = "ejb:/stateless-ejb-example/SimpleStatelessBean!io.novaordis.playground.jee.ejb.stateless.SimpleStateless")
private SimpleStateless bean;
private SimpleStateless bean;
</pre>
</pre>

Latest revision as of 21:54, 1 May 2017

Internal

Overview

This example describes how a client component (servlet) deployed on a different JBoss instance looks up an EJB and makes a remote invocation.

GitHub Examples

A servlet that calls into an EJB https://github.com/NovaOrdis/playground/tree/master/jee/ejb/stateless-and-servlet-different-jboss-instances
An EJB that calls into another EJB https://github.com/NovaOrdis/playground/tree/master/jee/ejb/stateless-to-stateless-different-jboss-instances

The Business Interface Must be Market as @Remote

The business interface must be marked with the @Remote annotation, otherwise the EJB won't be accessible remotely. If the business interface is not annotated with @Remote, the JNDI lookup on the client side, even if using the correct EJB client context, will fail to locate the EJB.

Business Interface Type Access

The calling servlet needs the business interface type to make the call, so the classes related to the business interface will have to be packaged within the WAR. Since those classes are also needed in the EJB deployment unit, the best strategy is to develop those as part of shared "common-types" Maven module, and pull its artifact both in the WAR and the EJB JAR. The example linked above showcases this approach. A Maven "recipe" to include some dependencies in a simple JAR artifact is described here: Include Dependencies in JAR Artifact.

EJB Lookup

Configure EJB Client Context by deploying jboss-ejb-client.xml

The consuming component, the servlet in this case, needs a way to look up the EJB reference that has been deployed on a remote JBoss instance. This is done internally by JBoss instance using a JBoss-specific mechanism named EJB client context. In order to use the EJB client context that knows how to invoke into the remote JBoss instance, and not the default EJB client context, the WAR must contain a JBoss-specific deployment descriptor named jboss-ejb-client.xml in its /WEB-INF directory.

<jboss-ejb-client xmlns:xsi="urn:jboss:ejb-client:1.2" 
                  xsi:noNamespaceSchemaLocation="jboss-ejb-client_1_2.xsd">
    <client-context>
        <ejb-receivers>
            <remoting-ejb-receiver outbound-connection-ref="remote-ejb-container-1"/>
        </ejb-receivers>
    </client-context>
</jboss-ejb-client>

Upon deployment, the presence of the jboss-ejb-client.xml deployment descriptor triggers a specific runtime configuration that allows the servlet to invoke into the remote EJB. The JBoss instance the servlet is deployed into must be configured in a specific way to support jboss-ejb-client.xml content. The configuration is described here:

JBoss Instance Configuration to Support Deployed EJB Client Contexts

More details about jboss-ejb-client.xml are available here:

jboss-ejb-client.xml

More details about the EJB client context are available here:

EJB Client Context

Use a Specific JNDI Name

Use a JNDI name that complies with the following pattern:

ejb:[/<application-name>/]<module-name>/<bean-name>!<view-class>

Example:

ejb:/stateless-ejb-example/SimpleStatelessBean!io.novaordis.playground.jee.ejb.stateless.SimpleStateless

The "ejb:" name space is essential, it is what seems to be engaging the correct EJB client context, if "java:global" is used, the look up will fail.

The !<view-class> part is also essential, if not used, the JNDI lookup will return an org.jboss.ejb.client.naming.ejb.EjbNamingContext instance, instead of a business interface type.

JNDI Lookup Code

The code that does the JNDI lookup is similar to:

SimpleStateless bean = null;

...

try {

   InitialContext  ic = new InitialContext();
   SimpleStateless bean = (SimpleStateless)ic.lookup(
       "ejb:/stateless-ejb-example/SimpleStatelessBean!io.novaordis.playground.jee.ejb.stateless.SimpleStateless");
}
catch(Exception e) {
    ...
}

@EJB

The equivalent @EJB injection would be:

@EJB(mappedName = "ejb:/stateless-ejb-example/SimpleStatelessBean!io.novaordis.playground.jee.ejb.stateless.SimpleStateless")
private SimpleStateless bean;

"mappedName" and "lookup" can be used interchangeably:

@EJB(lookup = "ejb:/stateless-ejb-example/SimpleStatelessBean!io.novaordis.playground.jee.ejb.stateless.SimpleStateless")
private SimpleStateless bean;

For more details, see

@EJB

EJB Invocation

Once the EJB reference is obtained from JNDI, business interface methods can be called on its reference:

...
bean.methodOne("something from servlet");