How configure an EJB client in EAP 7.1+ / 8.0+

Solution Verified - Updated

Environment

  • Red Hat JBoss Enterprise Application Platform (EAP)
    • 8
    • 7.1+

Issue

  • How to configure remote EJB client in EAP 8.0 + ?
  • How to do server-to-server EJB in EAP 7.4+?
  • How to do server-to-server EJB in EAP 7.3+?
  • How to do server-to-server EJB in EAP 7.2+?
  • How configure an EJB client in EAP 7.2
  • How configure an EJB client in EAP 7.1

Resolution

Table of contents

  1. Using the WildFlyInitialContextFactory (programatic)
  1. Using server to server in the JBoss profile (configured in the EAP config)
  1. Notes
  1. FAQ

Note

If the EJB is in the same JVM as the Client that is calling it, then the Client should follow the JavaEE spec and defined Portable JNDI Naming Syntax such as java:global/, java:app/, java:module as described in What are the allowed JNDI name formats for EJBs in EAP 7 / 6 according to EE6+

For example:

public void callEjbRemoteInterfaceInVM() {
   HelloRemote ejbRemote = new InitialContext().lookup("java:global/helloWorld/helloWorld-ejb/HelloWorldSLSB!org.jboss.examples.ejb.HelloRemote");
      ejbRemote.helloWorld();
}
public void callEjbLocalInterfaceInVM() {
   HelloRemote ejbLocal = new InitialContext().lookup("java:global/helloWorld/helloWorld-ejb/HelloWorldSLSB!org.jboss.examples.ejb.HelloLocal");
      ejbLocal.helloWorld();
}

If the Client is not in the same JVM as the EJB, then the 'ejb:' / ejb-client methods below would be used or the EJB over IIOP spec method as described in: How to expose an EJB via IIOP and call it in JBoss EAP 7 / 6

Using the WildFlyInitialContextFactory

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;

import org.jboss.ejb.client.EJBClient;

    public void callRemoteEjb() {
        HelloRemote remote = getInitialContext(host, port, user, pass).lookup("ejb:helloWorld/helloWorld-ejb/HelloWorldSLSB!org.jboss.examples.ejb.HelloRemote");
        remote.helloWorld();
    }
    public static Context getInitialContext(String host, Integer port, String username, String password)
            throws NamingException {
        Hashtable<String, Object> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
        env.put(Context.PROVIDER_URL, String.format("remote+http://%s:%d", host, port));
        if (username != null && password != null) {
            env.put(Context.SECURITY_PRINCIPAL, username);
            env.put(Context.SECURITY_CREDENTIALS, password);
        }
        return new InitialContext(env);
    }

Lookup Failover

In order to manage Lookup High Availability, you can provide a list of remote servers that will be checked for the Initial Lookup of the remote+http call. Here is the updated getInitialContext() method, supposing you were to contact two servers located at localhost:8080 and localhost:8180:

    public static Context getInitialContext(String host, Integer port, String username, String password)
            throws NamingException {
        Hashtable<String, Object> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
        env.put(Context.PROVIDER_URL, String.format("remote+http://%s:%d", host, port));
        if (username != null && password != null) {
            env.put(Context.SECURITY_PRINCIPAL, username);
            env.put(Context.SECURITY_CREDENTIALS, password);
        }
        return new InitialContext(env);
    }

Using the wildfly-config.xml and WildFlyInitialContextFactory (standalone client only)

The wildfly-config.xml can be used to specify a static configuration for a standalone client to use. Below is a simple example specifying plaintext username / password and the remote server connection (host/port). The wildfly-config.xml goes in the META-INF directory.

<configuration>
  <authentication-client xmlns="urn:elytron:1.0.1">
    <authentication-rules>
      <rule use-configuration="default"/>
    </authentication-rules>
    <authentication-configurations>
      <configuration name="default">
        <set-user-name name="ejbuser"/>
        <credentials>
          <clear-password password="redhat1!"/>
        </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>

Using the wildfly-config.xml, the InitialContext creation does not specify the host/port/user/pass since it is already defined in the wildfly-config.xml. The EJB lookup

public static Context getInitialContext(String host, Integer port, String username, String password) throws NamingException {
   Hashtable env = new Hashtable();
   env.put(Context.INITIAL_CONTEXT_FACTORY,  "org.wildfly.naming.client.WildFlyInitialContextFactory");
   return new InitialContext(env);
}
public void callRemoteEjb() {
   HelloRemote remote = getInitialContext(host, port, user, pass).lookup("ejb:helloWorld/helloWorld-ejb/HelloWorldSLSB!org.jboss.examples.ejb.HelloRemote");
      remote.helloWorld();
}

Configuring server to server in the JBoss profile

All of the following pieces are required to correctly configure EJB clients from within the JBoss container.

