Red Hat Training

A Red Hat training course is available for Red Hat JBoss Enterprise Application Platform

Chapter 5. Invoking Session Beans

5.1. Invoke a Session Bean Remotely using JNDI

This task describes how to add support to a remote client for the invocation of session beans using JNDI. The task assumes that the project is being built using Maven.

The ejb-remote quickstart contains working Maven projects that demonstrate this functionality. The quickstart contains projects for both the session beans to deploy and the remote client. The code samples below are taken from the remote client project.

This task assumes that the session beans do not require authentication.

Warning

Red Hat recommends that SSLv2, SSLv3, and TLSv1.0 be explicitly disabled in favor of TLSv1.1 or TLSv1.2 in all affected packages.

Prerequisites

  • You must already have a Maven project created ready to use.
  • Configuration for the JBoss EAP Maven repository has already been added.
  • The session beans that you want to invoke are already deployed.
  • The deployed session beans implement remote business interfaces.
  • The remote business interfaces of the session beans are available as a Maven dependency. If the remote business interfaces are only available as a JAR file then it is recommended to add the JAR to your Maven repository as an artifact. See the Maven documentation for the install:install-file goal for directions, http://maven.apache.org/plugins/maven-install-plugin/usage.html
  • You need to know the host name and JNDI port of the server hosting the session beans.

To invoke a session bean from a remote client you must first configure the project correctly.

Add Maven Project Configuration for Remote Invocation of Session Beans

  1. Add the required project dependencies.

    The pom.xml for the project must be updated to include the necessary dependencies.

  2. Add the jboss-ejb-client.properties file.

    The JBoss EJB client API expects to find a file in the root of the project named jboss-ejb-client.properties that contains the connection information for the JNDI service. Add this file to the src/main/resources/ directory of your project with the following content.

    EJB Client Properties File Example

    remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
    remote.connections=default
    remote.connection.default.host=localhost
    remote.connection.default.port = 8080
    remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
    #remote.connection.default.connect.options.org.xnio.Options.SSL_STARTTLS=true

    Change the host name and port to match your server. The default port number is 8080. For a secure connection, set the SSL_ENABLED line to true and uncomment the SSL_STARTTLS line. The Remoting interface in the container supports secured and unsecured connections using the same port.

  3. Add dependencies on the remote business interfaces.

    Add the Maven dependencies on the remote business interfaces of the session beans to the pom.xml.

    POM File Configuration Example

    <dependency>
        <groupId>org.jboss.quickstarts.eap</groupId>
        <artifactId>jboss-ejb-remote-server-side</artifactId>
        <type>ejb-client</type>
        <version>${project.version}</version>
    </dependency>

    After the project is configured correctly, you can add the code to access and invoke the session beans.

Obtain a Bean Proxy using JNDI and Invoke Methods of the Bean

  1. Handle checked exceptions.

    Two of the methods used in the following code (InitialContext() and lookup()) have a checked exception of type javax.naming.NamingException. These method calls must either be enclosed in a try/catch block that catches NamingException or in a method that is declared to throw NamingException. The ejb-remote quickstart uses the second technique.

  2. Create a JNDI context.

    A JNDI Context object provides the mechanism for requesting resources from the server. Create a JNDI context using the following code:

    Create a JNDI Context Code Example

    final Hashtable jndiProperties = new Hashtable();
    jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
    final Context context = new InitialContext(jndiProperties);

    The connection properties for the JNDI service are read from the jboss-ejb-client.properties file.

  3. Use the JNDI Context’s lookup() method to obtain a bean proxy.

    Invoke the lookup() method of the bean proxy and pass it the JNDI name of the session bean you require. This will return an object that must be cast to the type of the remote business interface that contains the methods you want to invoke.

    Invoke Lookup Method Code Example

    final RemoteCalculator statelessRemoteCalculator = (RemoteCalculator) context.lookup(
        "ejb:/jboss-ejb-remote-server-side//CalculatorBean!" +
        RemoteCalculator.class.getName());

    Session bean JNDI names are defined using a special syntax. For more information, see EJB JNDI Naming Reference.

  4. Invoke methods: Now that you have a proxy bean object you can invoke any of the methods contained in the remote business interface.

    Invoke Remote Method Code Example

    int a = 204;
    int b = 340;
    System.out.println("Adding " + a + " and " + b + " via the remote stateless calculator deployed on the server");
    int sum = statelessRemoteCalculator.add(a, b);
    System.out.println("Remote calculator returned sum = " + sum);

    The proxy bean passes the method invocation request to the session bean on the server, where it is executed. The result is returned to the proxy bean which then returns it to the caller. The communication between the proxy bean and the remote session bean is transparent to the caller.

You should now be able to configure a Maven project to support invoking session beans on a remote server and write the code invoke the session beans' methods using a proxy bean retrieved from the server using JNDI.

5.2. About EJB Client Contexts

JBoss EAP introduced the EJB client API for managing remote EJB invocations. The JBoss EJB client API uses the EJBClientContext, which may be associated with and be used by one or more threads concurrently. The means an EJBClientContext can potentially contain any number of EJB receivers. An EJB receiver is a component that knows how to communicate with a server that is capable of handling the EJB invocation. Typically, EJB remote applications can be classified into the following:

  • A remote client, which runs as a standalone Java application.
  • A remote client, which runs within another JBoss EAP instance.

Depending on the type of remote client, from an EJB client API point of view, there can potentially be more than one EJBClientContext within a JVM.

While standalone applications typically have a single EJBClientContext that may be backed by any number of EJB receivers, this isn’t mandatory. If a standalone application has more than one EJBClientContext, an EJB client context selector is responsible for returning the appropriate context.

