Script Your Remote Session Beans with Groovy

A JavaEE application with a multi-tier application generally has a presentation layer running in a servlet engine, a business logic layer running in a EJB container and a persistence layer facilitating JPA. During the development often there are occasions where a new functionality in the business logic (called backend from now on), which has no corresponding code in the presentation layer (let’s name it frontend), yet. So, what you need is a way to try out your code. This post shows you how to use the groovy shell to connect via JNDI to your remote session beans and call them in order to test your application fast. You may use it for a fast monitoring or maintenance API to your system, too. The groovy code of your efforts to test your code (monitor your application) may even be read from the history of the groovy shell and compiled into byte code. This code may be called from a test case or in a monitoring software (like nagios).

At first download the latest groovy binary from codehaus. Second, unpack it to a folder you like, add ${GROOVY_HOME}/bin to environment variable PATH and export a new environment variable called GROOVY_HOME pointing at the main directory of the groovy installation. On a UNIX system you may add this to your ~/.profile or ~/.bashrc. source your changed config like this: . ~/.bashrc. Start groovysh in order to create the .groovy-directory in your home directory. Copy the following grapeConfig.xml to this directory:

<?xml version="1.0"?>
<ivysettings>
  <settings
    defaultResolver="downloadGrapes"
    checkmodified="true"
    checkUpToDate="true"
    changingPattern=".*-SNAPSHOT"/>
  <resolvers>
    <chain name="downloadGrapes">
      <filesystem name="cachedGrapes">
	<ivy pattern=
	  "${user.home}/.groovy/grapes/[organisation]/[module]/ivy-[revision].xml"/>
	<artifact pattern=
	  "${user.home}/.groovy/grapes/[organisation]/[module]/[type]s/[artifact]-[revision].[ext]"/>
      </filesystem>
      <ibiblio
	name="ibiblio"
	m2compatible="true"/>
      <ibiblio
	name="JBoss Maven Repo"
	root="http://repository.jboss.com/maven2/"
	m2compatible="true" />
      <ibiblio
	name="localM2"
	root="file://${user.home}/.m2/repository"
	m2compatible="true" />
    </chain>
  </resolvers>
</ivysettings>

The order of resolvers in the chain is important. Don’t put your local m2 repository at the beginning of the chain. Maven 3.0.x seems to omit some jars and download the pom files only. This leads to download failures in Grapes. What we have done so far is install groovy and setup Ivy for dependency resolving with Grape.

Next, you should add a little maven project, handling the retrieval of the remote session beans and the authentication to the jboss for us. Use the following pom.xml:

<project
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>my.group.id</groupId>
	<artifactId>myapp-cli</artifactId>
	<version>0.0.1</version>
	<name>MYAPP - Command Line Interface</name>
	<description>A small package, which can be used in a script interpretert like groovy shell
	  for a command line interface to myapp</description>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.0.2</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>jboss</groupId>
			<artifactId>jbosssx-client</artifactId>
			<version>3.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.jboss.security</groupId>
			<artifactId>jboss-security-spi</artifactId>
			<version>2.0.4</version>
		</dependency>
		<dependency>
			<groupId>org.jboss.jbossas</groupId>
			<artifactId>jboss-as-client</artifactId>
			<version>5.1.0.GA</version>
			<type>pom</type>
		</dependency>
		<dependency>
			<groupId>my.group.id</groupId>
			<artifactId>myapp-backend-api</artifactId>
			<version>SNAPSHOT</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
	</dependencies>
	<repositories>
		<repository>
			<id>JBoss Maven Repo</id>
			<name>JBoss Maven Repo</name>
			<url>http://repository.jboss.com/maven2/</url>
		</repository>
	</repositories>
</project>

In src/main/resources/ you will need an auth.conf like this, for authentication with jaas to the jboss:

myapp {
   org.jboss.security.ClientLoginModule required debug=true;
};

Last but not least you need a util class connecting to jboss, authenticate via jaas and retrieve the session bean from JNDI:

public class BeanLoaderUtil {
	private static MySessionBeanRemote mySB;

