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;
}
}
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
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
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 Jakarta Enterprise Beans 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-remotingconnector and instead uses aremotingconnector. This is achieved by defining a new client-side connection property.remote.connection.default.protocol=remote
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:
- Configure the ORB for JTS Transactions in the JBoss EAP Configuration Guide.
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 istrue, then all the Jakarta Enterprise Beans with Enterprise Beans 2.x home interfaces are exposed through IIOP. Otherwise they must be explicitly enabled throughjboss-ejb3.xml. -
use-qualified-name: If this istrue, 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 asmyear/myejbjar/MyBean. If this isfalse, then the default binding name is simply the bean name.
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:
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"; } }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; }Create a remote interface for remote connection to the Enterprise Bean:
public interface IIOPRemote extends EJBObject { String sayHello() throws RemoteException; }Introduce the bean for remote call by creating a descriptor file
jboss-ejb3.xmlinMETA-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>NoteThe packed beans along with the descriptor in the JAR file is now ready to be deployed to the JBoss EAP container.
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");NoteThe client will need to have the
wildfly iiop openjdklibrary added to its class path. The client might also need to add theorg.wildfly:wildfly-iiop-openjdkartifact as Maven dependency.Use the context lookup to narrow the reference to the
IIOPBeanHomehome interface. Then call the home interfacecreate()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:
-
matchis a Classless Inter-Domain Routing (CIDR) block, such as10.0.0.0/8,ff00::\8,0.0.0.0/0,::/0. -
bind-addressspecifies the IP address to bind to when the destination address matches the CIDR block specified in thematchparameter. It should be the same address family as the CIDR block. bind-portis an optional source port number that defaults to0.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-factorythat must be a reference to an Elytronhttp-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.
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.