Access EJB on JBoss from outside

How to acces an enterprise java bean (EJB) running on JBoss from a standalone application running outside JBoss?

  1. In the code you must, among others:
    1. Set properties for a naming context and create one to be able to look the EJB up
    2. Authenticate by JBoss by means of JAAS
  2. To run the application:
    1. Set the classpath (-cp …): it must contain jbosssx.jar (ClientLoginModule), jboss-common.jar and jnpserver.jar (naming stuff) from <jboss home>/server/default/lib
    2. Create the JAAS configuration file sample_jaas.config containing:
      jboss_jaas { org.jboss.security.ClientLoginModule required; };
      

      If you ever wanted to run the application from JBoss, replace the JAAS config file by the following entry in <JBoss home>/server/default/conf/login-config.ml (application-policy name must be the same name as the one passed to the constructor of a LoginContext):

      <application-policy name = "tap_experiments">
             <authentication>
                <login-module code = "org.jboss.security.ClientLoginModule"  flag = "required"></login-module>
             </authentication>
          </application-policy>
      
    3. Pass the file to the JVM: -Djava.security.auth.login.config=sample_jaas.config
    4. Set a security manager by passing the following options to the JVM:
      -Djava.security.manager -Djava.security.policy="<jboss home>\server\default\conf\server.policy"
import java.rmi.RemoteException;
import java.util.Properties;

import javax.ejb.CreateException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import com.teradata.tap.system.query.ejb.QueryEngineRemote;
import com.teradata.tap.system.query.ejb.QueryEngineRemoteHome;

/**
 * Call a business method of an EJB running on JBoss
 */
public class ExternalCallEjbSample {

        public static void main(String[] args) {
                // 1: Get the naming Context
                // Required JARs (in jboss/server/default/lib): jboss-common.jar, jnpserver.jar 
                Properties props = new Properties();
                //      Matches the java.naming.factory.initial property in jndi.properties
                props.put(Context.INITIAL_CONTEXT_FACTORY,
                                "org.jnp.interfaces.NamingContextFactory");
                //      Matches the java.naming.provider.url property in jndi.properties
                props.put(Context.PROVIDER_URL, "jnp://localhost:1099");
                props.put(Context.URL_PKG_PREFIXES,
                                "org.jboss.naming:org.jnp.interfaces");
                QueryEngineRemoteHome queryEngineHome = null;

                try {

                        Context ctx = new InitialContext(props);

                        /*/ Print all entries in the JNDI
                         NamingEnumeration ne = ctx.list("java:");
                         while (ne.hasMore()) {
                         System.out.println("A: " + ne.next().toString());
                         } //*/

                        // Look up and instantiate the home interface of the EJB
                        // IMPORTANT: It fails if no SecurityManager specified for RMI class loader will be disabled
                        // -> add these options to the JVM:
                        // -Djava.security.manager -Djava.security.policy=" home>\server\default\conf\server.policy"
                        String beanName = QueryEngineRemoteHome.JNDI_NAME;
                        System.out.println("Looking up '" + beanName + "'");
                        Object lookup = ctx.lookup(beanName);

                        queryEngineHome = (QueryEngineRemoteHome) PortableRemoteObject
                                        .narrow(lookup, QueryEngineRemoteHome.class);

                } catch (NamingException e) {
                        System.out.println("new InitialContext failed:" + e);
                }

                // 2. Instantiate the (remote) EJB and call its business method(s)
                // 2.1 I have to authenticate unless security allows anybody to call create on the EJB
                //      Otherwise an EJBException: checkSecurityAssociation will be thrown.
                // TODO: JVM option -Djava.security.auth.login.config==sample_jaas.config - use org.jboss.security.ClientLoginModule
                // and have  home>\server\default\lib\jbosssx.jar on the path (class ClientLoginModule)
                // Listing of sample_jaas.config:
                //      jboss_jaas { org.jboss.security.ClientLoginModule required debug=true; };
                LoginContext loginContext = null;
                boolean loggedIn = false;
                try {
                        CallbackHandler handler = new MyPresetCallbackHandler("tapdev","tapdev");
                        // jboss_jaas - name of a configuration in the jaas config file 
                        loginContext = new LoginContext("jboss_jaas", handler);
                        System.out.println("Created LoginContext");
                        loginContext.login(); // throws LoginException
                        System.out.println("Logged in.");
                        loggedIn = true;
                } catch (LoginException le) {
                        System.out.println("Login failed");
                        le.printStackTrace();
                }

                // Create & use the EJB:
                if (loggedIn && queryEngineHome != null) {
                        try {
                                QueryEngineRemote queryEngine = queryEngineHome.create();
                                System.out.println("queryEngine remote created.");
                                // TODO: call business method(s)
                        } catch (RemoteException e1) {
                                e1.printStackTrace();
                        } catch (CreateException e1) {
                                e1.printStackTrace();
                        }
                }

                // Log out
                if (loggedIn && loginContext != null) {
                        try {
                                loginContext.logout();
                        } catch (LoginException e) {
                                System.out.println("Logout failed:" + e);
                        }
                }

                System.out.println("## DONE! ##");
        } // main

        /** Authentication CallbackHandler with preset username/password. */
        static class MyPresetCallbackHandler implements CallbackHandler {
                String username;

                char[] password;

                public MyPresetCallbackHandler(String username, String password) {
                        this.username = username;
                        this.password = password.toCharArray();
                }

                public void handle(Callback[] callbacks) throws java.io.IOException,
                                UnsupportedCallbackException {
                        for (int i = 0; i < callbacks.length; i++) {
                                Callback callback = callbacks[i];
                                if (callback instanceof NameCallback) {
                                        ((NameCallback) callback).setName(username);
                                } else if (callback instanceof PasswordCallback) {
                                        ((PasswordCallback) callback).setPassword(password);
                                } else {
                                        throw new UnsupportedCallbackException(callback,
                                                        "Unrecognized Callback");
                                }
                        }
                }// handle 
        }// MyPresetCallbackHandler
}

See EjbLocator.java
and EjbLocatorException.java.
With them, you can replace all above with QueryEngineRemote queryEngine = (QueryEngineRemote) EjbLocator.getInstance().locate( QueryEngineRemoteHome.JNDI_NAME );

Published by Jakub Holý

I’m a JVM-based developer since 2005, consultant, and occasionally a project manager, working currently with Iterate AS in Norway.