Deploying Grails 3.2 to JBoss 7.1 EAP
November 10, 2016
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.
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
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:
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:
This does two things, firstly it ensures the Servlet API dependencies are not in the WAR and secondly it forces Grails 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 Grails:
The JAXB dependency also seems be a required dependency of JBoss 7 so you have to add that:
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:
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
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 Grails to create the schema for you so we set
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
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 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 Grails a try. Happy deploying!