jakub holý

building the right thing, building it right, fast

Access EJB on JBoss from outside

2006-04-10 09:59:09j2ee

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">
                <login-module code = "org.jboss.security.ClientLoginModule"  flag = "required"></login-module>

    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 );