Deploying Grails® 3.2 to JBoss 7.1 EAP
November 10, 2016
Tags: #deployment
Following on from our post describing how to deploy Grails® 3 to JBoss 6.4 EAP, I recently undertook a new adventure to deploy Grails 3.2.3 to JBoss 7.1 EAP.
Like every new version of JBoss there are dependency challenges and tweaks to overcome in order to get to the point where you can deploy an application.
Configuring Gradle
After you have created a Grails application using the grails create-app
command the first thing that needs to be done is to modify the dependencies to versions compatible with JBoss 7.1.
JBoss 7.1 is based on Servlet 3.0 and earlier versions of the Bean Validation API. This means that you need to use Tomcat 7, which is based on Servlet 3.0, which you can do by specifying the Tomcat version in build.gradle
:
ext['tomcat.version'] = '7.0.72'
The above uses Spring Boot's ability to override the default versions of the org.springframework.boot:spring-boot-starter-tomcat
dependency. Speaking of the spring-boot-starter-tomcat
dependency, you need to make it provided:
provided "org.springframework.boot:spring-boot-starter-tomcat"
Since the classes within the Tomcat JARs are already provided by Tomcat these should not be included in the WAR. Next you should also specify the Servlet 3.0 API as provided:
provided "javax.servlet:javax.servlet-api:3.0.1"
This does two things, firstly it ensures the Servlet API dependencies are not in the WAR and secondly it forces the Grails framework to use Servlet 3.0 instead of the default 3.1.
The next challenge is the Bean Validation API, in earlier versions of JBoss it was possible to exclude the validation module and ship with a custom version of the Bean Validation API, this no longer seems to be possible with JBoss 7 so you have to downgrade the version of Hibernate Validator used by the Grails framework:
compile "org.hibernate:hibernate-validator:4.3.2.Final"
The JAXB dependency also seems be a required dependency of JBoss 7 so you have to add that:
runtime 'javax.xml.bind:jaxb-api:2.2.12'
Finally, generally with JBoss 7 you should deploy with a JNDI datasource so you should make the database driver dependency you are using provided
scope. For example for MySQL:
provided "mysql:mysql-connector-java"
JBoss Deployment Descriptors
The next step to getting JBoss configured correctly is to provide a src/main/webapp/WEB-INF/jboss-deployment-structure.xml
file, the contents of which should look like the following:
<?xml version='1.0' encoding='UTF-8'?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.1">
<deployment>
<exclusions>
<module name="org.jboss.logging" />
<module name="org.hibernate" />
<module name="org.hibernate.validator" />
</exclusions>
</deployment>
</jboss-deployment-structure>
The reason for this is to isolate and exclude dependencies you don't want to inherit from JBoss 7. I also recommend disabling Spring Boot re-packaging of the WAR file if you don't plan to run as a standalone WAR. You can do that by adding the following to build.gradle
:
bootRepackage.enabled = false
JNDI DataSource Configuration
To configure a JNDI datasource you should first define the production data source as using JNDI:
environments:
development:
dataSource:
dbCreate: create-drop
url: jdbc:h2:mem:devDb
test:
dataSource:
dbCreate: update
url: jdbc:h2:mem:testDb
production:
dataSource:
jndiName: "java:jboss/datasources/MyDataSource"
dbCreate: none
Note that you typically do not want the Grails tooling to create the schema for you so we set dbCreate
to none
. You can then run grails schema-export
to generate a build/ddl.sql
file that you can use to create the database in your production environment.
On the JBoss side if you are using standalone deployment, you need to modify the standalone/configuration/standalone.xml
file to configure your JNDI datasource. For example for MySQL adding the following section within the <datasources>
block will do:
<datasource jndi-name="java:jboss/datasources/MyDataSource" pool-name="MyDataSource">
<connection-url>jdbc:mysql://localhost:3306/my-database</connection-url>
<driver>com.mysql</driver>
<transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
<pool>
<min-pool-size>10</min-pool-size>
<max-pool-size>100</max-pool-size>
<prefill>true</prefill>
</pool>
<security>
<user-name>root</user-name>
</security>
<statement>
<prepared-statement-cache-size>32</prepared-statement-cache-size>
<share-prepared-statements>true</share-prepared-statements>
</statement>
</datasource>
Deploying the Application
Once you have prepared your application for deployment to deploy the app you can run gradle assemble
to build a WAR file and then copy the generated WAR file located in build/libs
to JBoss 7's standalone/deployments
directory and then run bin/standalone.sh
.
Note that if you receive an exception that looks like the following:
16:45:35,789 WARN [org.jboss.as.ee] (MSC service thread 1-6) JBAS011006: Not installing optional component org.springframework.web.context.request.async.StandardServletAsyncWebRequest due to exception: org.jboss.as.server.deployment.DeploymentUnitProcessingException: JBAS011054: Could not find default constructor for class org.springframework.web.context.request.async.StandardServletAsyncWebRequest
at org.jboss.as.ee.component.ComponentDescription$DefaultComponentConfigurator.configure(ComponentDescription.java:606)
This is apparently normal behaviour and can be safely ignored.
This Is WAY Too Hard
I agree. Deploying to "enterprise" containers is not fun. It can, however, be a requirement where you work. In order to simplify these steps I have created a JBoss 7 profile for Grails apps that tries to encapsulate all these changes. Simply run:
grails create-app myapp --profile org.grails.profiles:web-jboss7:1.0.0.RC1
This will create an application in the myapp
directory with all the changes necessary apart from preparing the JNDI datasource. Hopefully the profile encourages more JBoss 7 folks to give the Grails framework a try. Happy deploying!