In case of remote clients that run within another JBoss EAP instance, each deployed application will have a corresponding EJB client context. Whenever that application invokes another EJB, the corresponding EJB client context is used to find the correct EJB receiver, which then handles the invocation.

5.3. Considerations When Using a Single EJB Context

Summary

You must consider your application requirements when using a single EJB client context with standalone remote clients. For more information about the different types of remote clients, refer to: About EJB Client Contexts.

Typical Process for a Remote Standalone Client with a Single EJB Client Context

A remote standalone client typically has just one EJB client context backed by any number of EJB receivers. The following is an example of a standalone remote client application:

public class MyApplication {
    public static void main(String args[]) {
        final javax.naming.Context ctxOne = new javax.naming.InitialContext();
        final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface");
        beanOne.doSomething();
        ...
    }
}

Remote client JNDI lookups are usually backed by a jboss-ejb-client.properties file, which is used to set up the EJB client context and the EJB receivers. This configuration also includes the security credentials, which are then used to create the EJB receiver that connects to the JBoss EAP server. When the above code is invoked, the EJB client API looks for the EJB client context, which is then used to select the EJB receiver that will receive and process the EJB invocation request. In this case, there is just the single EJB client context, so that context is used by the above code to invoke the bean. The procedure to invoke a session bean remotely using JNDI is described in greater detail here: Invoke a Session Bean Remotely using JNDI.

Remote Standalone Client Requiring Different Credentials

A user application may want to invoke a bean more than once, but connect to the JBoss EAP server using different security credentials. The following is an example of a standalone remote client application that invokes the same bean twice:

public class MyApplication {
    public static void main(String args[]) {
        // Use the "foo" security credential connect to the server and invoke this bean instance
        final javax.naming.Context ctxOne = new javax.naming.InitialContext();
        final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface");
        beanOne.doSomething();
        ...

        // Use the "bar" security credential to connect to the server and invoke this bean instance
        final javax.naming.Context ctxTwo = new javax.naming.InitialContext();
        final MyBeanInterface beanTwo = ctxTwo.lookup("ejb:app/module/distinct/bean!interface");
        beanTwo.doSomething();
        ...
    }
}

In this case, the application wants to connect to the same server instance to invoke the EJB hosted on that server, but wants to use two different credentials while connecting to the server. Because the client application has a single EJB client context, which can have only one EJB receiver for each server instance, this means the above code uses just one credential to connect to the server and the code does not execute as the application expects it to.

Solution

Scoped EJB client contexts offer a solution to this issue. They provide a way to have more control over the EJB client contexts and their associated JNDI contexts, which are typically used for EJB invocations. For more information about scoped EJB client contexts, refer to Using Scoped EJB Client Contexts and Configure EJBs Using a Scoped EJB Client Context.

5.4. Transaction Behavior of EJB Invocations

Server to Server Invocations

Transaction attributes for distributed JBoss EAP applications must be handled such that the application is called on the same server. To discontinue a transaction, the destination method must be marked REQUIRES_NEW using different interfaces.

An EJB can be invoked using either of the following methods:

Note

JBoss EAP does not require Java Transaction Services (JTS) for transaction propagation on server-to-server EJB invocations if both servers are JBoss EAP. JBoss EJB client API library handles it itself.

EJB Remoting Call

To invoke EJB session beans with a JBoss EAP standalone client, the client must have a reference to the InitialContext object while the EJB proxies or UserTransaction are used. It is also important to keep the InitialContext object open while EJB proxies or UserTransaction are being used. Control of the connections will be inside the classes created by the InitialContext with the properties.

The following code example shows an EJB client that holds a reference to the InitialContext object. This code example was taken from the ejb-multi-server quickstart that ships with JBoss EAP.

EJB Client Code Example

package org.jboss.as.quickstarts.ejb.multi.server;

import java.util.Date;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;

import org.jboss.as.quickstarts.ejb.multi.server.app.MainApp;
import org.jboss.ejb.client.ContextSelector;
import org.jboss.ejb.client.EJBClientConfiguration;
import org.jboss.ejb.client.EJBClientContext;
import org.jboss.ejb.client.PropertiesBasedEJBClientConfiguration;
import org.jboss.ejb.client.remoting.ConfigBasedEJBClientContextSelector;

public class Client {

/**
* @param args no args needed
* @throws Exception
*/
    public static void main(String[] args) throws Exception {
        // suppress output of client messages
        Logger.getLogger("org.jboss").setLevel(Level.OFF);
        Logger.getLogger("org.xnio").setLevel(Level.OFF);

        Properties p = new Properties();
        p.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
        p.put("remote.connections", "one");
        p.put("remote.connection.one.port", "8080");
        p.put("remote.connection.one.host", "localhost");
        p.put("remote.connection.one.username", "quickuser");
        p.put("remote.connection.one.password", "quick-123");

        EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(p);
        ContextSelector<EJBClientContext> selector = new ConfigBasedEJBClientContextSelector(cc);
        EJBClientContext.setSelector(selector);

        Properties props = new Properties();
        props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
        InitialContext context = new InitialContext(props);


        final String rcal = "ejb:jboss-ejb-multi-server-app-main/ejb//" + ("MainAppBean") + "!" + MainApp.class.getName();
        final MainApp remote = (MainApp) context.lookup(rcal);
        final String result = remote.invokeAll("Client call at "+new Date());

        System.out.println("InvokeAll succeed: "+result);
    }

}

Note

