Chapter 5. Invoking Session Beans

5.1. About Jakarta Enterprise Beans Client Contexts

JBoss EAP introduced the Jakarta Enterprise Beans client API for managing remote Jakarta Enterprise Beans invocations. The JBoss Jakarta Enterprise Beans client API uses the EJBClientContext, which may be associated with and be used by one or more threads concurrently. This means that an EJBClientContext can potentially contain any number of Jakarta Enterprise Beans receivers. A Jakarta Enterprise Beans receiver is a component that knows how to communicate with a server that is capable of handling the Jakarta Enterprise Beans invocation. Typically, Jakarta Enterprise Beans 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 a Jakarta Enterprise Beans 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 Jakarta Enterprise Beans receivers, this isn’t mandatory. If a standalone application has more than one EJBClientContext, a Jakarta Enterprise Beans 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 Jakarta Enterprise Beans client context. Whenever that application invokes another Jakarta Enterprise Beans, the corresponding Jakarta Enterprise Beans client context is used to find the correct Jakarta Enterprise Beans receiver, which then handles the invocation.

5.2. Using Remote Jakarta Enterprise Beans Clients

5.2.1. Initial Context Lookup

You can pass the remote server’s address using the PROVIDER_URL property when creating an initial context:

public class Client {
    public static void main(String[] args)
            throws NamingException, PrivilegedActionException, InterruptedException {
        InitialContext ctx = new InitialContext(getCtxProperties());
        String lookupName = "ejb:/server/HelloBean!ejb.HelloBeanRemote";
        HelloBeanRemote bean = (HelloBeanRemote)ctx.lookup(lookupName);
        System.out.println(bean.hello());
        ctx.close();
    }
    public static Properties getCtxProperties() {
        Properties props = new Properties();
        props.put(Context.INITIAL_CONTEXT_FACTORY, WildFlyInitialContextFactory.class.getName());
        props.put(Context.PROVIDER_URL, "remote+http://127.0.0.1:8080");
        props.put(Context.SECURITY_PRINCIPAL, "joe");
        props.put(Context.SECURITY_CREDENTIALS, "joeIsAwesome2013!");
        return props;
    }
}
Note

The Initial context factory to be used for the lookup is org.wildfly.naming.client.WildFlyInitialContextFactory.

5.2.2. Remote Jakarta Enterprise Beans Configuration File

JBoss EAP features the Elytron security framework. The wildfly-config.xml file, which is present in the META-INF/ directory of the client application’s class path, allows a wide range of authentication and authorization options for the Elytron security framework and Jakarta Enterprise Beans client configuration.

<configuration>
   <authentication-client xmlns="urn:elytron:client:1.2">
      <authentication-rules>
         <rule use-configuration="default" />
      </authentication-rules>
      <authentication-configurations>
         <configuration name="default">
            <sasl-mechanism-selector selector="DIGEST-MD5" />
            <set-user-name name="admin" />
            <credentials>
               <clear-password password="password123!" />
            </credentials>
         </configuration>
      </authentication-configurations>
   </authentication-client>
   <jboss-ejb-client xmlns="urn:jboss:wildfly-client-ejb:3.0">
      <connections>
         <connection uri="remote+http://127.0.0.1:8080" />
      </connections>
   </jboss-ejb-client>
</configuration>

As an alternative to embedding the PROVIDER_URL, SECURITY_PRINCIPAL and SECURITY_CREDENTIALS parameters in the initial context, you can use the <connection-uri> and <authentication-client> elements in the wildfly-config.xml file to configure the connection URI and the security settings, respectively.

5.2.3. The ClientTransaction Annotation

The @org.jboss.ejb.client.annotation.ClientTransaction annotation handles transaction propagation from a Jakarta Enterprise Beans client. You can mandate the propagation to fail if the client has no transaction, or prevent the transaction propagation even if the client has one active. You can use the constants of the org.jboss.ejb.client.annotation.ClientTransactionPolicy interface to control the policy of the ClientTransaction annotation. The following are the constants of the org.jboss.ejb.client.annotation.ClientTransactionPolicy interface:

  • MANDATORY: Fail with exception when there is no client-side transaction context; propagate the client-side transaction context when it is present.
  • NEVER: Invoke without propagating any transaction context; if a client-side transaction context is present, an exception is thrown.
  • NOT_SUPPORTED: Invoke without propagating any transaction context whether or not a client-side transaction context is present.
  • SUPPORTS: Invoke without a transaction if there is no client-side transaction context; propagate the client-side transaction context if it is present.

If no annotation is present, the default policy is org.jboss.ejb.client.annotation.ClientTransactionPolicy#SUPPORTS, which means that the transaction is propagated if it is present, but the propagation does not fail, regardless of whether a transaction is present or not.

