WildFly remote EJB client tutorial

This tutorial will help you to troubleshooting common issues when deploying and running an EJB remote client with WildFly application server and earlier server releases (JBoss AS 7.1 and EAP 6)

Previous versions of JBoss AS (versions < 7.x) used JNP project as the JNDI naming implementation so developers are familiar with the jnp:// PROVIDER_URL URL in order to communicate with the Application Server. Starting with AS7, the JNP project is not used. The client side of the JNP project has now been replaced by jboss-remote-naming project (https://github.com/jbossas/jboss-remote-naming). There were various reasons why the JNP client was replaced by jboss-remote-naming project. One of them was the JNP project did not allow fine grained security configurations while communicating with the JNDI server. The jboss-remote-naming project is backed by the jboss-remoting project (https://github.com/jboss-remoting/jboss-remoting) which allows much more and better control over security.

Coding the Server classes

We will create a Maven project named ejb-remote-server with the following archetype:

$ mvn -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=ejb-javaee7 -DarchetypeRepository=https://nexus.codehaus.org/content/repositories/snapshots -DgroupId=com.sample.ejb -DartifactId=ejb-remote-server -Dversion=1.0 -Dpackage=com.sample.ejb -Darchetype.interactive=false --batch-mode --update-snapshots archetype:generate

Let's add some classes to our Project:

package com.sample.ejb;

public interface SampleBeanRemote {
     public String echo(String s);

}

And here's the implementation:

package com.sample.ejb;

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

@Stateless
@Remote(SampleBeanRemote.class) 
public class  SampleBeanRemoteImpl implements SampleBeanRemote  {

    @Override
    public String echo(String s) {

        return "Hello "+s;
    }


}

The following dependencies will be needed in the Server project:

    <properties>

        <version.wildfly.maven.plugin>1.0.2.Final</version.wildfly.maven.plugin>
        <version.jboss.spec.javaee.7.0>1.0.3.Final</version.jboss.spec.javaee.7.0>
        <version.ejb.plugin>2.3</version.ejb.plugin>

        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.jboss.spec</groupId>
                <artifactId>jboss-javaee-7.0</artifactId>
                <version>${version.jboss.spec.javaee.7.0}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

        <!-- Import the Common Annotations API (JSR-250), we use provided scope
            as the API is included in WildFly -->
        <dependency>
            <groupId>org.jboss.spec.javax.annotation</groupId>
            <artifactId>jboss-annotations-api_1.2_spec</artifactId>
            <scope>provided</scope>
        </dependency>

        <!-- Import the EJB API, we use provided scope as the API is included in WildFly -->
        <dependency>
            <groupId>org.jboss.spec.javax.ejb</groupId>
            <artifactId>jboss-ejb-api_3.2_spec</artifactId>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
 
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- WildFly plug-in to deploy the application -->
            <plugin>
                <groupId>org.wildfly.plugins</groupId>
                <artifactId>wildfly-maven-plugin</artifactId>
                <version>${version.wildfly.maven.plugin}</version>
                <configuration>
                    <filename>${project.build.finalName}.jar</filename>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-ejb-plugin</artifactId>
                <version>${version.ejb.plugin}</version>
                <configuration>
                    <ejbVersion>3.1</ejbVersion>
                    <generateClient>true</generateClient>
                </configuration>
            </plugin>

        </plugins>
    </build>

Coding the Client

Our remote EJB client will be a plain Java class. We will create for this purpose a new project, which contains into its pom.xml a dependency to our EJB interfaces. So, start by creating a new Maven project that barely contains a src/java folder. Execute the following command:

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false -DgroupId=com.sample.client -DartifactId=javaee7-ejb-
client-basic

We will add only one class to the project which will invoke remotely our EJBs:

package com.sample.client;

import javax.naming.*;

import com.sample.ejb.SampleBeanRemote;
import com.sample.ejb.SampleBeanRemoteImpl;

import java.util.*;


public class RemoteEJBClient {

    public static void main(String[] args) throws Exception {
        testRemoteEJB();

    }

    private static void testRemoteEJB() throws NamingException {

        final SampleBeanRemote ejb = lookupRemoteEJB();
        String s = ejb.echo("Frank");
        System.out.println(s);
    }

    private static SampleBeanRemote lookupRemoteEJB() throws NamingException {
        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");

        final Context context = new InitialContext(jndiProperties);


        final String appName = "";
        final String moduleName = "ejb-remote-server";
        final String distinctName = "";
        final String beanName = SampleBeanRemoteImpl.class.getSimpleName();

        final String viewClassName = SampleBeanRemote.class.getName();
        System.out.println("Looking EJB via JNDI ");
        System.out.println("ejb:" + appName + "/" + moduleName + "/" + distinctName + "/" + beanName + "!" + viewClassName);

        return (SampleBeanRemote) context.lookup("ejb:" + appName + "/" + moduleName + "/" + distinctName + "/" + beanName + "!" + viewClassName);


    }

}

As you can see, the trickiest part of it, is the new JNDI binding where the EJB is looked up. The actual JNDI name depends on the package name where the EJB is shipped. In our case, the server application is named ejb-remote-server.jar, therefore the module name is "ejb-remote-server". If we packed the web module in an ear file we should define the variable appName with the Ear name. 

In order to run this application you need to place a file named jboss-ejb-client.properties in the client classpath:

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

