WildFly: How to call an EJB from an EJB located in another application

The purpose of this tutorial is to demonstrate how to lookup and invoke an EJB deployed on an WildFly server instance from another EJB located in another application.

Here is our scenario:

wildfly remote ejb client tutorial

If, on the other hand, your scenario requires invoking an EJB from a standalone remote Client, then check this tutorial: WildFly remote EJB client tutorial

In order to demonstrate how to invoke EJBs from a remote EJB Client, we need to complete the following steps:

  • Create the EJB Server Project which contains the interfaces and the implementation for the EJBs
  • Create the EJB Client Project which looks up the remote EJBs
  • Configure Security in the Client Project so that it is authorized to invoke the remote EJBs

Create the EJB Server Project

So assumed you have a simple server project which includes the following Stateful and Stateless EJB:

package com.mastertheboss.ejb;

import javax.ejb.Remote;
import javax.ejb.Stateful;
import com.mastertheboss.exception.InsufficientFundsException;
 
@Stateful
@Remote(Account.class)
public class AccountEJB implements Account {

    long money;
 
    
	@Override
	public long getMoney() {
		return money;

	}

	public void createAccount(long amount)  
	{
		this.money= amount;
		 
	}

	@Override
	public void deposit(long amount)  
	{
		 
			this.money+= amount;
			 
		System.out.println("Money deposit. total is "+money);
	}
 
	 
	@Override
	public void withdraw(long amount) throws InsufficientFundsException {
		
		long newAmount = money - amount;
		if (newAmount < 0) {
			throw new InsufficientFundsException("Unsufficient funds for account! ");
		}
		
		money = newAmount;	 
		System.out.println("Money withdrawal. total is "+money);

	}
}

package com.mastertheboss.ejb;

import javax.ejb.Remote;
import javax.ejb.Stateless;

@Stateless
@Remote(Calculator.class)
public class CalculatorEJB implements Calculator {
   
	float interest=5;
 
	@Override
	public float calculateInterest(long money) {
	 
	    return money * (1+ (interest/100));
	       
   }
	
}

The interfaces for both classes are:

package com.mastertheboss.ejb;

import com.mastertheboss.exception.InsufficientFundsException;

public interface Account {

	public void deposit(long amount);
	public void withdraw(long amount) throws InsufficientFundsException;
	
	public long getMoney();
	public void createAccount(long amount);
}

package com.mastertheboss.ejb;

public interface Calculator {

	public float calculateInterest(long money);
	 
}

Configuring Server dependencies (WildFly 18 and newer)

The server project requires the following set of dependencies:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.wildfly.bom</groupId>
            <artifactId>wildfly-jakartaee8-with-tools</artifactId>
            <version>${version.server.bom}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
      <dependency>
         <groupId>org.jboss.spec.javax.annotation</groupId>
         <artifactId>jboss-annotations-api_1.3_spec</artifactId>
         <scope>provided</scope>
      </dependency>
      <dependency>
         <groupId>org.jboss.spec.javax.ejb</groupId>
         <artifactId>jboss-ejb-api_3.2_spec</artifactId>
         <scope>provided</scope>
      </dependency>
</dependencies>

The Property version.server.bom contains the Application Server version, for example:

<properties>
   <version.server.bom>18.0.0.Final</version.server.bom>
</properties>

Configuring Server dependencies (WildFly 11,12,13,14,15,16,17)

If your WildFly version is still not certified for Jakarta EE, then the only change you need to apply is to your BOM, which will be wildfly-javaee8-with-tools both for the Server and for the Client application. No other changes are required:

<dependencyManagement>
   <dependencies>
      <dependency>
         <groupId>org.wildfly.bom</groupId>
         <artifactId>wildfly-javaee8-with-tools</artifactId>
         <version>${version.server.bom}</version>
         <type>pom</type>
         <scope>import</scope>
      </dependency>
   </dependencies>
</dependencyManagement>

Deploy the EJB Server Project

As our pom.xml includes WildFly’s maven plugin, you can simply build and deploy the EJB Server project as follows:

mvn install wildfly:deploy

 Check from the server logs that the EJB project has been deployed and that JNDI bindings are available:

08:24:43,534 INFO  [org.jboss.as.ejb3.deployment] (MSC service thread 1-3) WFLYEJB0473: JNDI bindings for session bean named 'AccountEJB' in deployment unit 'deployment "ejb-client-basic.war"' are as follows:

	java:global/ejb-client-basic/AccountEJB!com.mastertheboss.ejb.Account
	java:app/ejb-client-basic/AccountEJB!com.mastertheboss.ejb.Account
	java:module/AccountEJB!com.mastertheboss.ejb.Account
	java:jboss/exported/ejb-client-basic/AccountEJB!com.mastertheboss.ejb.Account
	ejb:/ejb-client-basic/AccountEJB!com.mastertheboss.ejb.Account?stateful
	java:global/ejb-client-basic/AccountEJB
	java:app/ejb-client-basic/AccountEJB
	java:module/AccountEJB

Create the EJB Client Project

Now let’s write an EJB Client application. For the sake of simplicity, our application will be made up of just a Servlet FE and an EJB which invokes out Account and Calculator EJB. Here is the Servlet:

package com.mastertheboss.ejbclient;

import java.io.IOException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ejb.*;