Obtaining a UserTransaction reference on the client is unsupported for scenarios with a scoped EJB client context and for invocations which use the remote-naming protocol. This is because in these scenarios, InitialContext encapsulates its own EJB client context instance; which cannot be accessed using the static methods of the EJBClient class. When EJBClient.getUserTransaction() is called, it returns a transaction from default (global) EJB client context (which might not be initialized) and not from the desired one.

UserTransaction Reference on the Client-side

The following example shows how to get UserTransaction reference on a standalone client.

UserTransaction Code Example

import org.jboss.ejb.client.EJBClient;
import javax.transaction.UserTransaction;
...
    Context context = null;
    UserTransaction tx = null;
    try {
      Properties props = new Properties();
      // REMEMBER: there must be a jboss-ejb-client.properties with the
      // connection parameter in the client's classpath
      props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
      context = new InitialContext(props);
      System.out.println("\n\tGot initial Context: "+context);
      tx = EJBClient.getUserTransaction("yourServerName");
      System.out.println("UserTransaction = "+tx.getStatus());
      tx.begin();
      // do some work
      ...
    } catch (Exception e) {
      e.printStackTrace();
      tx.rollback();
    } finally{
      if(context != null) {
        context.close();
      }
    }

Note

To get UserTransaction reference on the client side; start your server with the following system property -Djboss.node.name=yourServerName and then use it on client side as following:

tx = EJBClient.getUserTransaction("yourServerName");

Replace "yourServerName" with the name of your server. If a user transaction is started on a node all invocations are sticky on the node and the node must have all the needed EJBs. It is not possible to use UserTransaction with remote-naming protocol and scoped-context.

Internet Inter-ORB Protocol (IIOP) Remote Call

To invoke an EJB bean using an IIOP remote call, you must first enable IIOP on the server.

To enable IIOP you must have the iiop-openjdk subsystem installed and the <iiop/> element present in the ejb3 subsystem configuration. The standalone-full.xml configuration that comes with the distribution has both of these enabled.

For a bean to be reachable by IIOP remote call, it needs to use EJB 2 and home interface with narrowing. More details on IIOP remote invocation can be seen at Configure IIOP for Remote EJB Calls.

Note

The major differences between IIOP remote call and EJB remoting call are:

  • When a client intends to invoke an EJB bean via IIOP remote call to start transaction on client side, the JTS transaction implementation has to be used. On the other hand, if a client intends to invoke an EJB bean via EJB remoting call, the JTA transaction implementation has to be used.
  • For EJB invocation via IIOP remote call, the transaction is created on client and propagated via call to the server. Whereas, for EJB invocation via EJB remoting call, the transaction is looked up on the server and is managed on the client side.

To enable JTS transactions on the server, you must change the transactions attribute from value spec to full in the iiop-openjdk subsystem and set the jts attribute in the transactions subsystem to true. You can accomplish this by using the following management CLI commands.

/subsystem=iiop-openjdk/:write-attribute(name=transactions,value=full)
/subsystem=transactions/:write-attribute(name=jts,value=true)
Important

For the client to successfully invoke an EJB transaction using an IIOP call, we need to add the client-side dependency on org.wildfly:wildfly-iiop-openjdk.

IIOP Client Code Example

import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import com.arjuna.ats.arjuna.recovery.RecoveryManager;
import com.arjuna.ats.internal.jts.ORBManager;
import com.arjuna.ats.internal.jts.context.ContextPropagationManager;
import com.arjuna.ats.jts.OTSManager;
import com.sun.corba.se.impl.orbutil.ORBConstants;
import com.arjuna.orbportability.ORB;
import com.arjuna.orbportability.OA;

final String host = "localhost";
final int port = 3528;

// For client we define how the Narayana will behave
System.setProperty("com.arjuna.ats.jts.alwaysPropagateContext", "true");

// Set orb to be initialized on client and being able to start ORB txn
Properties properties = new Properties();
properties.setProperty(ORBConstants.PERSISTENT_SERVER_PORT_PROPERTY, "15151");
properties.setProperty(ORBConstants.ORB_SERVER_ID_PROPERTY, "1");

// registers the appropriate filter with the ORB
new ContextPropagationManager();

org.omg.CORBA.ORB sunOrb = org.omg.CORBA.ORB.init(new String[0], properties);
ORB orb = null;
try {
    orb = com.arjuna.orbportability.ORB.getInstance("ClientSide");
    orb.setOrb(sunOrb);

    OA oa = OA.getRootOA(orb);
    org.omg.PortableServer.POA rootPOA = org.omg.PortableServer.POAHelper.narrow(sunOrb.resolve_initial_references("RootPOA"));
    oa.setPOA(rootPOA);

    oa.initOA();

    ORBManager.setORB(orb);
    ORBManager.setPOA(oa);

    // Recovery manager has to be started on client when we want recovery to work at client
    RecoveryManager.manager().startRecoveryManagerThread();

    // Getting context to lookup
    System.setProperty("com.sun.CORBA.ORBUseDynamicStub", "true");
    final Properties prope = new Properties();
    prope.put(Context.PROVIDER_URL, "corbaloc::" + host + ":" + port + "/JBoss/Naming/root");
    prope.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.iiop.naming:org.jboss.naming.client");
    prope.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory");
    Context context = new InitialContext(prope);

    // Bean lookup
    final Object iiopObj = context.lookup(IIOPBeanMandatory.class.getSimpleName());
    final IIOPBeanHome beanHome = (IIOPBeanHome) PortableRemoteObject.narrow(iiopObj, IIOPBeanHome.class);
    final IIOPRemote bean = beanHome.create();

    // Starting orb transaction
    OTSManager.get_current().begin();

    // Call bean - business logic
    bean.sayHello();

    // Manage the commit of the work
    OTSManager.get_current().commit(true);
    // or rollback
    // OTSManager.get_current().rollback();
} finally {
    // It's good to release resources - do it only once at the end
    if (orb != null) {
        orb.shutdown();
    }
    RecoveryManager.manager().terminate();
}