@ClientTransaction(ClientTransactionPolicy.MANDATORY)
@Remote
public interface RemoteCalculator  {
   public void callRemoteEjb() { }
}
@Stateless
@Remote(RemoteCalculator.class)
public class CalculatorBean implements RemoteCalculator {

   @Override
   public void callRemoteEjb()  {   }
}

The annotation allows the remote interface provider to tell the remote interface consumer whether transactions are needed for a method.

5.3. Remote Jakarta Enterprise Beans Data Compression

You can compress message streams containing Enterprise Beans protocol message

Note

Compression currently can only be specified by annotations on the Jakarta Enterprise Beans 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 Jakarta Enterprise Beans 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 Jakarta Enterprise Beans 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 Jakarta Enterprise Beans Client to scan for annotations.

5.4. Jakarta Enterprise Beans client remoting interoperability

Remote Jakarta Enterprise Beans client applications use connectors defined in the remoting subsystem in order to connect to the server. You can use one of the following connectors based on your needs:

  • http-connector: Supports client connections to the server through the HTTP upgrade feature of undertow with default port 8080. With this connector configured, the client uses the remote+http URI scheme for unencrypted connections or the remote+https URI scheme for encrypted connections.
  • connector: Supports client connections to the server through the legacy remote URI scheme. This connector is suitable for use with older Jakarta Enterprise Beans client applications.
Note

Besides using the previous remoting-based connectors, Jakarta Enterprise Beans clients can connect to the server through undertow and the HTTP protocol by using the http URI scheme. For more details, see Jakarta Enterprise Beans Invocation Over HTTP.

Default HTTP connector

The default connector is http-connector, which requires that the client use the URI scheme remote+http or remote+https. The default remote connection port is 8080, which is the same default port for undertow. The following example shows a jboss-ejb-client properties file:

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

Supporting clients from different JBoss EAP versions

If a client application uses the Jakarta Enterprise Beans client library from JBoss EAP 6 and requires a connection to a JBoss EAP 7 server, you must configure the server to expose a remoting connector on a port other than 8080. The client must then connect by using that newly configured connector.

A client application that uses the Jakarta Enterprise Beans client library from JBoss EAP 7 and requires a connection to a JBoss EAP 6 server must be aware that the server instance does not use the remoting http-remoting connector and instead uses a remoting connector connector. This is achieved by defining the following new client-side connection property:

remote.connection.default.protocol=remote

Supporting multiple connectors for Jakarta Enterprise Beans client applications

Prior to JBoss EAP 7.4, Jakarta Enterprise Beans client applications were limited to using only one remoting connector, defined in the remoting subsystem, to connect to a server. This connector was specified in the connector-ref attribute of the remote element of the ejb3 subsystem. You could either use the default http-connector for providing a connection through the HTTP upgrade feature of undertow with the remote+http protocol or use a legacy connector for providing a connection through the legacy remote protocol.

With JBoss EAP 7.4, you can specify a list of connectors that Jakarta Enterprise Beans clients can use for connection purposes. To specify this list, use the new connectors attribute of the remote element. The connectors attribute accepts a list of connectors defined in the remoting subsystem. This enables a single server to provide multiple connections for Jakarta Enterprise Beans client applications. For example, clients compatible with EAP 7.2 or later can connect to the server using the remote+http protocol with an http-connector and legacy clients compatible with prior versions of EAP 7.2 can connect using the legacy remote protocol with a connector.

Example

Considering legacy-remoting-connector is a connector defined in the remoting subsystem, the following example demonstrates the ejb3 subsystem configuration that updates the value of the remoting connectors by using the write attribute:

/subsystem=ejb3/service=remote:write-attribute(name=connectors, value=[http-remoting-connector, legacy-remoting-connector])

You can view the domain.xml or standalone.xml file to check the configured remoting connectors in the ejb3 subsystem:

<remote cluster="ejb" connectors="http-remoting-connector legacy-remoting-connector" thread-pool-name="default">
  <channel-creation-options>
   <option name="MAX_OUTBOUND_MESSAGES" value="1234" type="remoting"/>
  </channel-creation-options>
</remote>
Note

Jakarta Enterprise Beans remote calls are supported for JBoss EAP 7 with JBoss EAP 6 only.

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

5.5. Configure IIOP for Remote Jakarta Enterprise Beans Calls

JBoss EAP supports CORBA/IIOP-based access to Jakarta Enterprise Beans deployed on JBoss EAP.

The <iiop> element is used to enable IIOP, CORBA, invocation of Jakarta Enterprise Beans. 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 Jakarta Enterprise Beans with Enterprise Beans 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 Jakarta Enterprise Beans 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.
Note

