HTTPBuilder and mock HTTPBuilder in Groovy

HTTPBuilder is a wrapper of Apache famous HttpClient. However, job done in Groovy is like a breathe of fresh air compared to Java.

Here is an example, using HTTPBuilder make a REST service call, getting JSON response, plus the integration test. Implemented in Grails 1.3.5. Maybe you don’t fully understand why do this, why do that, but just look and feel codes at first.

  • in Config.groovy define web service URI in location.service.uri:

[groovy]
import grails.plugins.springsecurity.SecurityConfigType

environments {
production {
grails.config.locations = [ "classpath:production.properties"]
}
development {
grails.serverURL = "http://localhost:8080/${appName}"
location.service.uri = "http://somewhere.over.the.rainbow:10080"
}
test {
grails.serverURL = "http://localhost:8080/${appName}"
location.service.uri = "http://dummyURI/"
}
}

/——————————————————————————
// Security configuration
//——————————————————————————
grails.plugins.springsecurity.providerNames = [‘preauthAuthProvider’]
grails.plugins.springsecurity.auth.loginFormUrl = ‘/login’
grails.security.rolePrefix = ""
grails.plugins.springsecurity.filterChain.stripQueryStringFromUrls = false

// WARNING: if you use the following "InterceptUrlMap" it will override the default
// framework setting of "Annotation" where the security is driven at the controller
// class level
grails.plugins.springsecurity.securityConfigType = SecurityConfigType.InterceptUrlMap
grails.plugins.springsecurity.interceptUrlMap = [
‘/*/adminHome/**’: [‘ROLE_SUPER_ADMIN’, ‘IS_AUTHENTICATED_FULLY’],
‘/adminHome/**’: [‘ROLE_SUPER_ADMIN’, ‘IS_AUTHENTICATED_FULLY’],
‘/*/workCentre/**’: [‘ROLE_SUPER_ADMIN’, ‘IS_AUTHENTICATED_FULLY’],
‘/workCentre/**’: [‘ROLE_SUPER_ADMIN’, ‘IS_AUTHENTICATED_FULLY’],
‘/*/user/**’: [‘ROLE_SUPER_ADMIN’, ‘ROLE_WC_ADMIN’, ‘IS_AUTHENTICATED_FULLY’],
‘/user/**’: [‘ROLE_SUPER_ADMIN’, ‘ROLE_WC_ADMIN’, ‘IS_AUTHENTICATED_FULLY’],
‘/*/parcel/**’: [‘ROLE_WC_USER’, ‘ROLE_WC_ADMIN’, ‘IS_AUTHENTICATED_FULLY’],
‘/parcel/**’: [‘ROLE_WC_USER’, ‘ROLE_WC_ADMIN’, ‘IS_AUTHENTICATED_FULLY’]
]
[/groovy]

  • in resources.xml define HTTPBuilder bean:

[xml]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans&quot;
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quot;
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"&gt;

<bean id="httpBuilder" class="groovyx.net.http.HTTPBuilder">
<constructor-arg value="${location.service.uri}" />
</bean>

</beans>
[/xml]

  • in resources.groovy file:

[groovy]
import grails.util.GrailsUtil
import org.codehaus.groovy.grails.orm.hibernate.HibernateEventListeners

import org.paradise.manhattan.AuditListener

// Place your Spring DSL code here
beans = {
/**************************************
* Auditing wiring for all data updates
**************************************/
auditListener(AuditListener)
hibernateEventListeners(HibernateEventListeners) {
listenerMap = [‘pre-insert’:auditListener, ‘pre-update’:auditListener]
}

/************************
* Spring Security wiring
************************/
authenticationManager = { authenticationProvider = ref(‘preauthAuthProvider’) }

developmentSecurityFilter(org.paradise.manhattan.security.DevelopmentSecurityFilter) { authenticationManager = ref(‘authenticationManager’) }

preauthAuthProvider(org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider) {
preAuthenticatedUserDetailsService = ref("userProfileService")
throwExceptionWhenTokenRejected = true
}

transformerService(org.paradise.manhattan.integration.TransformerService)

switch(GrailsUtil.environment) {
case "test":
integrationService(test.MockIntegrationService)
break
case "functional":
integrationService(test.MockIntegrationService)
break
case "production":
integrationService(org.paradise.manhattan.integration.DefaultIntegrationService) {
transactionTemplate = ref("transactionTemplate")
manhattanWebServiceTemplate = ref("manhattanWebServiceTemplate")
jmsTemplate = ref("jmsTemplate")
transformerService = ref("transformerService")
httpBuilder = ref("httpBuilder")
}
break
default:
integrationService(org.paradise.manhattan.integration.DefaultIntegrationService) {
transactionTemplate = ref("transactionTemplate")
plisWebServiceTemplate = ref("plisWebServiceTemplate")
jmsTemplate = ref("jmsTemplate")
transformerService = ref("transformerService")
httpBuilder = ref("httpBuilder")
}
break
}
}
[/groovy]

  • In DefaultIntegrationService.groovy:

[groovy]
class DefaultIntegrationService implements IntegrationService {

// inject HTTPBuilder bean
def httpBuilder

void updateLocation(Location location) {

JAXBContext jaxbContext = JAXBContext.newInstance(CustomerCollectionPoints.class)

Marshaller marshaller = jaxbContext.createMarshaller()
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true)

StringResult stringResult = new StringResult()
marshaller.marshal(transformerService.mapLocationToCustomerCollectionPoints(location, Constants.MODIFICATION_TYPE_UPDATE), stringResult)

publishLocation(stringResult)
}

private void publishLocation (StringResult stringResult) {

log.debug (stringResult.toString())

httpBuilder.request(Method.POST) {
// uri.path = "update.xml"
requestContentType = ContentType.URLENC
body = stringResult.toString()

response.success = { resp, reader ->
assert resp.statusLine.statusCode == 200

println "Request succeed with status: ${resp.statusLine}"
println "Response headers:"
resp.headers.each { h ->
println " ${h.name} : ${h.value}"
}
println "Response Data:"
println " " + reader

if (reader[‘success’] == true) {
println "Publish location(s) succeed"
} else {
println "Failed to publish location(s) due to a problem"
throw new LocationServiceException(reader.toString())
}
}

response.failure = { resp ->
log.error "Request failed with status ${resp.status}"
}
}
}

}
[/groovy]

  • In Integration test DefaultIntegrationServiceTests.groovy:

[groovy]
class DefaultIntegrationServiceTests extends GrailsUnitTestCase {

def defaultIntegrationService

void testUpdateLocation() {

def httpBuilder = new MockFor(HTTPBuilder)
def result
def requestDelegate = [response: [‘statusLine’: [‘protocol’: ‘HTTP/1.1′,’statusCode’: 200, ‘status’: ‘OK’]], uri: [:]]
httpBuilder.demand.request { Method method, Closure body ->
body.delegate = requestDelegate
body.setResolveStrategy(Closure.DELEGATE_FIRST)
body.call()
requestDelegate.response.success(requestDelegate.response, [‘success’: true]) // or failure depending on what’s being tested
}

httpBuilder.use {
result = ‘200’ // Example response handler that you set in your request closure parameter
defaultIntegrationService.updateLocation(createLocation())
}
}

}
[/groovy]
Reference

Grails – The pinnacle of Spring MVC

Here is an example demonstrating how light, easy, straight-forward Grails could implement MVC web application that list location address.

  • Data model layer – Location.groovy

[groovy]
class Location implements Serializable {

Long id
String street
String suburb
String state
String postCode

static mapping = {
id generator: ‘sequence’, params: [sequence: ‘seq_location_id’]
}

static constraints = {
street(nullable: false, blank: false, maxSize: 80)
suburb(nullable: false, blank: false, maxSize: 50)
state(nullable: false, blank: false, size: 2..3, inList: Constants.STATES)
postCode(nullable: false, blank: false, size: 4, matches: ‘\d+’)
}
[/groovy]

With Grails’ Object Relational Mapping (ORM) solution – GORM, after run “grails run-app“, database will be initialised, table “LOCATION”, table column created and constrained in an automatic fashion.

Have a look database Location table in SQL:

[sql]
CREATE TABLE "BOB_BUILDER"."LOCATION"
(
"ID" NUMBER(19,0) NOT NULL ENABLE,
"STREET" VARCHAR2(80 CHAR) NOT NULL ENABLE,
"SUBURB" VARCHAR2(50 CHAR) NOT NULL ENABLE,
"STATE" VARCHAR2(3 CHAR) NOT NULL ENABLE,
"POST_CODE" VARCHAR2(4 CHAR) NOT NULL ENABLE
)
[/sql]

  • Control layer – LocationController.groovy

[groovy]
class LocationController {

def index = {
redirect(action: "list", params: params)
}

def list = {
params.max = 10

[locationInstanceList: Location.list(params), locationInstanceTotal: Location.count()]
}

}
[/groovy]

params.max is like cursor on database, telling only want to show 10 Location records in the window.

  • Presentation layer – list.gsp

[html]
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>

<body>

<div class="list">
<table>
<thead>
<tr>
<g:sortableColumn property="street" title="Street"/>
<g:sortableColumn property="suburb" title="Suburb"/>
<g:sortableColumn property="state" title="State"/>
<g:sortableColumn property="postCode" title="Post code"/>
</tr>
</thead>
<tbody>
<g:each in="${locationInstanceList}" status="i" var="locationInstance">
<tr id="location_${locationInstance.id}" class="${(i % 2) == 0 ? ‘odd’ : ‘even’}">
<td>${locationInstance.street}</td>
<td>${locationInstance.suburb}</td>
<td>${locationInstance.state}</td>
<td>${locationInstance.postCode}</td>
</tr>
</g:each>
</tbody>
</table>
</div>

<div class="paginateButtons">
<g:paginate total="${locationInstanceTotal}"/>
</div>

</body>
[/html]

Now you have a web page showing the list  of locations with pagination feature. URLs like

all in REST fashion. Moreover, you can sort each column in the list. For example:

parameters “sort” and “orders” will be passed as controller.params to Data model layer, and will be sorted by the backend database.

Less code. Simply the best.

Grails event triggered by compile codes and build war …

This solution show you how in Grails framework, if you want to add and trigger an event (checking SVN repository for the latest revision number and adding to app.properties file) during (before or after) application codes compiled and war built.

[groovy]
import org.tmatesoft.svn.core.wc.SVNClientManager
import org.tmatesoft.svn.core.wc.SVNStatus
import org.tmatesoft.svn.core.SVNException
import org.tmatesoft.svn.core.wc.SVNWCClient
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory
import org.tmatesoft.svn.core.wc.SVNInfo
import org.tmatesoft.svn.core.wc.SVNRevision

eventCompileStart = {
println "[Event] Compile starts"
}

eventWarStart = {
def basedir = ‘.’

println "[Event] Build war starts (under project root directory)"

try {
SVNStatus svnStatus = SVNClientManager.newInstance().getStatusClient().doStatus(new File(basedir), false)

String url = svnStatus.getURL()
println ‘SVN repository URL = ‘ + url
String svnRevision = svnStatus.getCommittedRevision()
println ‘SVN committed revision number = ‘ + svnRevision

// String compiler = System.getenv(‘Compiler’)

// DAVRepositoryFactory.setup();
// SVNRepositoryFactoryImpl.setup();
// FSRepositoryFactory.setup();
//
// SVNClientManager clientManager = SVNClientManager.newInstance();
// SVNWCClient wcClient = clientManager.getWCClient();
// File baseFile = new File(basedir);
// SVNInfo svninfo = wcClient.doInfo(baseFile, SVNRevision.WORKING);
// def svnRevision = svninfo.getRevision().number;

println ‘Current SVN revision number in application.properties = ‘ + metadata.’svn.revision’

metadata.’svn.revision’ = svnRevision.toString()
metadata.persist()
} catch (SVNException ex) {
// something went wrong
println "**************** SVN Exception **************"
println ex.getMessage();
}
}

eventWarEnd = {
println "[Event] Build war ends ${appName} ${appVersion}"
}
[/groovy]

Take a screenshot in Selenium functional test

Want to take a screenshot every time after one functional test is finished. Here is how:

[groovy]

import org.junit.After
import org.junit.Before
import org.openqa.selenium.WebDriver
import org.openqa.selenium.WebDriverBackedSelenium
import org.openqa.selenium.TakesScreenshot
import org.openqa.selenium.OutputType
import org.apache.commons.io.FileUtils
import org.junit.Rule
import org.junit.rules.TestName

abstract class SeleniumScenario {

@Rule
public TestName testName = new TestName();

@Before
public void setup() {
SeleniumEngine.initialiseSelenium()
}

@After
public void close() {
takeScreenShot()

SeleniumEngine.closeSelenium()
}

void takeScreenShot() {
File screenShot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE)

String className = this.getClass().getSimpleName();
String methodName = this.testName.getMethodName();

String filename = ‘manhattan-functional-‘ + className + ‘-‘ + methodName + ‘.png’

// screenShot is the file we have acquired; filename is the name of the image file
try {
FileUtils.copyFile(screenShot, new File(filename))
} catch (IOException e) {
e.printStackTrace()
}
}

}
[/groovy]

Functional test in Grails with Selenium 2 (Selenium 1 + webdriver)

There are a lot goodness me moment when program in Groovy and Grails. This is one goodie – an example of functional test in Grails with Selenium 2 (actually it “pronounces” Selenium 1 + webdriver)

SeleniumEngine class

[groovy]
package org.paradise.manhattan

import org.openqa.selenium.WebDriverBackedSelenium
import org.openqa.selenium.ie.InternetExplorerDriver
import grails.util.BuildSettings

class SeleniumEngine {
static String baseUrl
static WebDriverBackedSelenium currentSelenium

static void initialiseSelenium() {
// get the default baseUrl or baseUrl set on the command line, e.g:
// $GRAIL_HOME/bin/grails test-app -functional -baseUrl=http://apdev/ApolloRuralBank
baseUrl = System.getProperty(BuildSettings.FUNCTIONAL_BASE_URL_PROPERTY)

// remove the last character in Url if it’s ‘/’
if (baseUrl[-1..-1] == ‘/’) {
baseUrl = baseUrl[0..-2]
}

if (currentSelenium == null) {
currentSelenium = new WebDriverBackedSelenium(new InternetExplorerDriver(), baseUrl)
}
}
static void closeSelenium() {
currentSelenium.close()
currentSelenium = null
}
}
[/groovy]

SeleniumScenario class

[groovy]
package org.paradise.manhattan

import org.junit.After
import org.junit.Before
import org.openqa.selenium.WebDriver
import org.openqa.selenium.WebDriverBackedSelenium

abstract class SeleniumScenario {

static final int TIMEOUT = 60000;

@Before
public void setup() {
SeleniumEngine.initialiseSelenium()
}

@After
public void close() {
SeleniumEngine.closeSelenium()
}

WebDriverBackedSelenium getSelenium() {
return SeleniumEngine.currentSelenium
}

WebDriver getDriver() {
return SeleniumEngine.currentSelenium.wrappedDriver
}

String getBaseUrl() {
return SeleniumEngine.baseUrl
}

void waitForPageToLoad() {
selenium.waitForPageToLoad(String.valueOf(TIMEOUT))
}
}
[/groovy]

AbstractPage class

[groovy]
package org.paradise.manhattan.pages

import org.openqa.selenium.By
import org.openqa.selenium.Keys
import org.openqa.selenium.WebDriver
import org.openqa.selenium.WebDriverBackedSelenium
import org.openqa.selenium.WebElement

import org.paradise.manhattan.SeleniumScenario

abstract class AbstractPage {

SeleniumScenario scenario

AbstractPage(SeleniumScenario scenario) {
this.scenario = scenario
}

void go() {
scenario.selenium.open(scenario.baseUrl + pageUrl)
}

abstract String getPageUrl()

WebDriverBackedSelenium getSelenium() {
return scenario.selenium
}

WebDriver getDriver() {
return scenario.driver
}

void enterKey(Keys key) {
driver.findElement(By.id(‘container’)).sendKeys(key)
}

void enterKey(String id, Keys key) {
driver.findElement(By.id(id)).sendKeys(key)
}

void enterValue(String id, String value) {
driver.findElement(By.id(id)).sendKeys(value)
}

void clickOn(String id) {
driver.findElement(By.id(id)).click()
}
}
[/groovy]

PreliminaryAssessmentPage class

[groovy]
package org.paradise.manhattan.pages

import org.paradise.manhattan.SeleniumScenario
import org.openqa.selenium.By
import org.openqa.selenium.Keys
import org.openqa.selenium.WebElement

class PreliminaryAssessmentPage extends AbstractPage {

String pageUrl = ‘/preliminaryAssessment’

PreliminaryAssessmentPage(SeleniumScenario scenario) {
super(scenario)
}

boolean verify() {
scenario.waitForPageToLoad()

WebElement activeClass = driver.findElement(By.className(‘active’))
return activeClass.text == ‘Preliminary Assessment’ ? true : false
}

def input() {
enterValue(‘allPresent’, ‘Y’)
enterKey(Keys.F9)

return new VerificationPage(scenario)
}

}
[/groovy]

There are more codes here

[groovy]
package org.paradise.manhattan

import static org.junit.Assert.*

import org.junit.Before
import org.junit.Test

import org.paradise.manhattan.pages.PreliminaryAssessmentPage
import org.paradise.manhattan.VerificationPage

class AuthorisationTests extends SeleniumScenario {

PreliminaryAssessmentPage preliminaryAssessmentPage
VerificationPage verificationPage

@Before
public void setup() {
super.setup()
}

@Test
public void testHappyDay() {
preliminaryAssessmentPage = new PreliminaryAssessmentPage(this)
preliminaryAssessmentPage.go()
assertTrue(preliminaryAssessmentPage.verify())

verificationPage = preliminaryAssessmentPage.input()
assertTrue(verificationPage.verify())
}
}
[/groovy]

application.properties

#Grails Metadata file
#Wed Aug 15 16:29:51 EST 2010
app.grails.version=1.3.5
app.name=Manhattan
app.servlet.version=2.4
app.version=1.0
plugins.cachefilter=0.5
plugins.code-coverage=1.2.5
plugins.jasper=1.5.2
plugins.tomcat=1.3.5
plugins.webdriver=0.3.3

By default to run functional test:

$GRAILS_HOME/bin/grails test-app -functional

This will let Grails bring up the application run on localhost:8080/${app.name}. Then there comes goodie thing.

When you want to run your hard written functional test testing application runs on a remote host, without start up application on localhost:

$GRAILS_HOME/bin/grails test-app -functional -baseUrl=http://somewhere-over-the-rainbow/app

On Windows machine, add double quotes on “baseUrl”:

$GRAILS_HOME/bin/grails test-app -functional "-baseUrl=http://somewhere-over-the-rainbow/app"

Reference

Mock object return non-sense error in unit test in Grails

Strange problem, actually it’s not strange at all after the investigation, encountered when develop unit text for a Grails project by using the mock service:

org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Error converting Bean with class grails.test.DemandProxy
org.apache.commons.lang.UnhandledException: org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Error converting Bean with class grails.test.DemandProxy
at grails.test.MockUtils$_mockController_closure11.doCall(MockUtils.groovy:121)
at org.paradise.manhattan.controller.MockController$_closure12.doCall(MockController.groovy:316)
at org.paradise.manhattan.controller.MockController$_closure12.doCall(MockController.groovy)
at org.paradise.manhattan.controller.MockControllerTests.testGetInterestRate(MockControllerTests.groovy:140)
Caused by: org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Error converting Bean with class grails.test.DemandProxy
at grails.converters.JSON.value(JSON.java:198)
at grails.converters.JSON.convertAnother(JSON.java:161)
at grails.converters.JSON.value(JSON.java:198)
at grails.converters.JSON.convertAnother(JSON.java:161)
at grails.converters.JSON.value(JSON.java:198)
at grails.converters.JSON.convertAnother(JSON.java:161)
at grails.converters.JSON.value(JSON.java:198)
at grails.converters.JSON.convertAnother(JSON.java:161)
at grails.converters.JSON.value(JSON.java:198)
at grails.converters.JSON.render(JSON.java:133)
at grails.test.MockUtils$_mockController_closure11.doCall(MockUtils.groovy:121)
at org.paradise.manhattan.controller.MockController$_closure12.doCall(MockController.groovy:316)
at org.paradise.manhattan.controller.MockController$_closure12.doCall(MockController.groovy)
at org.paradise.manhattan.controller.MockControllerTests.testGetInterestRate(MockControllerTests.groovy:140)
at _GrailsTest_groovy$_run_closure4.doCall(_GrailsTest_groovy:269)
at _GrailsTest_groovy$_run_closure4.call(_GrailsTest_groovy)
at _GrailsTest_groovy$_run_closure2.doCall(_GrailsTest_groovy:226)
at _GrailsTest_groovy$_run_closure1_closure21.doCall(_GrailsTest_groovy:185)
at _GrailsTest_groovy$_run_closure1.doCall(_GrailsTest_groovy:172)
at TestApp$_run_closure1.doCall(TestApp.groovy:101)
at gant.Gant$_dispatch_closure5.doCall(Gant.groovy:381)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy:415)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:427)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:415)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.executeTargets(Gant.groovy:590)
at gant.Gant.executeTargets(Gant.groovy:589)
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: -1
at java.lang.AbstractStringBuilder.substring(AbstractStringBuilder.java:881)
at java.lang.StringBuilder.substring(StringBuilder.java:55)
at grails.converters.JSON.handleCircularRelationship(JSON.java:365)
at grails.converters.JSON.value(JSON.java:190)
at grails.converters.JSON.convertAnother(JSON.java:161)

Then find out it’s wrong in implement of mockfor in Grails and mockfor is buggy. And improved solution:

[java]
……
import static org.hamcrest.CoreMatchers.*
import static org.junit.Assert.assertThat
……
import grails.test.ControllerUnitTestCase
import org.codehaus.groovy.grails.commons.ConfigurationHolder
……
import apolloruralbank.BusinessDataTransformationService
import org.paradise.schemas.productandservicefulfilment.financialservices.getinterestrate.v1.GetInterestRateRequestType
import org.paradise.manhattan.integration.ManhattanWebService
import grails.converters.JSON
import groovy.mock.interceptor.MockFor

class MockControllerTests extends ControllerUnitTestCase {
……
def manhattanWebServiceMock
def businessDataTransformationServiceMock

protected void setUp() {
super.setUp()

def mockConfig = new ConfigObject()
mockConfig.identityVerification.baseRedirectUrlCoan = ‘testBaseRedirectUrl’
mockConfig.grails.serverURL = "http://abcdefg&quot;
ConfigurationHolder.config = mockConfig

def productDetails = new ProductDetails()
productDetails.productGroup = ‘Term Deposit’
productDetails.productType = ‘Personal’
productDetails.amount = ‘500’
productDetails.term = ‘2 Months’
productDetails.interestPayableOptions = ‘Paid at Maturity’
businessDataTransformationServiceMock = new MockFor(BusinessDataTransformationService)
businessDataTransformationServiceMock.ignore.generateGetInterestRateRequest { p1 ->
GetInterestRateRequestType soapRequest = new GetInterestRateRequestType()

soapRequest.setProductGroup(productDetails.productGroup)
soapRequest.setProduct(productDetails.product)
soapRequest.setProductType(productDetails.productType)
soapRequest.setAmount(new BigDecimal(productDetails.amount))

return soapRequest
}
controller.businessDataTransformationService = businessDataTransformationServiceMock.proxyInstance()
manhattanWebServiceMock = new MockFor(ManhattanWebService)
manhattanWebServiceMock.ignore.getInterestRate { p1, p2 -> return 6.75 }
controller.manhattanWebService = manhattanWebServiceMock.proxyInstance()
}
……
void testGetInterestRate() {
def jsonResult

controller.params.productGroup = ‘Term Deposit’
controller.params.productType = ‘Personal’
controller.params.amount = ‘500’

controller.params.interestPayableOption = ‘Paid at Maturity’

controller.params.term = ‘1 Month’
controller.getInterestRate()
jsonResult = JSON.parse(controller.response.contentAsString)
assertThat("JSON return is wrong", jsonResult.proposedInterestRate.toString(), is("6.75"))

controller.params.interestPayableOption = ‘Paid Monthly’
controller.params.term = ’24 Months’
shouldFail(RuntimeException){
controller.getInterestRate()
}
}
[/java]

Soap web service with client certificate authentication

In my previous post Example of SOAP web service call in Grails, it demos how to make soap web service call in Grails, over http. For further complicated requirement, web service all need to over https this time, and need to be with client certificate authentication.

How to do that in a rapid development environment?

SoapUI is your good friend.

But firstly, we need a self-signed CA (Certificate Authority) certificate, by using keytool in JDK.

terrence@stingy ~
09:23:59 122 $ keytool -genkeypair -alias soapui -keystore soapui.jks
Enter keystore password: XXXXXXXX 
Re-enter new password: XXXXXXXX
What is your first and last name?
  [Unknown]:  Terrence Miao
What is the name of your organizational unit?
  [Unknown]:  Scrum Line
What is the name of your organization?
  [Unknown]:  Paradise
What is the name of your City or Locality?
  [Unknown]:  Eden
What is the name of your State or Province?
  [Unknown]:  Heaven
What is the two-letter country code for this unit?
  [Unknown]:  
Is CN=Terrence Miao, OU=Scrum Line, O=Paradise, L=Eden, ST=Heaven, C=Unknown correct?
  [no]:  yes

Enter key password for 
	(RETURN if same as keystore password):

Setup SSL certificate for mock truststore and keystore in SoapUI preference:

Start Grails project by command run-app with Java VM option as the following:

-Djavax.net.ssl.trustStore=/Users/terrence/Projects/Star/test/functional/soapui.jks
-Djavax.net.ssl.trustStorePassword=soapui
-Djavax.net.ssl.keyStore=/Users/terrence/Projects/Star/test/functional/soapui.jks
-Djavax.net.ssl.keyStorePassword=soapui

This is the screenshot from IntelliJ:

In Grails project, endpoint for mock web service:

soap.apollo.defaultUri = "https://localhost:18443/mockFinancialServices"

Example of SOAP web service call in Grails

Try to make SOAP web service call in Grails. How to do it? There is Spring web service client – http://static.springsource.org/spring-ws/site/reference/html/client.html

Groovy is Java in disguise. And Grails is Spring MVC in disguise. Adding Spring web service client in Grails making life a bit easier.

Firstly, define Spring beans for web service in resources.xml.

[xml]
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans&quot; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quot;
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"&gt;
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"
/>
<bean id="manhattanJaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>org.paradise.schemas.productandservicefulfilment.financialservices.getinterestrate.v1.GetInterestRateRequestType</value>
<value>org.paradise.schemas.productandservicefulfilment.financialservices.getinterestrate.v1.GetInterestRateResponseType</value>
<value>org.paradise.schemas.productandservicefulfilment.financialservices.publishfinancialservicestransaction.v1.PublishFinancialServicesTransactionRequestType</value>
<value>org.paradise.schemas.productandservicefulfilment.financialservices.publishfinancialservicestransaction.v1.PublishFinancialServicesTransactionResponseType</value>
<value>org.paradise.schema.commondatamodel.common.v1.InterfaceHeader</value>
<value>org.paradise.schema.commondatamodel.common.v1.SOAPException</value>
</list>
</property>
</bean>
<bean id="manhattanMessageSender" class="org.springframework.ws.transport.http.CommonsHttpMessageSender">
<property name="connectionTimeout" value="${manhattan.webservice.connection.timeout:60000}"
/>
<property name="readTimeout" value="${manhattan.webservice.request.timeout:60000}"
/>
</bean>
<bean id="manhattanWebServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory" />
<property name="messageSender" ref="manhattanMessageSender" />
<property name="defaultUri" value="${soap.manhattan.defaultUri}" />
<property name="marshaller" ref="manhattanJaxb2Marshaller" />
<property name="unmarshaller" ref="manhattanJaxb2Marshaller" />
</bean>
</beans>
[/xml]

resources.xml is under $GRAILS_PROJECT_HOME/grails-app/conf/spring directory.

Java Objects GetInterestRateRequestType, GetInterestRateResponseType are objects generated from WSDL file by wsimport. There are two operations in defined service: GetInterestRate() and PublishFinancialServicesTransaction(). For example in GetInterestRateRequestType.java file:

[java]

import java.math.BigDecimal;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "getInterestRateRequestType", propOrder = {
"productGroup",
"product",
"productType",
"amount"
})
@XmlRootElement(name = "getInterestRate")
public class GetInterestRateRequestType {

@XmlElement(name = "ProductGroup", required = true)
protected String productGroup;
@XmlElement(name = "Product", required = true)
protected String product;
@XmlElement(name = "ProductType", required = true)
protected String productType;
@XmlElement(name = "Amount", required = true)
protected BigDecimal amount;

}
[/java]

  • All six “classesToBeBound” in resources.xml file should have @XmlRootElement annotation. Otherwise Exception thrown during run time.
  • ${soap.manhattan.defaultUri} defined in Config.groovy file, pointing to web service endpoint.
  • connectionTimeout defined in variable ${manhattan.webservice.connection.timeout:60000}, default is 60000 milli seconds.
  • readTimeout defined in variable ${manhattan.webservice.request.timeout:60000}, default is 60000 milli seconds.

To inject manhattanWebServiceTemplate into manhattanWebService, need initialise bean in resources.groovy file under $GRAILS_PROJECT_HOME/grails-app/conf/spring directory. In resources.groovy file:

[java]
import grails.util.*

// Place your Spring DSL code here
beans = {
switch(GrailsUtil.environment) {
case "production":
manhattanWebService(org.paradise.manhattan.integration.ManhattanWebService) {
manhattanWebServiceTemplate = ref("manhattanWebServiceTemplate")
}
break
case "test":
manhattanWebService(org.paradise.manhattan.integration.ManhattanWebService) { manhattanWebServiceTemplate = ref("manhattanWebServiceTemplate")
} break
case "functional":
manhattanWebService(FunctionalTestSupport.FakedManhattanWebService)
break
case "development":
manhattanWebService(org.paradise.manhattan.integration.ManhattanWebService) {
manhattanWebServiceTemplate = ref("manhattanWebServiceTemplate")
}
break
default:
manhattanWebService(org.paradise.manhattan.integration.ManhattanWebService) {
manhattanWebServiceTemplate = ref("manhattanWebServiceTemplate")
}
break
}
}
[/java]

Now the core part ManhattanWebService.groovy:

[java]
class ManhattanWebService {

static transactional = true

def manhattanWebServiceTemplate

public static final String GET_INTEREST_RATE_ACTION = "/Services/FinancialServices/getInterestRate_v1"
public static final String GET_INTEREST_RATE_OPERATION = "getInterestRate_v1"
public static final String PUBLISH_FINANCIAL_SERVICES_ACTION = "/Services/FinancialServices/publishFinancialServicesTransaction_v1"
public static final String PUBLISH_FINANCIAL_SERVICES_OPERATION = "publishFinancialServicesTransaction_v1"

public Object getSoapResponse(Object wsRequest, final String soapAction, final InterfaceHeader soapInterfaceMethod) {

// turn off gzip encoding so we can read the response when using tcpmon
// ((AbstractHttpWebServiceMessageSender) manhattanWebServiceTemplate.getMessageSenders()[0]).setAcceptGzipEncoding(false);

// let the manhattanWebServiceTemplate create the xml and add the soap header
Object wsResponse = manhattanWebServiceTemplate.marshalSendAndReceive(
wsRequest, new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) {
try {
SoapMessage soapMessage = (SoapMessage) message;
// add the required ‘SOAP Action’ to the message before sending
soapMessage.setSoapAction(soapAction);
// the soap header can be modified here if necessary
SoapHeader soapHeader = ((SoapMessage) message).getSoapHeader();
org.paradise.schema.commondatamodel.common.v1.ObjectFactory objectFactory = new org.paradise.schema.commondatamodel.common.v1.ObjectFactory();
JAXBElement<InterfaceHeader> element = objectFactory.createInterfaceHeader(soapInterfaceMethod);
StringResult stringResult = new StringResult();
manhattanWebServiceTemplate.getMarshaller().marshal(element, stringResult);
StringSource headerSource = new StringSource(stringResult.toString());
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(headerSource,soapHeader.getResult());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
return wsResponse;
}

def processException(SoapFaultClientException soapException) {

SoapFaultDetail soapFaultDetail = soapException.getSoapFault().getFaultDetail();
if (soapFaultDetail == null) {
throw soapException;
}

SoapFaultDetailElement soapFaultDetailElementChild = soapFaultDetail.getDetailEntries().next();
Source detailSource = soapFaultDetailElementChild.getSource();
try {
SOAPException detail = (SOAPException) manhattanWebServiceTemplate.getUnmarshaller().unmarshal(detailSource);
String exceptionCode = detail.getException().getExceptionCode();
String exceptionDesc = detail.getException().getExceptionDescription();
throw new ManhattanWebServiceException("Exception code: " + exceptionCode + " Exception description: " + exceptionDesc)
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}

def getInterestRate(GetInterestRateRequestType soapRequest) {
try {
InterfaceHeader interfaceHeader = PayloadGenerator.createInterfaceHeader(PayloadGenerator.INTERFACENAME_GETINTERESTRATE)
def soapResponse = getSoapResponse(soapRequest, GET_INTEREST_RATE_ACTION, interfaceHeader)
return soapResponse.interestRate
} catch (SoapFaultClientException sfce) {
processException(sfce)
}
}

}
[/java]

Calling web service in Grails:

[java]
def getInterestRate = {
GetInterestRateRequestType soapRequest= generateSoapRequest()
def interestRate = manhattanWebService.getInterestRate(soapRequest)

render text: interestRate, contentType: "text/plain", encoding: "UTF-8"
}

def generateSoapRequest() {
session["PRODUCT"] = new ProductDetails()
session["PRODUCT"].amount = ‘10000’

return generateGetInterestRateRequest(session["PRODUCT"])
}

def generateGetInterestRateRequest(ProductDetails productDetails) {
GetInterestRateRequestType soapRequest = new GetInterestRateRequestType()

soapRequest.setProductGroup(‘High Interest Account’)
soapRequest.setProduct(‘Cash Management’)
soapRequest.setProductType(‘Personal’)
soapRequest.setAmount(new BigDecimal(productDetails.amount))

return soapRequest
}
[/java]

In PayloadGenerator.groovy file:

[java]
static def createInterfaceHeader(String interfaceName) {
InterfaceHeader interfaceHeader = new InterfaceHeader()

interfaceHeader.setInterfaceName(interfaceName)
interfaceHeader.setInterfaceVersion(INTERFACE_VERSION)
interfaceHeader.setMessageType(REQUEST_MESSAGE_TYPE)
interfaceHeader.setBusinessReferenceID("Business Reference ID")
interfaceHeader.setSourceSystemID("POS")
// Optional in Request Header
interfaceHeader.setSourceInformation("SiteId CounterId")
interfaceHeader.setTimestamp(DateUtil.getXMLGeorgianCalendar(new GregorianCalendar()))

return interfaceHeader;
}
[/java]

The HTTP Header in SOAP request is:

OST /mockFinancialServices HTTP/1.1
Accept-Encoding: gzip
Accept: text/xml, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
SOAPAction: "/Services/FinancialServices/getInterestRate_v1"
Content-Type: text/xml; charset=utf-8
Content-Length: 1938
User-Agent: Jakarta Commons-HttpClient/3.1
Host: 127.0.0.1:8000

Pay attention to the SOAPAction in the HTTP Header.

The SOAP request sent:

[xml]
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"&gt;
<SOAP-ENV:Header>
<ns4:InterfaceHeader xmlns:ns4="http://www.paradise.org/Schema/CommonDataModel/Common:v1&quot;
xmlns="http://www.paradise.org/schemas/ProductandServicefulfilment/FinancialServices/getInterestRate:v1&quot;
xmlns:ns2="http://www.paradise.org/Schema/CommonMessageModel/RetailTransaction:v1&quot;
xmlns:ns3="http://www.paradise.org/Schema/CommonDataModel/InvolvedParty:v1&quot;
xmlns:ns5="http://www.paradise.org/Schema/CommonDataModel/Location:v1&quot;
xmlns:ns6="http://www.paradise.org/schemas/ProductandServicefulfilment/FinancialServices/publishFinancialServicesTransaction:v1"&gt;
<ns4:InterfaceName>GetInterestRate</ns4:InterfaceName>
<ns4:InterfaceVersion>1.0</ns4:InterfaceVersion>
<ns4:MessageType>Request</ns4:MessageType>
<ns4:BusinessReferenceID>Business Reference ID</ns4:BusinessReferenceID>
<ns4:SourceSystemID>POS</ns4:SourceSystemID>
<ns4:SourceInformation>SiteId CounterId</ns4:SourceInformation>
<ns4:Timestamp>2012-07-08T15:21:17.877+10:00</ns4:Timestamp>
</ns4:InterfaceHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns2:getInterestRate xmlns:ns2="http://www.paradise.org/schemas/ProductandServicefulfilment/FinancialServices/getInterestRate:v1&quot;
xmlns:ns3="http://www.paradise.org/Schema/CommonMessageModel/RetailTransaction:v1&quot;
xmlns:ns4="http://www.paradise.org/Schema/CommonDataModel/InvolvedParty:v1&quot;
xmlns:ns5="http://www.paradise.org/Schema/CommonDataModel/Common:v1&quot;
xmlns:ns6="http://www.paradise.org/Schema/CommonDataModel/Location:v1&quot;
xmlns:ns7="http://www.paradise.org/schemas/ProductandServicefulfilment/FinancialServices/publishFinancialServicesTransaction:v1"&gt;
<ns2:ProductGroup>High Interest Rate Account</ns2:ProductGroup>
<ns2:Product>Cash Management</ns2:Product>
<ns2:ProductType>Personal</ns2:ProductType>
<ns2:Amount>10000</ns2:Amount>
</ns2:getInterestRate>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
[/xml]

Git and IntelliJ for impatient and not-very-happy developers

If you want to be a good developer, you should try IntelliJ and switch to Git.

Having played with Spring Tools Suite, an Eclipse based IDE for more than six weeks. How do I feel? I hate it. Buggy and inconsistent. Unexpected behaviour should easily waste a few hours to figure out and workaround. Eclipse is no better. A fat blow-ware, confliction between different plugins …

IntelliJ has a lot goodies, bit by bit, piece by piece. Saving a few keystroke are not big deal to carry one task, but to do it thousand times, even one keystroke less can save your life.

+Dean Budd Deano has barracked for IntelliJ long time ago. You have to really try it by yourself, feel it by yourself, understand it by yourself, then convince you how good it is. IntelliJ will become my major development platform for JEE and Grails project.

Git, how many time I have to remind you how quick and lighting fast it is?

Setting up a new Git repository on my MacBook Pro tonight, 15MB big source codes initialised, pulled, pushed out and into repository. Everything just bang, bang, bang. Even Perforce never impress me so much.

95% time you only need run git on command line. The other 5% time I switch to SmartGit on Mac is to have a look graphic branch history.

Don’t be a loser. Good tools help and accelerate your work, and leave a smile on your face after job done …

Keep walking around WebLogic server and JRockit traps

Happily developed web based application by using Groovy and Grails, and it runs happily as well on Tomcat. Then deploy to WebLogic server in staging environment which runs in JRockit JVM in RedHat Linux (better for performance), bugged.

Curiously try to duplicate the issue, I set up the exactly same version WebLogic server for Windows, but using standard JDK 1.6.x, same war file deployed, but application runs without any problem.

Always admire Oracle’s product quality and highly calibre service, I never bother to call them – help yourself, I can see Oracle sales smiling to me.

The first issue on WebLogic in JRockit is Exception thrown when try to start up the application:

Caused By: java.lang.IllegalArgumentException: com.sun.xml.internal.messaging.saaj.soap.LocalStrings != com.sun.xml.messaging.saaj.soap.LocalStrings
        at java.util.logging.Logger.getLogger(Logger.java:337)
        at com.sun.xml.messaging.saaj.soap.SAAJMetaFactoryImpl.(SAAJMetaFactoryImpl.java:71)
        at java.lang.Class.newInstance0(Class.java:355)
        at java.lang.Class.newInstance(Class.java:308)
        at javax.xml.soap.FactoryFinder.newInstance(FactoryFinder.java:59)

Did some search and found out this link on developers favourited StackOverflow – http://stackoverflow.com/questions/3889087/illegalargumentexception-com-sun-xml-internal-messaging-saaj-soap-localstrings

It seems JRockit has its own implementation of SOAP SAAJ which is not compatible, even in saaj-impl-1.3.2.jar file already specifies to use implementation inside jar file, rather then jar bundled with JVM:

    64 META-INF/services/javax.xml.soap.MessageFactory
    51 META-INF/services/javax.xml.soap.MetaFactory
    63 META-INF/services/javax.xml.soap.SOAPConnectionFactory
    57 META-INF/services/javax.xml.soap.SOAPFactory

The content inside these files are:

$ cat javax.xml.soap.MessageFactory
com.sun.xml.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl

$ cat javax.xml.soap.MetaFactory
com.sun.xml.messaging.saaj.soap.SAAJMetaFactoryImpl

$ cat javax.xml.soap.SOAPConnectionFactory
com.sun.xml.messaging.saaj.client.p2p.HttpSOAPConnectionFactory

$ cat javax.xml.soap.SOAPFactory
com.sun.xml.messaging.saaj.soap.ver1_1.SOAPFactory1_1Impl

To fix or walk around this issue, I have to ask application to use JVM’s SOAP SAAJ implementation. This is easily to do in Grails BuildConfig.groovy file:

[groovy]
grails.war.resources = { stagingDir, args ->
println "Deleting non-dependent jars development WebLogic Server …"
delete(file: "${stagingDir}/WEB-INF/lib/saaj-impl-1.3.2.jar")

}
[/groovy]

Fix the first problem, then comes the second during runtime:

java.lang.NoSuchFieldError: org/springframework/util/ReflectionUtils.USER_DECLARED_METHODSLorg/springframework/util/ReflectionUtils$MethodFilter;
        at org.springframework.web.bind.annotation.support.HandlerMethodResolver.init(HandlerMethodResolver.java:101)
        at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodResolver.(AnnotationMethodHandlerAdapter.java:504)
        at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodResolver.(AnnotationMethodHandlerAdapter.java:503)
        at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.getMethodResolver(AnnotationMethodHandlerAdapter.java:445)
        at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.supports(AnnotationMethodHandlerAdapter.java:387)

So after reading the source codes ReflectionUtils.java, it seems there is a confliction related to spring-core package.

Filter out all spring jar files from war file:

    60835  WEB-INF/lib/grails-spring-1.3.5.jar
   320926  WEB-INF/lib/org.springframework.aop-3.0.3.RELEASE.jar
    53082  WEB-INF/lib/org.springframework.asm-3.0.3.RELEASE.jar
    30193  WEB-INF/lib/org.springframework.aspects-3.0.3.RELEASE.jar
   553901  WEB-INF/lib/org.springframework.beans-3.0.3.RELEASE.jar
   662191  WEB-INF/lib/org.springframework.context-3.0.3.RELEASE.jar
   100368  WEB-INF/lib/org.springframework.context.support-3.0.3.RELEASE.jar
   365217  WEB-INF/lib/org.springframework.core-3.0.3.RELEASE.jar
   160146  WEB-INF/lib/org.springframework.expression-3.0.3.RELEASE.jar
     1808  WEB-INF/lib/org.springframework.instrument-3.0.3.RELEASE.jar
   379691  WEB-INF/lib/org.springframework.jdbc-3.0.3.RELEASE.jar
   184665  WEB-INF/lib/org.springframework.jms-3.0.3.RELEASE.jar
   333500  WEB-INF/lib/org.springframework.orm-3.0.3.RELEASE.jar
    60838  WEB-INF/lib/org.springframework.oxm-3.0.3.RELEASE.jar
   231345  WEB-INF/lib/org.springframework.transaction-3.0.3.RELEASE.jar
   389117  WEB-INF/lib/org.springframework.web-3.0.3.RELEASE.jar
   404158  WEB-INF/lib/org.springframework.web.servlet-3.0.3.RELEASE.jar
   321190  WEB-INF/lib/spring-aop-3.0.5.RELEASE.jar
    53082  WEB-INF/lib/spring-asm-3.0.5.RELEASE.jar
   555410  WEB-INF/lib/spring-beans-3.0.5.RELEASE.jar
   668861  WEB-INF/lib/spring-context-3.0.5.RELEASE.jar
   100870  WEB-INF/lib/spring-context-support-3.0.5.RELEASE.jar
   382442  WEB-INF/lib/spring-core-3.0.5.RELEASE.jar
   169752  WEB-INF/lib/spring-expression-3.0.5.RELEASE.jar
    61379  WEB-INF/lib/spring-oxm-3.0.5.RELEASE.jar
   395587  WEB-INF/lib/spring-web-3.0.5.RELEASE.jar
   418977  WEB-INF/lib/spring-webmvc-3.0.5.RELEASE.jar
   487211  WEB-INF/lib/spring-ws-core-2.0.0.RELEASE.jar
    90472  WEB-INF/lib/spring-xml-2.0.0.RELEASE.jar

There are two versions Spring in the war file!

org.springframework.*-3.0.3.*.jar are dependencies of Grails version 1.3.5; spring-*-3.0.5.*.jar are the dependencies of spring-ws-core-2.0.0*.jar.

So add more to Grails BuildConfig.groovy file to delete the conflicted jar files:

[groovy]
delete(file: "${stagingDir}/WEB-INF/lib/spring-aop-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-asm-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-beans-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-context-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-context-support-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-core-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-expression-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-oxm-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-web-3.0.5.RELEASE.jar")
[/groovy]
Lesson learned is JRockit maybe has a different ClassLoader strategy to standard JDK release.