	public static void login(String host, String port, String username,
			String password) throws Exception {
		ClassLoader cl = ControllerUtil.class.getClassLoader();
		// The context class loader does not do anything in the
		// groovy shell, so we replace him by the classloader of the class.
		// This will help jaas code to load the class ClientLoginModule.
		Thread.currentThread().setContextClassLoader(cl);
		URL authconf = cl.getResource("auth.conf");
		String p = URLDecoder.decode(authconf.toExternalForm(), "UTF-8");
		System.setProperty("java.security.auth.login.config", p);

		loginContext = new LoginContext("myapp",
			new UsernamePasswordHandler(username, password));
		loginContext.login();

		Properties env = new Properties();
		env.setProperty(Context.INITIAL_CONTEXT_FACTORY,
			"org.jboss.security.jndi.JndiLoginInitialContextFactory");
		env.setProperty("java.naming.factory.url.pkgs",
			"org.jboss.naming:org.jnp.interfaces");
		env.setProperty("java.naming.factory.initial",
			"org.jboss.naming.NamingContextFactory");
		env.setProperty("java.naming.factory.url.pkgs",
			"org.jboss.naming:org.jnp.interfaces");
		env.setProperty("java.naming.provider.url",
			"jnp://" + host +":" + port);

		InitialContext ctx = new InitialContext(env);

		try {
			mySB = (MySessionBeanRemote) ctx.lookup("MySessionBean/remote");
		}
		finally {
			ctx.close();
		}
	}

	public static void logout() throws Exception {
		if(loginContext != null) {
			loginContext.logout();
		}
	}

	public static PaperController getMySB() {
		return mySB;
	}

	public static void main(String args[]) throws Exception {
		login("localhost", "1099", "myuser", "mypwd");
	}
}

The mvn clean install the artifact myapp-cli-0.0.1.jar will be installed in the local repository. We could use grape in the groovy shell in order to load the myapp-cli artifact and import the BeanLoaderUtil in order to start working with your beans, but unfortunately is the Grape-class not very verbose, so lets load all the transitive dependencies with the grape command line tool instead:

$ grape install my.group.id myapp-cli 0.0.1
:: loading settings :: url = jar:file:/opt/groovy-1.7.10/lib/ivy-2.2.0.jar!/org/apache/ivy/core/settings/ivysettings.xml
:: resolving dependencies :: caller#all-caller;working
        confs: [default]
        found my.group.id#myapp-cli;0.0.1 in localM2
        found jboss#jbosssx-client;3.2.3 in ibiblio
        found org.jboss.security#jboss-security-spi;2.0.4 in JBoss Maven Repo
        found org.jboss.jbossas#jboss-as-client;5.1.0.GA in JBoss Maven Repo
        found commons-logging#commons-logging;1.1.0.jboss in JBoss Maven Repo
        found oswego-concurrent#concurrent;1.3.4-jboss-update1 in JBoss Maven Repo
        found org.hibernate#ejb3-persistence;1.0.2.GA in ibiblio
        found org.hibernate#hibernate-annotations;3.4.0.GA in ibiblio
        found org.hibernate#hibernate-commons-annotations;3.1.0.GA in ibiblio
        found org.slf4j#slf4j-api;1.5.6 in ibiblio
...

There may be problems loading some artifacts, as your maven repository has grown uncontrolled over different maven versions. If this is the case, try deleting your local maven repository or parts of it (it is only a cache), in order to get it up and running. The grape command will be really slow the first time, because it checks all resolvers for every dependency and downloads/copies it to the local grapeCache. So, further calls will be much faster (though they are still slow…). As soon as the grape install command returns successfully start the groovy shell and type the following statements:

groovy:000> import static groovy.grape.Grape.*;
===> [import static groovy.grape.Grape.*;]
groovy:000> grab([group:"my.group.id", module:"myapp-cli", version:"0.0.1"]);
===> null
groovy:000> import static my.group.id.BeanLoaderUtil.*;
===> [import static groovy.grape.Grape.*;, import static my.group.id.BeanLoaderUtil.*;]
groovy:000> login("localhost", "1099", "myuser", "mypwd");
===> null
groovy:000> mySB = getMySB();
===> Proxy to jboss.j2ee:ear=myapp-SNAPSHOT.ear,jar=myapp-backend-impl-SNAPSHOT.jar,name=MySessionBean,service=EJB3 implementing [interface my.group.id.backend.MySessionBeanRemote]
groovy:000> myPojo = mySB.getFoo("The incredible foo bar");
===> Foo[id=1,version=10,longName=The incredible foo bar]
groovy:000> println myPojo.longName;
The incredible foo bar
===> null

Of course you need a jboss up and runnning, listening on jndi port 1099 with a corresponding EJB application. Jeehaa!

Addendum

If you are encountering EJBAccess exceptions you might try to change the authentication type. Replace lines 11-17 in your BeanLoaderUtil with the following code:

SecurityClient client = SecurityClientFactory.getSecurityClient();
client.setSimple(username, password);
client.login();

You won’t need the auth.conf anymore, too.

Leave a Reply

Your email address will not be published. Required fields are marked *