Even though a RemoteHome interface is not normally required for Jakarta Enterprise Beans 3 remote calls, it is required for any Jakarta Enterprise Beans 3 bean that is exposed using IIOP. You must then enable IIOP using the jboss-ejb3.xml file, or by enabling IIOP for all Jakarta Enterprise Beanss in the standalone-full.xml configuration file.

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:2.1">

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:5.0">
  ...
  <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 a Jakarta Enterprise Beans That Communicates Using IIOP

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

  1. Create an Enterprise Beans 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 Enterprise Bean:

    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();
    }

5.6. Configure the Jakarta Enterprise Beans Client Address

You can determine the Jakarta Enterprise Beans client address using the SessionContext interface, as shown in the example below.

public class HelloBean implements HelloBeanRemote {
    @Resource
    SessionContext ctx;
    private Long counter;
    public HelloBean() {
    }
    @PostConstruct
    public void init() {
        counter = 0L;
    }
    @Override
    @RolesAllowed("users")
    public String hello() {
        final String message = "method hello() invoked by user " + ctx.getCallerPrincipal().getName()
                + ", source addr = " +  ctx.getContextData().get("jboss.source-address").toString();
        System.out.println(message);
        return message;
    }
}

Standalone Client Configuration

You can configure the outbound-bind-addresses element within the worker element having namespace urn:xnio:3.5 in the wildfly-config.xml file. The bind-address sub-element takes the attributes match, bind-address, bind-port, as defined below.

The following is an example of the standalone client configuration using the wildfly-config.xml file.

<configuration>
    <worker xmlns="urn:xnio:3.5">
        <worker-name value="default"/>
        <outbound-bind-addresses>
            <bind-address bind-address=IP_ADDRESS_TO_BIND_TO bind-port=OPTIONAL_SOURCE_PORT_NUMBER match=CIDR_BLOCK />
        </outbound-bind-addresses>
    </worker>
</configuration>

The outbound-bind-address requires the following attributes:

  • match is a Classless Inter-Domain Routing (CIDR) block, such as 10.0.0.0/8, ff00::\8, 0.0.0.0/0, ::/0.
  • bind-address specifies the IP address to bind to when the destination address matches the CIDR block specified in the match parameter. It should be the same address family as the CIDR block.
  • bind-port is an optional source port number that defaults to 0.

    If no matching expression exists, then the outbound socket is not explicitly bound.

Container-based Configuration

Container-based configuration of the Jakarta Enterprise Beans client address is similar to the standalone client configuration defined in the wildfly-config.xml file.

The example below configures the outbound-bind-address on the default worker element of the io subsystem, which the ejb3 subsystem uses by default.

/subsystem=io/worker=default/outbound-bind-address=SPECIFY_OUTBOUND_BIND_ADDRESS:add(bind-address=IP_ADDRESS_TO_BIND_TO, bind-port=OPTIONAL_SOURCE_PORT_NUMBER, match=CIDR_BLOCK)

5.7. Jakarta Enterprise Beans Invocation Over HTTP

Jakarta Enterprise Beans invocation over HTTP includes two distinct parts: the client-side and the server-side implementations.

5.7.1. Client-side Implementation

The client-side implementation consists of an EJBReceiver that uses the Undertow HTTP client to invoke the server. Connection management is handled automatically using a connection pool.

In order to configure an Jakarta Enterprise Beans client application to use HTTP transport, you must add the following dependency on the HTTP transport implementation:

<dependency>
    <groupId>org.wildfly.wildfly-http-client</groupId>
    <artifactId>wildfly-http-ejb-client</artifactId>
</dependency>

To perform the HTTP invocation, you must use the http URL scheme and include the context name of the HTTP invoker, wildfly-services. For example, if you are using remote+http://localhost:8080 as the target URL, in order to use the HTTP transport, you must update this to http://localhost:8080/wildfly-services.

5.7.2. Server-side Implementation

The server-side implementation consists of a service that handles the incoming HTTP requests, unmarshals them and passes the result to the internal Jakarta Enterprise Beans invocation code.

In order to configure the server, the http-invoker must be enabled on each of the virtual hosts that you wish to use in the undertow subsystem. This is enabled by default in the standard configurations. If it is disabled, it can be re-enabled using the following management CLI command:

/subsystem=undertow/server=default-server/host=default-host/setting=http-invoker:add(http-authentication-factory=myfactory, path="wildfly-services")

http-invoker has two attributes: a path that defaults to wildfly-services, and one of the following:

  • An http-authentication-factory that must be a reference to an Elytron http-authentication-factory, as shown in the above command.
  • A legacy security-realm.

Note that the above two attributes are mutually exclusive: you cannot specify both an http-authentication-factory and a security-realm at the same time.

Note

Any deployment that aims to use the http-authentication-factory must use Elytron security with the same security domain corresponding to the specified HTTP authentication factory.