I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

1. Overview

In this article, we’re going to discuss how to get started with Enterprise JavaBean (EJB) development.

Enterprise JavaBeans are used for developing scalable, distributed, server-side components and typically encapsulate the business logic of the application.

We’ll use WildFly 10.1.0 as our preferred server solution, however, you are free to use any Java Enterprise application server of your choice.

2. Setup

Let’s start by discussing the Maven dependencies required for EJB 3.2 development and how to configure the WildFly application server using either the Maven Cargo plugin or manually.

2.1. Maven Dependency

In order to use EJB 3.2, make sure you add the latest version to the dependencies section of your pom.xml file:

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>7.0</version>
    <scope>provided</scope>
</dependency>
You will find the latest dependency in the Maven Repository. This dependency ensures that all Java EE 7 APIs are available during compile time. The provided scope ensures that once deployed, the dependency will be provided by the container where it has been deployed.

2.2. WildFly Setup With Maven Cargo

Let’s talk about how to use the Maven Cargo plugin to setup the server.

Here is the code for the Maven profile that provisions the WildFly server:

<profile>
    <id>wildfly-standalone</id>
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.cargo</groupId>
                <artifactId>cargo-maven2-plugin</artifactId>
                <version>${cargo-maven2-plugin.version</version>
                <configuration>
                    <container>
                        <containerId>wildfly10x</containerId>
                        <zipUrlInstaller>
                            <url>
                                http://download.jboss.org/
                                  wildfly/10.1.0.Final/
                                    wildfly-10.1.0.Final.zip
                            </url>
                        </zipUrlInstaller>
                    </container>
                    <configuration>
                        <properties>
                            <cargo.hostname>127.0.0.0</cargo.hostname>
                            <cargo.jboss.management-http.port>
                                9990
                            </cargo.jboss.management-http.port>
                            <cargo.servlet.users>
                                testUser:admin1234!
                            </cargo.servlet.users>
                        </properties>
                    </configuration>
                </configuration>
            </plugin>
        </plugins>
    </build>
</profile>

We use the plugin to download the WildFly 10.1 zip directly from the WildFly’s website. Which is then configured, by making sure that the hostname is 127.0.0.1 and setting the port to 9990.

Then we create a test user, by using the cargo.servlet.users property, with the user id testUser and the password admin1234!.

Now that configuration of the plugin is completed we should be able to call a Maven target and have the server download, installed, launched and the application deployed.

To do this, navigate to the ejb-remote directory and run the following command:

mvn clean package cargo:run

When you run this command for the first time, it will download the WildFly 10.1 zip file, extract it and execute the installation and then launch it. It will also add the test user discussed above. Any further executions will not download the zip file again.

2.3. Manual Setup of WildFly

In order to setup WildFly manually, you must download the installation zip file yourself from the wildfly.org website. The following steps are a high-level view of the WildFly server setup process:

After downloading and unzipping the file’s contents to the location where you want to install the server, configure the following environment variables:

JBOSS_HOME=/Users/$USER/../wildfly.x.x.Final
JAVA_HOME=`/usr/libexec/java_home -v 1.8`

Then in the bin directory, run the ./standalone.sh for Linux based operating systems or ./standalone.bat for Windows.

After this, you will have to add a user. This user will be used to connect to the remote EJB bean. To find out how to add a user you should take a look at the ‘add a user’ documentation.

For detailed setup instructions please visit WildFly’s Getting Started documentation.

The project POM has been configured to work with the Cargo plugin and manual server configuration by setting two profiles. By default, the Cargo plugin is selected. However to deploy the application to an already installed, configured and running Wildfly server execute the following command in the ejb-remote directory:

mvn clean install wildfly:deploy -Pwildfly-runtime

3. Remote vs Local

A business interface for a bean can be either local or remote.

A @Local annotated bean can only be accessed if it is in the same application as the bean that makes the invocation, i.e. if they reside in the same .ear or .war.

A @Remote annotated bean can be accessed from a different application, i.e. an application residing in a different JVM or application server.

There are some important points to keep in mind when designing a solution that includes EJBs:

  • The java.io.Serializable, java.io.Externalizable and interfaces defined by the javax.ejb package are always excluded when a bean is declared with @Local or @Remote
  • If a bean class is remote, then all implemented interfaces are to be remote
  • If a bean class contains no annotation or if the @Local annotation is specified, then all implemented interfaces are assumed to be local
  • Any interface that is explicitly defined for a bean which contains no interface must be declared as @Local
  • The EJB 3.2 release tends to provide more granularity for situations where local and remote interfaces need to explicitly defined

4. Creating the Remote EJB

Let’s first create the bean’s interface and call it HelloWorld:

@Remote
public interface HelloWorld {
    String getHelloWorld();
}

Now we will implement the above interface and name the concrete implementation HelloWorldBean:

@Stateless(name = "HelloWorld")
public class HelloWorldBean implements HelloWorld {

    @Resource
    private SessionContext context;

    @Override
    public String getHelloWorld() {
        return "Welcome to EJB Tutorial!";
    }
}

Note the @Stateless annotation on the class declaration. It denotes that this bean is a stateless session bean. This kind of bean does not have any associated client state, but it may preserve its instance state and is normally used to do independent operations.

The @Resource annotation injects the session context into the remote bean.

The SessionContext interface provides access to the runtime session context that the container provides for a session bean instance. The container then passes the SessionContext interface to an instance after the instance has been created. The session context remains associated with that instance for its lifetime.

The EJB container normally creates a pool of stateless bean’s objects and uses these objects to process client requests. As a result of this pooling mechanism, instance variable values are not guaranteed to be maintained across lookup method calls.

5. Remote Setup

In this section, we will discuss how to setup Maven to build and run the application on the server.

Let’s look at the plugins one by one.

5.1. The EJB Plugin

The EJB plugin which is given below is used to package an EJB module. We have specified the EJB version as 3.2.

The following plugin configuration is used to setup the target JAR for the bean:

<plugin>
    <artifactId>maven-ejb-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <ejbVersion>3.2</ejbVersion>
    </configuration>
</plugin>

5.2. Deploy the Remote EJB

To deploy the bean in a WildFly server ensure that the server is up and running.

Then to execute the remote setup we will need to run the following Maven commands against the pom file in the ejb-remote project:

mvn clean install

Then we should run:

mvn wildfly:deploy

Alternatively, we can deploy it manually as an admin user from the admin console of the application server.

6. Client Setup

After creating the remote bean we should test the deployed bean by creating a client.

First, let’s discuss the Maven setup for the client project.

6.1. Client-Side Maven Setup

In order to launch the EJB3 client we need to add the following dependencies:

<dependency>
    <groupId>org.wildfly</groupId>
    <artifactId>wildfly-ejb-client-bom</artifactId>
    <type>pom</type>
    <scope>import</scope>
</dependency>

We depend on the EJB remote business interfaces of this application to run the client. So we need to specify the EJB client JAR dependency. We add the following in the parent pom:

<dependency>
    <groupId>com.baeldung.ejb</groupId>
    <artifactId>ejb-remote</artifactId>
    <type>ejb</type>
</dependency>

The <type> is specified as ejb.

6.2. Accessing The Remote Bean

We need to create a file under src/main/resources and name it jboss-ejb-client.properties that will contain all the properties that are required to access the deployed bean:

remote.connections=default
remote.connection.default.host=127.0.0.1
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
  .SASL_POLICY_NOPLAINTEXT = false
remote.connection.default.connect.options.org.xnio.Options
  .SASL_DISALLOWED_MECHANISMS = ${host.auth:JBOSS-LOCAL-USER}
remote.connection.default.username=testUser
remote.connection.default.password=admin1234!

7. Creating the Client

The class that will access and use the remote HelloWorld bean has been created in EJBClient.java which is in the com.baeldung.ejb.client package.

7.1 Remote Bean URL

The remote bean is located via a URL that conforms to the following format:

ejb:${appName}/${moduleName}/${distinctName}/${beanName}!${viewClassName}
  • The ${appName} is the application name of the deployment. Here we have not used any EAR file but a simple JAR or WAR deployment, so the application name will be empty
  • The ${moduleName} is the name we set for our deployment earlier, so it is ejb-remote
  • The ${distinctName} is a specific name which can be optionally assigned to the deployments that are deployed on the server. If a deployment doesn’t use distinct-name then we can use an empty String in the JNDI name, for the distinct-name, as we did in our example
  • The ${beanName} variable is the simple name of the implementation class of the EJB, so in our example it is HelloWorld
  • ${viewClassName} denotes the fully-qualified interface name of the remote interface

7.2 Look-up Logic

Next, let’s have a look at our simple look-up logic:

public HelloWorld lookup() throws NamingException { 
    String appName = ""; 
    String moduleName = "remote"; 
    String distinctName = ""; 
    String beanName = "HelloWorld"; 
    String viewClassName = HelloWorld.class.getName();
    String toLookup = String.format("ejb:%s/%s/%s/%s!%s",
      appName, moduleName, distinctName, beanName, viewClassName);
    return (HelloWorld) context.lookup(toLookup);
}

In order to connect to the bean we just created, we will need a URL which we can feed into the context.

7.3 The Initial Context

We’ll now create/initialize the session context:

public void createInitialContext() throws NamingException {
    Properties prop = new Properties();
    prop.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
    prop.put(Context.INITIAL_CONTEXT_FACTORY, 
      "org.jboss.naming.remote.client.InitialContextFacto[ERROR]
    prop.put(Context.PROVIDER_URL, "http-remoting://127.0.0.1:8080");
    prop.put(Context.SECURITY_PRINCIPAL, "testUser");
    prop.put(Context.SECURITY_CREDENTIALS, "admin1234!");
    prop.put("jboss.naming.client.ejb.context", false);
    context = new InitialContext(prop);
}

To connect to the remote bean we need a JNDI context. The context factory is provided by the Maven artifact org.jboss:jboss-remote-naming and this creates a JNDI context, which will resolve the URL constructed in the lookup method, into proxies to the remote application server process.

7.4 Define Lookup Parameters

We define the factory class with the parameter Context.INITIAL_CONTEXT_FACTORY.

The Context.URL_PKG_PREFIXES is used to define a package to scan for additional naming context.

The parameter org.jboss.ejb.client.scoped.context = false tells the context to read the connection parameters (such as the connection host and port) from the provided map instead of from a classpath configuration file. This is especially helpful if we want to create a JAR bundle that should be able to connect to different hosts.

The parameter Context.PROVIDER_URL defines the connection schema and should start with http-remoting://.

8. Testing

To test the deployment and check the setup, we can run the following test to make sure everything works properly:

@Test
public void testEJBClient() {
    EJBClient ejbClient = new EJBClient();
    HelloWorldBean bean = new HelloWorldBean();
    
    assertEquals(bean.getHelloWorld(), ejbClient.getEJBRemoteMessage());
}

With the test passing, we can now be sure everything is working as expected.

9. Conclusion

So we have created an EJB server and a client which invokes a method on a remote EJB. The project can be run on any application server by properly adding the dependencies for that server.

The entire project can be found over on GitHub.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS