2009-09-25

Using Gradle to deploy to Glassfish

As mentioned before, I've used Gradle for my build in my latest project. I started to get tired of logging on to the admin console to deploy the WAR file so it was time to look at Glassfish & Gradle in combination.

Glassfish 2.1 includes some ANT tasks which is a wrapper to the asadmin command line tools. Using ANT tasks from Gradle is really simple - even those who aren't part of the main ANT distribution.

I started out with checking if the machine has an environment variable pointing to the glassfish install directory:

gfHome = System.getenv('GLASSFISH_HOME')
logger.info("Glassfish home: " + gfHome)
if (gfHome == null || gfHome.length() <= 0)
{
msg = "No GLASSFISH_HOME in environment variable. Please set GLASSFISH_HOME to glassfish installation directory"
logger.error(msg)
throw new RuntimeException(msg)
}
This will throw an error and the build script will fail if the environment variable GLASSFISH_HOME isn't set. If this succeeds I need to define the task by using ANT's taskdef from Gradle.

ant.taskdef(name: 'gfDeploy',
classname: 'org.apache.tools.ant.taskdefs.optional.sun.appserv.DeployTask') {
classpath {
fileset(dir: gfHome + File.separator + 'lib') {
include(name: '*.jar')
}
}
}
I use the lib directory of the Glassfish install directory and add all jar files to the classpath.

In order to use the deploy task, I need a password file which contains the password for the admin user. I've added a file called dev.passfile in the project directory and it contains a singel line:

AS_ADMIN_PASSWORD=adminadmin
Then the only thing which is missing is calling the task:

ant.gfdeploy(file: war.archivePath.path, name: project.name, contextroot: project.name,
upload: 'true', precompilejsp: 'false', asinstalldir: gfHome) {
server(host: 'mydevserver', port: '40048', user: 'admin', passwordfile: 'dev.passfile')
}
As you see I can reference the war file with the war.archivePath.path. This is a handy shortcut to reference the generated WAR file. Also used the project.name as the context root which avoids the version number in the URL.

Since I need to define an undeploy tasks also I can use the power of Groovy and separate out the commen part as a method. For instance I need to check for GLASSFISH_HOME environment variable for each task I define so I put this in a separate method:

def getGlassfishHomeDir()
{
gfHome = System.getenv('GLASSFISH_HOME')
logger.info("Glassfish home: " + gfHome)
if (gfHome == null || gfHome.length() <= 0)
{
msg = "No GLASSFISH_HOME in environment variable. Please set GLASSFISH_HOME to glassfish installation directory"
logger.error(msg)
throw new RuntimeException(msg)
}
return gfHome
}
This can then be called from every gradle tasks.

Smooth :-)

2009-07-10

GWT and AES decryption

I’ve been working on a simple GWT application which had to decrypt some content in the browser. The requirement was that this should happen in the browser – as close to the end user as possible.

The first solution I considered was to use Java code which then would be generated into JavaScript with the GWT compiler. I couldn’t use the built in support in Java since these classes isn’t supported by the GWT compiler, but I thought I might find an implementation which could be used. After some research I found this comment and the specifically the first point:

virtually all crypto libraries out there,… to asymmetric 
(RSA, DSA, 'public/private keypairs') rely heavily on register
looping; the idea that the maximum value an integer field will
hold + 1 'loops around' silently to the minimum value.

javascript numbers do not behave that way (they upgrade
themselves silently to doubles) - and GWT's 'ints', for
reasons of speed, aren't objectified stuff that
software-matically does integer math, they are just translated
to javascript numbers. Hence this looping never occurs, and all

those libraries get confused, and they don't work. You're FAR
FAR better off finding a proper chunk of code designed for
javascript specifically, and wrapping that using JSNI.

This certainly made me look the other way which was to embed JavaScript implementation of the AES algorithm. We had some requirements to the algorithm implementation:


  • Support for 256 bit key lengths

  • Support for salt

  • Cipher Block Chaining

  • Support for initialization vector

  • Handle PKCS-7 padding

After some trial with several implementations we finally got the pidCrypt library working with the content encrypted by our WPF .NET client. It is hosted on SourceForge and the team has been really responsive to questions regarding challenges that I experienced.

In order to get this to work with GWT I had to do the following:

Add the JavaScript to the project

I added the following JavaScript files pidcrypt.js, pidcrypt_util.js, aes_core.js, aes_cbc.js and md5.js to the project (src/main/webapp).

Make those JavaScripts available to the GWT applicaiton

I had to declare the scripts in my GWT application configuration:

<script src="pidcrypt.js" />
<script src="pidcrypt_util.js"/>
<script src="md5.js" />
<script src="aes_core.js" />
<script src="aes_cbc.js" />
Wrap those JavaScript calls with native methods

In order to call those JavaScripts from my "regular Java code" (the Java code which gets compiled to JavaScript), I had to wrap the JavaScript calls with native methods:

public static native String Decrypt(String encryptedText, String key)
/*-{
var aes = new $wnd.pidCrypt.AES.CBC();
var iv = '4857487548754874587549889898';

aes.initByValues(encryptedText, key, iv, {nBits:256, A0_PAD:false});

return aes.decrypt();
}-*/;

With this in place, I was able to decrypt some of the content which our .NET client had encrypted.

2009-07-03

Building GWT application with Gradle

I wanted to check out Gradle in a small GWT application that we build for a very specific purpose. The Gradle version I've used is 0.61. Gradle has a plugin concept, but I haven't seen any GWT plugin for Gradle. The setup ended up being rather easy.

I defined a task which used ANT's Java task:

task gwtCompile << {
created = (new File(gwtBuildDir)).mkdirs()
ant.java(classname:'com.google.gwt.dev.Compiler', failOnError: 'true', fork: 'true') {
jvmarg(value: '-Xmx184M')
arg(line: '-war ' + gwtBuildDir)
arg(line: '-logLevel INFO')
arg(line: '-style PRETTY')
arg(value: 'me.trond.app.MyApp')
classpath {
pathElement(location: srcRootName + '/' + srcDirNames[0])
pathElement(path: configurations.compile.asPath)
pathElement(path: configurations.gwtCompile.asPath)
}
}
}


If you look at the classpath elements I've included a special configuration called gwtCompile. I had to define my own configuration:

configurations {
gwtCompile
}


This also had to be reflected in the dependencies:

dependencies {
...
gwtCompile (
[group: 'com.google.gwt', name: 'gwt-user', version: '1.6.4'],
[group: 'com.google.gwt', name: 'gwt-dev', version: '1.6.4', classifier: 'windows']
)
}


Since I use the war plugin for gradle I had to make sure that gwtCompile task got called before packaging up the war file:

war.dependsOn gwtCompile


Also ensured that the result of the gwtCompile got included in the war file:

war {
//Adds the JavaScript and resources compiled by the GWT compiler
fileSet(dir: file(gwtBuildDir) )
}

2009-01-29

Glassfish: starting node agents in clustered setup fails with ExceptionInInitializerError

We're starting to use glassfish as our application server. The major reason going from Tomcat was that we needed more fully-fledged appserver with JMS functionality. We have had some issues regarding configuring glassfish - especially for cluster support.

I started with a fresh glassfish 2.1 install and headed for a clustered setup. I've seen a few blog post regarding this which made it look simple enough. So I started out with the following commands:


lib/ant/bin/ant -f setup-cluster.xml -Ddomain.name=myDomain -Dadmin.user=admin
-Dadmin.password=password -Dmaster.password=password
bin/asadmin start-domain myDomain
bin/asadmin create-node-agent --host localhost --port 4848 --user admin
--passwordfile /home/glassfish/passfile agentOnFirst
bin/asadmin create-cluster --host localhost --port 4848 --user admin
--passwordfile /home/glassfish/passfile myCluster
bin/asadmin create-instance --host localhost --port 4848 --user admin
--passwordfile /home/glassfish/passfile --nodeagent agentOnFirst --cluster
myCluster instance1


The passfile contained

AS_ADMIN_PASSWORD=password
AS_ADMIN_MASTERPASSWORD=password


Which is what I issued to the setup-cluster.xml ANT build script. When I tried to run the node-agent I got an ExceptionInInitializerError. I tried many different combination - like supplying password when requested instead of providing this as parameters, but it still seems to fail.

I was not able to get the node-agent to start until I tried without changing the admin/master password. If I rather just used the standard passwords, the nodeagent started just fine. Here the commands:


lib/ant/bin/ant -f setup-cluster.xml -Ddomain.name=myDomain
bin/asadmin start-domain myDomain
bin/asadmin create-node-agent --host localhost --port 4848 --savemasterpassword agentOnFirst
bin/asadmin create-cluster --host localhost --port 4848 myCluster
bin/asadmin create-instance --host localhost --port 4848 --nodeagent agentOnFirst --cluster myCluster instance1
bin/asadmin start-node-agent agentOnFirst


I've reported this as a bug as I still consider the original commands or variants of them to be correct.