For more information, see Configuring Transactions in the JBoss EAP Configuration Guide.

5.5. Example EJB Invocation from a Remote Server Instance

JBoss EAP is secure by default. No communication can happen with a server instance from a remote client without passing the appropriate credentials, irrespective of whether it is a standalone client or another server instance. In order to allow a client server to communicate with a destination server, you must configure user credentials to be used during this server communication.

Following example demonstrates how to invoke EJBs deployed on a JBoss EAP server instance from another remote JBoss EAP server instance. For ease of reference, let us use the following aliases:

  • Client server: the server from which the EJB invocation happens.
  • Destination server: the server on which the EJB is deployed.

Prerequisites

  • Configure the user with required credentials on the destination server. See Adding a Management User in the JBoss EAP Configuration Guide for details.
  • Start the destination server.

    ./standalone.sh -server-config=standalone-full.xml
  • Deploy the application. See Deploying Applications in the JBoss EAP Configuration Guide for details.
Note

Each of your server instances must have a unique jboss.node.name system property. You can set this value by passing it to the startup script:

./standalone.sh -server-config=standalone-full.xml -Djboss.node.name=<add appropriate value here>

5.5.1. Configuring the Client Server

You must let the client server know about the destination server’s EJB remoting connector, over which it can communicate during the EJB invocations. To achieve this, you must add a remote-outbound-connection to the remoting subsystem on the client server. The remote-outbound-connection configuration indicates that an outbound connection will be created to a remote server instance from this client server. The remote-outbound-connection must have an outbound-socket-binding configured with itself, which points to a remote host and a remote port of the destination server.

  1. Start the client server:

    /standalone.sh -server-config=standalone-full.xml -Djboss.socket.binding.port-offset=100
  2. Create a security realm on the client server to communicate with a secure destination server. The client server must provide the user credentials to the destination server. To achieve this, you need to create a security realm on the client server, which will pass the user information provided for the user that was added to the destination server.

    You must use a security realm which stores a base64-encoded password and then passes on these credentials when asked for. You need to create the base64 encoded version of the password that was provided for the user created initially for the destination server. You may use OpenSSL to generate base64-encoded passwords at the command line.

    echo -n “password” | openssl dgst -sha256 -binary | openssl base64

    Here the password in plain text - password - is piped into the OpenSSL digest function then piped into another OpenSSL function to convert into base64-encoded format. You can now use base64-encoded password in the security realm that you configure on the client server.

  3. Run the following management CLI commands to create a security realm for the base64-encoded password:

    /core-service=management/security-realm=ejb-security-realm:add()
    /core-service=management/security-realm=ejb-security-realm/server-identity=secret:add(value=<base64-encoded password>)

    You may notice that the management CLI shows the message "process-state" ⇒ "reload-required", so you must restart the server before this change can be used.

    On successful invocation of this command, the following configuration will be created in the <management> section of standalone.xml:

    <management>
            <security-realms>
                ...
                <security-realm name="ejb-security-realm">
                    <server-identities>
                        <secret value=<base64-encoded password>/>
                    </server-identities>
                </security-realm>
            </security-realms>
    ...

    The code snippet above creates a security realm named ejb-security-realm with the base64-encoded password.

  4. Create an outbound-socket-binding on the client server. You must now create an outbound-socket-binding that points to the destination server’s host and port.

    /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=remote-ejb:add(host=localhost, port=8080)

    The command above creates an outbound-socket-binding named remote-ejb which points to localhost as the host and port 8080 as the destination port. Note that the host information should match the host/IP of the destination server. In this example, we are running the client and destination servers on the same machine so we use localhost. Similarly, the port information should match the http-remoting connector port used by the ejb3 subsystem; by default it is 8080.

    When this command is run successfully, you will see that the standalone-full.xml was updated with the following outbound-socket-binding in the socket-binding-group:

    <socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
           ...
           <outbound-socket-binding name="remote-ejb">
              <remote-destination host="localhost" port="8080"/>
           </outbound-socket-binding>
       </socket-binding-group>
  5. Create a remote-outbound-connection that uses this newly created outbound-socket-binding. Now let us create a remote-outbound-connection which will use the newly created outbound-socket-binding pointing to the EJB remoting connector of the destination server:

    /subsystem=remoting/remote-outbound-connection=remote-ejb-connection:add(outbound-socket-binding-ref=remote-ejb, protocol=http-remoting, security-realm=ejb-security-realm, username=ejb)

    The command above creates a remote-outbound-connection named remote-ejb-connection in the remoting subsystem and uses the previously created remote-ejb outbound-socket-binding. Notice the outbound-socket-binding-ref in the command above, with the http-remoting protocol. Furthermore, we also set the security-realm attribute to point to the security realm that we created in the previous step. Also notice that the username attribute is set to use the user who is allowed to communicate with the destination server.

    This step creates an outbound connection on the client server to the remote destination server and sets up the username to the user who is allowed to communicate with that destination server. It also sets up the security realm to a pre-configured security realm capable of passing along the user credentials (in this case the password). This way when a connection has to be established from the client server to the destination server, the connection creation logic will have the necessary security credentials to pass along and set up a successful secure connection.

    Let us run the following two operations to set some default connection creation options for the outbound connection:

    /subsystem=remoting/remote-outbound-connection=remote-ejb-connection/property=SASL_POLICY_NOANONYMOUS:add(value=false)
    /subsystem=remoting/remote-outbound-connection=remote-ejb-connection/property=SSL_ENABLED:add(value=false)

    Ultimately, upon successful invocation of this command, the following configuration will be created in the remoting subsystem:

    <subsystem xmlns="urn:jboss:domain:remoting:1.1">
    ...
       <outbound-connections>
            <remote-outbound-connection name="remote-ejb-connection" outbound-socket-binding-ref="remote-ejb" protocol="http-remoting" security-realm="ejb-security-realm" username="ejb">
                <properties>
                    <property name="SASL_POLICY_NOANONYMOUS" value="false"/>
                    <property name="SSL_ENABLED" value="false"/>
                </properties>
            </remote-outbound-connection>
        </outbound-connections>
    </subsystem>

    This completes our configuration on the client server. Our next step is to deploy the application on the client server which will invoke the bean deployed on the destination server.