Basically this file contains the host and port where the EJB will be looked up (in our case localhost and the Undertow port which tunnels the request to the EJB Container, in this example, 8080). In terms of configuration.

In order to compile and run the Client project, you will need the following Maven dependencies:

  <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <version.jboss.spec.javaee.7.0>1.0.3.Final</version.jboss.spec.javaee.7.0>
        <version.wildfly.maven.plugin>1.0.2.Final</version.wildfly.maven.plugin>

        <version.exec.plugin>1.2.1</version.exec.plugin>
        <version.war.plugin>3.0.0</version.war.plugin>

        <!-- maven-compiler-plugin -->
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>

    <dependencyManagement>
      <dependencies>

         <dependency>
            <groupId>org.jboss.spec</groupId>
            <artifactId>jboss-javaee-7.0</artifactId>
            <version>${version.jboss.spec.javaee.7.0}</version>
            <type>pom</type>
            <scope>import</scope>
         </dependency>

         <dependency>
             <groupId>org.wildfly</groupId>
             <artifactId>wildfly-ejb-client-bom</artifactId>
             <version>${version.wildfly}</version>
             <type>pom</type>
             <scope>import</scope>
         </dependency>
      </dependencies>

    </dependencyManagement>

    <dependencies>

        <dependency>
            <groupId>org.jboss.spec.javax.transaction</groupId>
            <artifactId>jboss-transaction-api_1.2_spec</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.jboss.spec.javax.ejb</groupId>
            <artifactId>jboss-ejb-api_3.2_spec</artifactId>
            <scope>runtime</scope>
        </dependency>

       <dependency>
          <groupId>org.wildfly.quickstarts</groupId>
          <artifactId>wildfly-ejb-remote-server-side</artifactId>
          <type>ejb-client</type>
         <version>${project.version}</version>
       </dependency>

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

    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>

            <!-- Add the maven exec plug-in to allow us to run a java program
                via maven -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>${version.exec.plugin}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <executable>java</executable>
                    <workingDirectory>${project.build.directory}/exec-working-directory</workingDirectory>
                    <arguments>
                        <!-- automatically creates the classpath using all
                            project dependencies, also adding the project build directory -->
                        <argument>-classpath</argument>
                        <classpath>
                        </classpath>
                        <argument>org.jboss.as.quickstarts.ejb.remote.client.RemoteEJBClient</argument>
                    </arguments>
                </configuration>
            </plugin>

            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <mainClass>org.jboss.as.quickstarts.ejb.remote.client.RemoteEJBClient</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.wildfly.plugins</groupId>
                <artifactId>wildfly-maven-plugin</artifactId>
                <version>${version.wildfly.maven.plugin}</version>
                <inherited>true</inherited>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>

</build>

As you can see, besides the EJB and Transaction API, we had to reference the Server project (ejb.remote.server) in order to use the Interfaces of our EJB.

Source code for this project is available at: https://github.com/fmarchioni/mastertheboss/tree/master/ejb/remote-ejb

EJB 3 clients from remote hosts

if your EJB 3 clients are arriving from a remote host, you will need to authenticate with an application user in order to be allowed by the remoting protocol.

At first add an user using the add-user.sh script:

[francesco@localhost bin]$ ./add-user.sh 

What type of user do you wish to add? 
 a) Management User (mgmt-users.properties) 
 b) Application User (application-users.properties)
(a): b

Enter the details of the new user to add.
Using realm 'ApplicationRealm' as discovered from the existing property files.
Username : ejbuser
Password : 
Re-enter Password : 
What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[  ]: 
About to add user 'ejbuser' for realm 'ApplicationRealm'
Is this correct yes/no? yes
Added user 'ejbuser' to file '/home/francesco/jboss/wildfly-10.0.0.Final/standalone/configuration/application-users.properties'
Added user 'ejbuser' to file '/home/francesco/jboss/wildfly-10.0.0.Final/domain/configuration/application-users.properties'
Added user 'ejbuser' with groups  to file '/home/francesco/jboss/wildfly-10.0.0.Final/standalone/configuration/application-roles.properties'
Added user 'ejbuser' with groups  to file '/home/francesco/jboss/wildfly-10.0.0.Final/domain/configuration/application-roles.properties' 

Next, add the user information into your jboss-ejb-client.properties

remote.connection.default.username=ejbuser
remote.connection.default.password=ejbuser123

JBoss AS 7 and EAP 6 Projects

If you are running an older version of the application server, the most relevant change (besides the dependencies) is the jboss-ejb-client.properties which uses the older Remoting Port to allow EJB request, enter the Container:

endpoint.name=client-endpoint
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
 
remote.connection.default.host=localhost
remote.connection.default.port = 4447
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false

 

Related articles available on mastertheboss.com

JBoss AS 7 introduction

This is an introduction tutorial to the newest JBoss AS 7 which a

Develop Java EE applications with JBoss AS 7

In this tutorial we will learn how to create and deploy a Java EE

Adding users with JBoss AS 7

If you are planning to add new users to your management interface

Using JBoss AS 7 management API programmatically

In this tutorial we will show how to use detyped management API t

JNDI view in JBoss AS 7

JNDI can be checked on the naming subsystem of the application se

How to install a module on WildFly / JBoss AS 7

Installing a module on WildFly / JBoss AS 7 requires creating a p

Follow us on Twitter