Client application code
        // create the InitialContext
        final Context context = new javax.naming.InitialContext();
        final Greeter bean = (Greeter) context.lookup("ejb:myapp/myejb/GreeterBean!" + org.myapp.ejb.Greeter.class.getName());

JBoss Profile configuration: This is configuration that goes in your standalone.xml or domain.xml.

Elytron Example Configuration (EAP 8 / EAP 7.1+)

Client application configuration
  • jboss-ejb-client.xml goes in the WEB-INF if the top level deployment is a war or in the top level deployment's META-INF if not a war
            <jboss-ejb-client xmlns:xsi="urn:jboss:ejb-client:1.4" xsi:noNamespaceSchemaLocation="jboss-ejb-client_1_4.xsd">
                <client-context>
                    <profile name="remote-ejb-profile" />
                </client-context>
            </jboss-ejb-client>
EJB-client-server configuration
  1. Add an Elytron Authentication Configuration for the credentials to connect to the remote EAP instance

Standalone Mode

/subsystem=elytron/authentication-configuration=ejbauth:add(authentication-name=server, security-domain=ApplicationDomain, forwarding-mode=authorization, realm=ApplicationRealm, credential-reference={clear-text=redhat1!})

Domain Mode (host-master used in this example)

/profile=default/subsystem=elytron/authentication-configuration=ejbauth:add(authentication-name=server, security-domain=ApplicationDomain, forwarding-mode=authorization, realm=ApplicationRealm, credential-reference={clear-text=redhat1!})
  1. Add an Elytron Authentication Context

Standalone Mode

/subsystem=elytron/authentication-context=ejbauthctx:add(match-rules=[{match-no-user=true,authentication-configuration=ejbauth}])

Domain Mode (host-master used in this example)

/profile=default/subsystem=elytron/authentication-context=ejbauthctx:add(match-rules=[{match-no-user=true,authentication-configuration=ejbauth}])

The resulting xml added looks like:

        <subsystem xmlns="urn:wildfly:elytron:13.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
            <authentication-client>
                <authentication-configuration name="ejbauth" authentication-name="server" realm="ApplicationRealm" security-domain="ApplicationDomain" forwarding-mode="authorization">
                    <credential-reference clear-text="redhat1!"/>
                </authentication-configuration>
                <authentication-context name="ejbauthctx">
                    <match-rule match-no-user="true" authentication-configuration="ejbauth"/>
                </authentication-context>
            </authentication-client>
  1. Create an outbound-socket-binding on the "Client Server"

Standalone Mode

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

Domain Mode

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

The xml added looks like:

            <socket-binding-group name="standard-sockets" 
                                  default-interface="public" 
                                  port-offset="${jboss.socket.binding.port-offset:0}">
                    ...
              <outbound-socket-binding name="remote-socket-ejb">
                <remote-destination host="localhost" port="8080"/>
              </outbound-socket-binding>
            </socket-binding-group>
  1. Create the remoting profile with the connections

Standalone Mode