@WebServlet(urlPatterns = "/ejbclient")
public class ServletFE extends HttpServlet {
    @EJB EJBClient ejb;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) {

        response.setContentType("html");
        write(response, "Example Servlet to show how EJB can invoke an EJB in another application");

        try {
            long money= request.getParameter("money") != null ? Long.parseLong(request.getParameter("money")) : 100l;
            float moneyWithInterest = ejb.callRemoteEJBs(money);
            write(response, "Amount: " + moneyWithInterest);
            } catch (Exception n) {
                  write(response, "Failed to invoke Remote EJB");
                write(response, n.getMessage());

            }

    }

    private static void write(HttpServletResponse writer, String message) {

        try {
            writer.getWriter().write(message + "\n");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

As you can see, it’s just a tiny wrapper to our EJB, which contains the actual EJB Client interaction:

 
package com.mastertheboss.ejbclient;

import java.util.Hashtable;

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

import com.mastertheboss.ejb.*;
import com.mastertheboss.exception.*;


import javax.naming.spi.NamingManager;
import javax.ejb.*;
@Stateless
public class EJBClient {

	@EJB(name="ejb:/ejb-server-basic/CalculatorEJB!com.mastertheboss.ejb.Calculator")
	Calculator calculator;

	@EJB(name="ejb:/ejb-server-basic/AccountEJB!com.mastertheboss.ejb.Account?stateful")
	Account account;

	public float callRemoteEJBs(long money) throws Exception {
		account.createAccount(money);
		System.out.println("Create Account with "+money);

		account.deposit(money/2);
		System.out.println("Deposit " +(money/2));

		try {
			account.withdraw(money/3);
			System.out.println("Withdraw "+(money/3));

		} catch (InsufficientFundsException e) {

			e.printStackTrace();
		}
		money = account.getMoney();
		System.out.println("Money left " + money);
		float totalMoney = calculator.calculateInterest(money);
		System.out.println("Money plus interests " + totalMoney);
		return totalMoney;
	}
}

Please notice that, as we are going to invoke an EJB which is resident in the same Application Server, all you need in order to lookup the remote EJB is just this:

@EJB(name="ejb:/ejb-server-basic/CalculatorEJB!com.mastertheboss.ejb.Calculator")
Calculator calculator;

@EJB(name="ejb:/ejb-server-basic/AccountEJB!com.mastertheboss.ejb.Account?stateful")
Account account;

Configuring Client dependencies (WildFly 18 and newer)

The server project requires the following set of dependencies:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.wildfly.bom</groupId>
            <artifactId>wildfly-jakartaee8-with-tools</artifactId>
            <version>${version.server.bom}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
<!-- We depend on the EJB remote business interfaces of this application -->
	<dependency>
	 <groupId>com.itbuzzpress.chapter4</groupId>
	 <artifactId>ejb-server-basic</artifactId>
	 <type>ejb-client</type>
	 <version>${project.version}</version>
	</dependency>
<!-- Include the ejb client jars -->
	<dependency>
	 <groupId>org.wildfly</groupId>
	 <artifactId>wildfly-ejb-client-bom</artifactId>
	 <type>pom</type>
	 <scope>compile</scope>
	</dependency>
</dependencies>

Much the same way, the Property version.server.bom contains the Application Server version, for example:

<properties>
   <version.server.bom>18.0.0.Final</version.server.bom>
</properties>

As for the EJB Server project, if you are running a WildFly version between WildFly 11 and WildFly 17 use the artifactId wildfly-javaee8-with-tools in your dependencyManagement section.

Configuring Security in the EJB Client Project

Another change impacting your clients is that the former jboss-ejb-client.properties file is depecated so you are encouraged to migrate to the Elytron wildfly-config.xml file  which unifies all client configuration in a single place. For the purpose of our example, we will just add a default wildfly-confug.xml file which uses any available SASL mechanism selector for authentication.

<configuration>
    <authentication-client xmlns="urn:elytron:1.0">
        <authentication-rules>
                    <rule use-configuration="default" />
        </authentication-rules>
        <authentication-configurations>
            <configuration name="default">
                <sasl-mechanism-selector selector="#ALL" />
                <set-mechanism-properties>
                    <property key="wildfly.sasl.local-user.quiet-auth" value="true" />
                 </set-mechanism-properties>
                <providers>
                    <use-service-loader/>
                </providers>
             </configuration>
        </authentication-configurations>
    </authentication-client>
</configuration>

On the other hand, if you need to provide credentials, then you can add them in the authentication-configurations block as in the following example:

<configuration>
    <authentication-client xmlns="urn:elytron:1.0">
        <authentication-rules>
            <rule use-configuration="default"/>
        </authentication-rules>
        <authentication-configurations>
            <configuration name="default">
                <sasl-mechanism-selector selector="DIGEST-MD5"/>
                <set-user-name name="ejbuser"/>
                <credentials>
                    <clear-password password="password1!"/>
                </credentials>
            </configuration>
        </authentication-configurations>
    </authentication-client>	
</configuration>

 To create the user on the server, just execute the add-user.sh script as follows:

$ ./add-user.sh -a -u ejbuser -p password1! 

Testing the EJB Client Project

In order to test our application, deploy the Client project as well:

mvn clean install wildfly:deploy

You can Test the application throigh the Servlet URI, which is “/ejbclient”:

curl http://localhost:8080/ejb-client-basic/ejbclient?money=200
Example Servlet to show how EJB can invoke an EJB in another application
Amount: 245.69998

You can check the source code for this tutorial at: https://github.com/fmarchioni/mastertheboss/tree/master/ejb/intra-server-remote-ejb

 

Found the article helpful? if so please follow us on Socials