Samstag, 30. November 2013

JBoss AS 7 programmatic EJB Remote Client

Recently I have worked on a project which I had to migrate from JBoss 6 to JBoss 7 and the main problem for me was the EJB remote client connection to the JBoss 7.

An EJB remote client with JBoss 7 can be connected with the following three different approaches.
  1. jboss-ejb-client.properties.
  2. Setting property file through system property jboss.ejb.client.properties.file.path
  3. programmatically
The first two approaches can be found on the jboss documentation (https://docs.jboss.org/author/display/AS72/EJB+invocations+from+a+remote+client+using+JNDI) and in the jboss quickstarts. The problems with those are:
  • static nature
  • useless in case of client specific security (username, password)
The solution for the last approach was very hard to find and time consuming. So here is my solution to make it work with a JBoss specific JBossClientContextBuilder. (Note: Do not forget the jboss-client.jar in your classpath).

IHelloBean.java
public interface IHelloBean {
 String getHello();
}
HelloBean.java
@Stateless
@Remote(IHelloBean.class)
public class HelloBean implements IHelloBean
{
 @Override
 public String getHello()
 {
  return "Hello";
 }
}
HelloClient.java
public class HelloClient
{
 public static void main(String[] args) throws NamingException
 {
  final Context context = new JBossClientContextBuilder().build();

  String lookupString = "ejb:/sectest/HelloBean!test.IHelloBean";
  final IHelloBean helloBean = (IHelloBean) context.lookup(lookupString);
  System.out.println("Hello: " + helloBean.getHello());
 }
}
JBossClientContextBuilder.java
public class JBossClientContextBuilder {
 private String port;
 private String host;
 private String user;
 private String password;
 private boolean noanonymous;

 public JBossClientContextBuilder port(String port) {
  this.port = port;
  return this;
 }

 public JBossClientContextBuilder host(String host) {
  this.host = host;
  return this;
 }

 public JBossClientContextBuilder user(String user) {
  this.user = user;
  return this;
 }

 public JBossClientContextBuilder password(String password) {
  this.password = password;
  return this;
 }

 public JBossClientContextBuilder noanonymous(boolean noanonymous) {
  this.noanonymous = noanonymous;
  return this;
 }

 public Context build() throws NamingException {
  Properties clientProp = new Properties();
  clientProp
    .put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED",
      "false");
  clientProp.put("remote.connections", "default");

  // Default 4447
  clientProp.put("remote.connection.default.port", StringUtils.defaultIfEmpty(port, "4447"));

  // Default 127.0.0.1
  clientProp.put("remote.connection.default.host",
    StringUtils.defaultIfEmpty(host, "127.0.0.1"));

  if (!StringUtils.isEmpty(user)) {
   clientProp.put("remote.connection.default.username", user);
  }
  if (!StringUtils.isEmpty(password)) {
   clientProp.put("remote.connection.default.password", password);
  }

  if (noanonymous) {
   clientProp
     .put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS",
       "true");
   clientProp
     .put("remote.connection.default.connect.options.org.xnio.Options.SASL_DISALLOWED_MECHANISMS",
       "JBOSS-LOCAL-USER");
  }
  clientProp
    .put("jboss.naming.client.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT",
      "false");

  System.out.println(Arrays.deepToString(clientProp.entrySet().toArray(
    new Map.Entry[0])));

  EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(
    clientProp);
  ContextSelector<EJBClientContext> selector = new ConfigBasedEJBClientContextSelector(
    cc);
  EJBClientContext.setSelector(selector);

  Properties props = new Properties();
  props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");

  return new InitialContext(props);
 }
}