5.5.2. Adding jboss-ejb-client.xml to Client Application

Add the jboss-ejb-client.xml to the client application as META-INF/jboss-ejb-client.xml:

<jboss-ejb-client xmlns="urn:jboss:ejb-client:1.0">
   <client-context>
       <ejb-receivers>
           <remoting-ejb-receiver outbound-connection-ref="remote-ejb-connection"/>
       </ejb-receivers>
   </client-context>
</jboss-ejb-client>

Notice that we have configured the EJB client context for this application to use a remoting-ejb-receiver that points to the remote-outbound-connection named remote-ejb-connection, which we had created earlier. This links the EJB client context to use the remote-ejb-connection pointing to the EJB remoting connector on the destination server.

5.5.3. Invoking the Bean

Following snippet shows how to invoke the bean:

import javax.naming.Context;
import java.util.Hashtable;
import javax.naming.InitialContext;
…
public void invokeOnBean() {
        try {
            final Hashtable props = new Hashtable();
            // setup the ejb: namespace URL factory
            props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
            // create the InitialContext
            final Context context = new javax.naming.InitialContext(props);
            // Lookup the Greeter bean using the ejb: namespace syntax which is explained here https://docs.jboss.org/author/display/AS71/EJB+invocations+from+a+remote+client+using+JNDI
            final Greeter bean = (Greeter) context.lookup("ejb:" + "myapp" + "/" + "myejb" + "/" + "" + "/" + "GreeterBean" + "!" + org.myapp.ejb.Greeter.class.getName());
            // invoke on the bean
            final String greeting = bean.greet("Tom");
            System.out.println("Received greeting: " + greeting);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
}

The code above will invoke the bean deployed on the destination server and return the result.

5.5.4. Deploying the Client Application

Let us deploy the client application on the client server. You can use either the CLI or the admin console or a IDE or deploy manually to EAP_HOME/standalone/deployments folder. Ensure that the application is deployed successfully.

See Deploying Applications in the JBoss EAP Configuration Guide for details.

5.6. Using Scoped EJB Client Contexts

Summary

To invoke an EJB In earlier versions of JBoss EAP, you would typically create a JNDI context and pass it the PROVIDER_URL, which would point to the target server. Any invocations done on EJB proxies that were looked up using that JNDI context, would end up on that server. With scoped EJB client contexts, user applications have control over which EJB receiver is used for a specific invocation.

Use Scoped EJB Client Context in a Remote Standalone Client

Prior to the introduction of scoped EJB client contexts, the context was typically scoped to the client application. Scoped client contexts now allow the EJB client contexts to be scoped with the JNDI contexts. The following is an example of a standalone remote client application that invokes the same bean twice using a scoped EJB client context:

public class MyApplication {
    public static void main(String args[]) {

        // Use the "foo" security credential connect to the server and invoke this bean instance
        final Properties ejbClientContextPropsOne = getPropsForEJBClientContextOne():
        final javax.naming.Context ctxOne = new javax.naming.InitialContext(ejbClientContextPropsOne);
        final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface");
        beanOne.doSomething();
        ...
        ctxOne.close();

        // Use the "bar" security credential to connect to the server and invoke this bean instance
        final Properties ejbClientContextPropsTwo = getPropsForEJBClientContextTwo():
        final javax.naming.Context ctxTwo = new javax.naming.InitialContext(ejbClientContextPropsTwo);
        final MyBeanInterface beanTwo = ctxTwo.lookup("ejb:app/module/distinct/bean!interface");
        beanTwo.doSomething();
        ...
        ctxTwo.close();
    }
}

To use the scoped EJB client context, you configure EJB client properties programmatically and pass the properties on context creation. The properties are the same set of properties that are used in the standard jboss-ejb-client.properties file. To scope the EJB client context to the JNDI context, you must also specify the org.jboss.ejb.client.scoped.context property and set its value to true. This property notifies the EJB client API that it must create an EJB client context, which is backed by EJB receivers, and that the created context is then scoped or visible only to the JNDI context that created it. Any EJB proxies looked up or invoked using this JNDI context will only know of the EJB client context associated with this JNDI context. Other JNDI contexts used by the application to lookup and invoke EJBs will not know about the other scoped EJB client contexts.

JNDI contexts that do not pass the org.jboss.ejb.client.scoped.context property and are not scoped to an EJB client context will use the default behavior, which is to use the existing EJB client context that is typically tied to the entire application.

Scoped EJB client contexts provide user applications with the flexibility that was associated with the JNP based JNDI invocations in previous versions of JBoss EAP. It provides user applications with more control over which JNDI context communicates to which server and how it connects to that server.

Note

With the scoped context, the underlying resources are no longer handled by the container or the API, so you must close the InitialContext when it is no longer needed. When the InitialContext is closed, the resources are released immediately. The proxies that are bound to it are no longer valid and any invocation will throw an Exception. Failure to close the InitialContext may result in resource and performance issues.

5.6.1. Configure EJBs Using a Scoped EJB Client Context

EJBs can be configured using a map-based scoped context. This is achieved by programmatically populating a properties map using the standard properties found in jboss-ejb-client.properties, specifying true for the org.jboss.ejb.client.scoped.context property, and passing the properties on the InitialContext creation.

The benefit of using a scoped context is that it allows you to configure access without directly referencing the EJB or importing JBoss classes. It also provides a way to configure and load balance a host at runtime in a multithreaded environment.

Configure an EJB Using a Map-Based Scoped Context
  1. Set the properties:

    Configure the EJB client properties programmatically, specifying the same set of properties that are used in the standard jboss-ejb-client.properties file. To enable the scoped context, you must specify the org.jboss.ejb.client.scoped.context property and set its value to true. Following is an example that configures the properties programmatically.

    // Configure  EJB Client properties for the InitialContext
    Properties ejbClientContextProps = new Properties();
    ejbClientContextProps.put("remote.connections","name1");
    ejbClientContextProps.put("remote.connection.name1.host","localhost");
    ejbClientContextProps.put("remote.connection.name1.port","8080");
    // Property to enable scoped EJB client context which will be tied to the JNDI context
    ejbClientContextProps.put("org.jboss.ejb.client.scoped.context", "true");
  2. Pass the properties on the context creation:

    // Create the context using the configured properties
    InitialContext ic = new InitialContext(ejbClientContextProps);
    MySLSB bean = ic.lookup("ejb:myapp/ejb//MySLSBBean!" + MySLSB.class.getName());
  3. Close the scoped EJB client context:

    Look up the root JNDI context for ejb: string to fetch the EJB naming context. Then use ejbRootNamingContext instance to look up the rest of the EJB JNDI name to fetch the EJB proxy. Use the close() method to close ejbRootNamingContext and the EJB JNDI context. Closing ejbRootNamingContext ensures that the scoped EJB client context associated with the JNDI context is closed too. Effectively, this closes the connection to the server within that EJB client context.

    final Properties props = new Properties();
    // mark it for scoped EJB client context
    props.put("org.jboss.ejb.client.scoped.context","true");
    // add other properties
    props.put(...);
    ...
    Context jndiCtx = new InitialContext(props);
    Context ejbRootNamingContext = (Context) jndiCtx.lookup("ejb:");
    try {
        final MyBean bean = (MyBean)ejbRootNamingContext.lookup("app/module/distinct/bean!interface");
    } finally {
        try { // close the EJB naming JNDI context
            ejbRootNamingContext.close();
        } catch (Throwable t) {
            // log and ignore
        }
        try { // also close our other JNDI context since we are done with it too
            jndiCtx.close();
        } catch (Throwable t) {
            // log and ignore
        }
    }

Contexts generated by lookup EJB proxies are bound by this scoped context and use only the relevant connection parameters. This makes it possible to create different contexts to access data within a client application or to independently access servers using different logins.

In the client, both the scoped InitialContext and the scoped proxy are passed to threads, allowing each thread to work with the given context. It is also possible to pass the proxy to multiple threads that can use it concurrently.

The scoped context EJB proxy is serialized on the remote call and then deserialized on the server. When it is deserialized, the scoped context information is removed and it returns to its default state. If the deserialized proxy is used on the remote server, because it no longer has the scoped context that was used when it was created, this can result in an EJBCLIENT000025 error or possibly call an unwanted target by using the EJB name.

5.7. EJB Client Properties

Summary

The following tables list properties that can be configured programmatically or in the jboss-ejb-client.properties file.

EJB Client Global Properties

The following table lists properties that are valid for the whole library within the same scope.

Table 5.1. Global Properties

Property NameDescription

endpoint.name

Name of the client endpoint. If not set, the default value is client-endpoint.

This can be helpful to distinguish different endpoint settings because the thread name contains this property.

remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED

Boolean value that specifies whether the SSL protocol is enabled for all connections.

Warning

Red Hat recommends that SSLv2, SSLv3, and TLSv1.0 be explicitly disabled in favor of TLSv1.1 or TLSv1.2 in all affected packages.

deployment.node.selector

The fully qualified name of the implementation of org.jboss.ejb.client.DeploymentNodeSelector.

This is used to load balance the invocation for the EJBs.

invocation.timeout

The timeout for the EJB handshake or method invocation request/response cycle. The value is in milliseconds.

The invocation of any method throws a java.util.concurrent.TimeoutException if the execution takes longer than the timeout period. The execution completes and the server is not interrupted.

reconnect.tasks.timeout

The timeout for the background reconnect tasks. The value is in milliseconds.

If a number of connections are down, the next client EJB invocation will use an algorithm to decide if a reconnect is necessary to find the right node.

org.jboss.ejb.client.scoped.context

Boolean value that specifies whether to enable the scoped EJB client context. The default value is false.

If set to true, the EJB Client will use the scoped context that is tied to the JNDI context. Otherwise the EJB client context will use the global selector in the JVM to determine the properties used to call the remote EJB and host.

EJB Client Connection Properties

The connection properties start with the prefix remote.connection. where the CONNECTION_NAME is a local identifier only used to uniquely identify the connection.

Table 5.2. Connection Properties

Property NameDescription

remote.connections

A comma-separated list of active connection-names. Each connection is configured by using this name.

remote.connection.CONNECTION_NAME.host

The host name or IP for the connection.

remote.connection.CONNECTION_NAME.port

The port for the connection. The default value is 8080.

remote.connection.CONNECTION_NAME.username

The user name used to authenticate connection security.

remote.connection.CONNECTION_NAME.password

The password used to authenticate the user.

remote.connection.CONNECTION_NAME.connect.timeout

The timeout period for the initial connection. After that, the reconnect task will periodically check whether the connection can be established. The value is in milliseconds.

remote.connection.CONNECTION_NAME.callback.handler.class

Fully qualified name of the CallbackHandler class. It will be used to establish the connection and can not be changed as long as the connection is open.

remote.connection.CONNECTION_NAME.channel.options.org.jboss.remoting3.RemotingOptions.MAX_OUTBOUND_MESSAGES

Integer value specifying the maximum number of outbound requests. The default is 80.

There is only one connection from the client (JVM) to the server to handle all invocations.

remote.connection.CONNECTION_NAME.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS

Boolean value that determines whether credentials must be provided by the client to connect successfully. The default value is true.

If set to true, the client must provide credentials. If set to false, invocation is allowed as long as the remoting connector does not request a security realm.

remote.connection.CONNECTION_NAME.connect.options.org.xnio.Options.SASL_DISALLOWED_MECHANISMS

Disables certain SASL mechanisms used for authenticating during connection creation.

JBOSS-LOCAL-USER means the silent authentication mechanism, used when the client and server are on the same machine, is disabled.

remote.connection.CONNECTION_NAME.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT

Boolean value that enables or disables the use of plain text messages during the authentication. If using JAAS, it must be set to false to allow a plain text password.

remote.connection.CONNECTION_NAME.connect.options.org.xnio.Options.SSL_ENABLED

Boolean value that specifies whether the SSL protocol is enabled for this connection.

Warning

Red Hat recommends that SSLv2, SSLv3, and TLSv1.0 be explicitly disabled in favor of TLSv1.1 or TLSv1.2 in all affected packages.

remote.connection.CONNECTION_NAME.connect.options.org.jboss.remoting3.RemotingOptions.HEARTBEAT_INTERVAL

Interval to send a heartbeat between client and server to prevent automatic close, for example, in the case of a firewall. The value is in milliseconds.

EJB Client Cluster Properties

If the initial connection connects to a clustered environment, the topology of the cluster is received automatically and asynchronously. These properties are used to connect to each received member. Each property starts with the prefix remote.cluster. where the CLUSTER_NAME refers to the related to the servers infinispan subsystem configuration.

Table 5.3. Cluster Properties

Property NameDescription

remote.cluster.CLUSTER_NAME.clusternode.selector

The fully qualified name of the implementation of org.jboss.ejb.client.ClusterNodeSelector.

This class, rather than org.jboss.ejb.client.DeploymentNodeSelector, is used to load balance EJB invocations in a clustered environment. If the cluster is completely down, the invocation will fail with the message No ejb receiver available.

remote.cluster.CLUSTER_NAME.channel.options.org.jboss.remoting3.RemotingOptions.MAX_OUTBOUND_MESSAGES

Integer value specifying the maximum number of outbound requests that can be made to the entire cluster.

remote.cluster.CLUSTER_NAME.node.NODE_NAME. channel.options.org.jboss.remoting3.RemotingOptions.MAX_OUTBOUND_MESSAGES

Integer value specifying the maximum number of outbound requests that can be made to this specific cluster-node.

5.8. Remote EJB Data Compression

Previous versions of JBoss EAP included a feature where the message stream that contained the EJB protocol message could be compressed. This feature has been included in JBoss EAP 6.3 and later.

Note

Compression currently can only be specified by annotations on the EJB interface which should be on the client and server side. There is not currently an XML equivalent to specify compression hints.

Data compression hints can be specified via the JBoss annotation org.jboss.ejb.client.annotation.CompressionHint. The hint values specify whether to compress the request, response or request and response. Adding @CompressionHint defaults to compressResponse=true and compressRequest=true.

The annotation can be specified at the interface level to apply to all methods in the EJB’s interface such as:

import org.jboss.ejb.client.annotation.CompressionHint;

@CompressionHint(compressResponse = false)
public interface ClassLevelRequestCompressionRemoteView {
    String echo(String msg);
}

Or the annotation can be applied to specific methods in the EJB’s interface such as:

import org.jboss.ejb.client.annotation.CompressionHint;

public interface CompressableDataRemoteView {

    @CompressionHint(compressResponse = false, compressionLevel = Deflater.BEST_COMPRESSION)
    String echoWithRequestCompress(String msg);

    @CompressionHint(compressRequest = false)
    String echoWithResponseCompress(String msg);

    @CompressionHint
    String echoWithRequestAndResponseCompress(String msg);

    String echoWithNoCompress(String msg);
}

The compressionLevel setting shown above can have the following values:

  • BEST_COMPRESSION
  • BEST_SPEED
  • DEFAULT_COMPRESSION
  • NO_COMPRESSION

The compressionLevel setting defaults to Deflater.DEFAULT_COMPRESSION.

Class level annotation with method level overrides:

@CompressionHint
public interface MethodOverrideDataCompressionRemoteView {

    @CompressionHint(compressRequest = false)
    String echoWithResponseCompress(final String msg);

    @CompressionHint(compressResponse = false)
    String echoWithRequestCompress(final String msg);

    String echoWithNoExplicitDataCompressionHintOnMethod(String msg);
}

On the client side ensure the org.jboss.ejb.client.view.annotation.scan.enabled system property is set to true. This property tells JBoss EJB Client to scan for annotations.

5.9. EJB Client Remoting Interoperability

The default remote connection port is 8080. The jboss-ejb-client properties file looks like this:

remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=localhost
remote.connection.default.port=8080
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false

Default Connector

The default connector is http-remoting.

  • If a client application uses the EJB client library from JBoss EAP 6 and wants to connect to a JBoss EAP 7 server, the server must be configured to expose a remoting connector on a port other than 8080. The client must then connect using that newly configured connector.
  • A client application that uses the EJB client library from JBoss EAP 7 and wants to connect to a JBoss EAP 6 server must be aware that the server instance does not use the http-remoting connector and instead uses a remoting connector. This is achieved by defining a new client-side connection property.

    remote.connection.default.protocol=remote
Note

EJB remote calls are supported for JBoss EAP 7 with JBoss EAP 6 only.

Besides EJB client remoting interoperability, you can connect to legacy clients using the following options:

5.10. Configure IIOP for Remote EJB Calls

JBoss EAP supports CORBA/IIOP-based access to EJBs deployed on JBoss EAP.

The <iiop> element is used to enable IIOP, CORBA, invocation of EJBs. The presence of this element means that the iiop-openjdk subsystem is installed. The <iiop> element includes the following two attributes:

  • enable-by-default: If this is true, then all the EJBs with EJB 2.x home interfaces are exposed through IIOP. Otherwise they must be explicitly enabled through jboss-ejb3.xml.
  • use-qualified-name: If this is true, then the EJBs are bound to the CORBA naming context with a binding name that contains the application and modules name of the deployment, such as myear/myejbjar/MyBean. If this is false, then the default binding name is simply the bean name.
Important

IIOP calls can be done only with EJB 2 beans. EJB 3 beans are not supported by IIOP in JBoss EAP 7.0.

Enabling IIOP

To enable IIOP you must have the IIOP OpenJDK ORB subsystem installed, and the <iiop/> element present in the ejb3 subsystem configuration. The standalone-full.xml configuration that comes with the distribution has both of these enabled.

IIOP is configured in the iiop-openjdk subsystem of the server configuration file.

<subsystem xmlns="urn:jboss:domain:iiop-openjdk:1.0">

Use the following management CLI command to access and update the iiop-openjdk subsystem.

/subsystem=iiop-openjdk

The IIOP element takes two attributes that control the default behavior of the server.

<subsystem xmlns="urn:jboss:domain:ejb3:1.2">
  ...
  <iiop enable-by-default="false" use-qualified-name="false"/>
  ...
</subsystem>

The following management CLI command adds the <iiop> element under the ejb3 subsystem:

/subsystem=ejb3/service=iiop:add(enable-by-default=false, use-qualified-name=false)

Create an EJB That Communicates Using IIOP

The following example demonstrates how to make a remote IIOP call from the client:

  1. Create an EJB 2 bean on the server:

    @Remote(IIOPRemote.class)
    @RemoteHome(IIOPBeanHome.class)
    @Stateless
    public class IIOPBean {
        public String sayHello() throws RemoteException {
             return "hello";
        }
    }
  2. Create a home implementation, which has a mandatory method create(). This method is called by the client to obtain proxy of remote interface to invoke business methods:

    public interface IIOPBeanHome extends EJBHome {
        public IIOPRemote create() throws RemoteException;
    }
  3. Create a remote interface for remote connection to the EJB:

    public interface IIOPRemote extends EJBObject {
        String sayHello() throws RemoteException;
    }
  4. Introduce the bean for remote call by creating a descriptor file jboss-ejb3.xml in META-INF:

    <?xml version="1.0" encoding="UTF-8"?>
    <jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
                   xmlns="http://java.sun.com/xml/ns/javaee"
                   xmlns:iiop="urn:iiop"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd
                      http://java.sun.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-spec-2_0.xsd
                      urn:iiop jboss-ejb-iiop_1_0.xsd"
                   version="3.1"
                   impl-version="2.0">
        <assembly-descriptor>
            <iiop:iiop>
                <ejb-name>*</ejb-name>
            </iiop:iiop>
        </assembly-descriptor>
    </jboss:ejb-jar>
    Note

    The packed beans along with the descriptor in the JAR file is now ready to be deployed to the JBoss EAP container.

  5. Create a context at the client side:

    System.setProperty("com.sun.CORBA.ORBUseDynamicStub", "true");
    final Properties props = new Properties();
    props.put(Context.PROVIDER_URL, "corbaloc::localhost:3528/JBoss/Naming/root");
    props.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.iiop.naming:org.jboss.naming.client");
    props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory");
    props.put(Context.OBJECT_FACTORIES, "org.jboss.tm.iiop.client.IIOPClientUserTransactionObjectFactory");
    Note

    The client will need to have the wildfly iiop openjdk library added to its class path. The client might also need to add the org.wildfly:wildfly-iiop-openjdk artifact as Maven dependency.

  6. Use the context lookup to narrow the reference to the IIOPBeanHome home interface. Then call the home interface create() method to access the remote interface, which allows you to call its methods:

    try {
        Context context = new InitialContext(props);
    
        final Object iiopObj = context.lookup(IIOPBean.class.getSimpleName());
        final IIOPBeanHome beanHome = (IIOPBeanHome) PortableRemoteObject.narrow(iiopObj, IIOPBeanHome.class);
        final IIOPRemote bean = beanHome.create();
    
        System.out.println("Bean saying: " + bean.sayHello());
    } catch (Exception e) {
        e.printStackTrace();
    }