[How-to] Deploy Spring Boot 2.x apps on WebSphere 8.5.5

James Tran
4 min readApr 8, 2019

--

Goal:

Deploy a Spring Boot application which was implemented using Spring Boot 2.x on WebSphere Application Server 8.5.5 with NO changes to server-level configurations.

Challenges:

  1. Spring Data JPA 2.x with Hibernate requires JPA 2.1 which is not supported by WAS 8.5.5.
  2. We may not have sufficient admin privileges to create a shared library nor to modify server-level configurations such as the “Default Java Persistence API settings” as suggested by this StackOverflow answer.
  3. Even if we have sufficient privileges, doing so would affect all applications deployed on the same server. This is a risk you may not want to take.
  4. Our Spring Boot app should be able to evolve to new version independently, without being held back by WAS outdated libraries.

Tech stack:

  1. Spring Boot 2.1.9
  2. IBM WebSphere Application Server 8.5.5.15
  3. Maven

Steps:

1. Modify pom.xml to generate a WAR instead of a JAR

  • Update packaging policy
<packaging>war</packaging>
  • Exclude the embedded tomcat by marking its scope as provided
<dependency> 
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>

2. Modify Application.java to start the application in a stand-alone servlet container.

Let the server know how to start your app

The above changes are enough if you want to start your application in a stand-alone Tomcat server. Just check Spring Boot documentation for the minimum Tomcat version required for the Spring Boot version you’re using at the time of deployment.

3. Modify application-level class loader order to prioritize the JAR libraries in your WAR instead of using the shared libraries on the server.

By default, WAS class loader will search for a dependency from the parent node (i.e. shared libraries) first before looking into the child itself. This behavior will cause the notorious exception below, which occurs when Hibernate invokes the wrong class from the shared JPA 2.0 library on WAS 8.5.5.

java.lang.NoSuchMethodError: javax/persistence/Table.indexes()

Instead, what the server should do is to get the JPA 2.1 classes from the JAR libraries in our own WAR. To override this behavior, simply update the following setting.

  • In the administrative console, click Applications > Application Types > WebSphere enterprise applications > application_name > Manage modules > webmodule_name.
  • Select Classes loaded with local class loader first (parent last) from the drop down list.
  • Click OK, and then Save to save your changes.

In case you still run into the issue after making the changes, search your project for a @Bean declaration for theLocalContainerEntityManagerFactoryBean class. Create one if you don’t have it yet and make sure to set the JPA vendor adapter as following.

Declare a custom EntityManagerFactory

The secret sauce behind this bean is the HibernateJpaVendorAdapter class which helps you declare SpringHibernateJpaPersistenceProvider as the chosen JPA 2.1 persistence provider standing behind all of your database-related logic.

If you’re interested in learning more about the LocalContainerEntityManagerFactoryBean, you can take a look at the articles below.

4. At this point, if you try to deploy and start your application on WAS, you may run into another exception.

javax.persistence.PersistenceException: [PersistenceUnit: centralPersistenceUnit] Unable to build Hibernate SessionFactory;
Caused by: org.hibernate.cfg.beanvalidation.IntegrationException: Error activating Bean Validation integration
Caused by: java.lang.NoClassDefFoundError: javax/el/ELManager

This happens because we excluded the dependency for Tomcat earlier. It turns out that this dependency provides more than just the embedded servlet container. To bring back the EL-related classes, we need to include the following dependency in our pom.xml.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

In case you’re curious, the above artifact brings along the following dependencies.

<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<scope>compile</scope>
</dependency>

5. If you’re using Spring Security in your project, you may run into yet another exception upon starting your application.

Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception
Caused by: javax.xml.bind.JAXBException: ClassCastException: attempting to cast jar:file:/C:/Development/IBM/WebSphere/AppServer/endorsed_apis/jaxb-api.jar!/javax/xml/bind/JAXBContext.class to wsjar:file:/C:/Development/IBM/WebSphere/AppServer/profiles/AppSrv01/installedApps/JAMESNode02Cell/spring_war.ear/spring.war/WEB-INF/lib/jaxb-api-2.3.1.jar!/javax/xml/bind/JAXBContext.class. Please make sure that you are specifying the proper ClassLoader.

This happens because WAS carries an “optimized” JAXB implementation and there was a conflict when WAS tried to cast its own JAXBContext into the one coming from our JAR library. To overcome this problem, you have 2 options:

  • If you’d like to use the “optimized” JAXB implementation from IBM, you can simply exclude the JAXB dependency from your pom.xml. This change lets your application use the shared library provided by WAS.
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<scope>provided</scope>
</dependency>
  • In case you prefer to have your application evolve independently of WAS shared library, you can instruct your application to use the ContextFactory from our packaged WAR instead with one single line of code in Application.java.

6. Give it a try and give me a pat on the back!

How I created my WAR:

To create the WAR artifact containing all of my Rest endpoints as well as React front-end code, I used the following POM.xml:

Final thoughts:

I managed to deploy a very complex project using Spring Boot 2.1.9 and Spring Cloud Greenwich.SR3 to WAS 8.5.5 using the steps above. I have no doubt it’d work for you too.

Even though IBM claims it’s using an “optimized” JAXB implementation, one discussion on the developer forum claims otherwise. Perhaps, you should take a look at the discussion before you decide to use their implementation.

Cheers!

--

--