/subsystem=ejb3/remoting-profile=remote-ejb-profile:add()
/subsystem=ejb3/remoting-profile=remote-ejb-profile/remote-http-connection=server1:add(uri=remote+https://localhost:8080)

Domain Mode (default profile used in this example, see in server-groups the exact profile to modify):

/subsystem=ejb3/remoting-profile=remote-ejb-profile:add()
/profile=default/subsystem=ejb3/remoting-profile=remote-ejb-profile/remote-http-connection=server1:add(uri=remote+https://localhost:8080)

The xml added looks like:

        <subsystem xmlns="urn:jboss:domain:ejb3:9.0">
        ...
            <remote cluster="ejb" connectors="http-remoting-connector" thread-pool-name="default">
                <channel-creation-options>
                    <option name="MAX_OUTBOUND_MESSAGES" value="1234" type="remoting"/>
                </channel-creation-options>
                <profiles>
                    <profile name="remote-ejb-profile">
                        <remote-http-connection name="server1" uri="remote+https://localhost:8080"/>
                    </profile>
                </profiles>
            </remote>
        ...
        </subsystem>
EJB-server configuration

Add an application user:

Standalone Mode

 ./add-user.sh -a -u ejbuser -p redhat1! 

Domain Mode

 ./add-user.sh -a -u ejbuser -p redhat1! 

Picketbox Example Configuration (EAP 7 / EAP 6) (deprecated)

Client application configuration
  • jboss-ejb-client.xml goes in the WEB-INF if the top level deployment is a war or in the top level deployment's META-INF if not a war
            <jboss-ejb-client xmlns="urn:jboss:ejb-client:1.4">
              <client-context>
                <ejb-receivers>
                  <remoting-ejb-receiver outbound-connection-ref="remote-ejb-connection"/>
                </ejb-receivers>
              </client-context>
            </jboss-ejb-client>
  1. Create a security realm on the client server (when adding the EJB user on EJB server side EAP with ./add-user.sh -a -u ejbuser -p redhat1! -ds we can get a base64 encoded password cmVkaGF0MSE= , use this same password value for the secret value inside <server-identities> section)

Standalone Mode

/core-service=management/security-realm=ejb-security-realm:add()
/core-service=management/security-realm=ejb-security-realm/server-identity=secret:add(value="cmVkaGF0MSE=")

Domain Mode (host-master used in this example)

/host=master/core-service=management/security-realm=ejb-security-realm:add()
/host=master/core-service=management/security-realm=ejb-security-realm/server-identity=secret:add(value="cmVkaGF0MSE=")

The xml added looks like:

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

2.Create an outbound-socket-binding on the "Client Server"

Standalone Mode

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

Domain Mode

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

The xml added looks like:

            <socket-binding-group name="standard-sockets" 
                                  default-interface="public" 
                                  port-offset="${jboss.socket.binding.port-offset:0}">
                    ...
              <outbound-socket-binding name="remote-socket-ejb">
                <remote-destination host="localhost" port="8080"/>
              </outbound-socket-binding>
            </socket-binding-group>

3.Create a "remote-outbound-connection" which uses this newly created "outbound-socket-binding"

Standalone Mode

/subsystem=remoting/remote-outbound-connection=remote-ejb-connection:add(outbound-socket-binding-ref=remote-socket-ejb,security-realm=ejb-security-realm,username=ejb)
/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)

Domain Mode (default profile used in this example, see in server-groups the exact profile to modify):

/profile=default/subsystem=remoting/remote-outbound-connection=remote-ejb-connection:add(outbound-socket-binding-ref=remote-socket-ejb,security-realm=ejb-security-realm,username=ejbuser)
/profile=default/subsystem=remoting/remote-outbound-connection=remote-ejb-connection/property=SASL_POLICY_NOANONYMOUS:add(value=false)
/profile=default/subsystem=remoting/remote-outbound-connection=remote-ejb-connection/property=SSL_ENABLED:add(value=false)

/profile=default/subsystem=remoting/remote-outbound-connection=remote-ejb-connection/property=org.jboss.remoting3.RemotingOptions.HEARTBEAT_INTERVAL:add(value=50000)
/profile=default/subsystem=remoting/remote-outbound-connection=remote-ejb-connection/property=KEEP_ALIVE:add(value=true)
/profile=default/subsystem=remoting/remote-outbound-connection=remote-ejb-connection/property=READ_TIMEOUT:add(value=60000)

The xml added looks like:

            <subsystem xmlns="urn:jboss:domain:remoting:1.1">
            ....
              <outbound-connections>
                <remote-outbound-connection name="remote-ejb-connection" 
                                            outbound-socket-binding-ref="remote-socket-ejb" 
                                            security-realm="ejb-security-realm" 
                                            username="ejbuser">
                  <properties>
                    <property name="SASL_POLICY_NOANONYMOUS" value="false"/>
                    <property name="SSL_ENABLED" value="false"/>
                    <property name="org.jboss.remoting3.RemotingOptions.HEARTBEAT_INTERVAL" value="50000"/>
                    <property name="KEEP_ALIVE" value="true"/>
                    <property name="READ_TIMEOUT" value="60000"/>
                  </properties>
                </remote-outbound-connection>
              </outbound-connections>
            </subsystem>

Notes

Clustering

If the remote EAP instances are clustered, once an EJB client connects to one of these clustered instances, the client will receive a cluster view for the cluster and will then connect to those nodes for load balancing and failover. It is recommended to list two nodes in the InitialContext or remote-outbound-connections for HA in the event the first clustered node is down, it will try the second node and then the full cluster view will be received.

If the host specified is in a cluster, it will have affinity to that cluster. If the same EJB is deployed on two different clusters, and the provider url contains a host in cluster1 and host in cluster2, it will connect to host1 / cluster1 and have cluster1 affinity, unless host1 is unavailable and then it will failover to host2 / cluster2 and have affinity to cluster2. To load balance across more than one cluster, see: EJB Load Balancing across multiple clusters in JBoss EAP 7.1

FAQ

Troubleshooting

Common Issues

Diagnostic Steps

Files to collect when an issue occurs:

  • logs from both the client and server side when the issue occurs
  • the client code showing how it sets up the InitialContext , any ejb client related configuration files such as jboss-ejb-client.xml, jboss-ejb-client.properties, wildfly-config.xml, etc.
  • the server side: the EAP configuration, EJB annotations, ejb-jar.xml, jboss-ejb3.xml, etc anything that might be relevant
Components
Category

This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.