Running Spring Boot JPA apps in WebSphere 8.5.5

Follow

Spring Boot is a wonderful tool for creating rich powerful applications with a limited amount of code or complexity.  It can optionally include Spring Data with JPA support. The Spring abstraction layer for JPA makes creating database independent applications a snap.  The problem is that it uses JPA 2.1 features which WebSphere 8.5.5 does not support. 

This article covers how to

  1. Downgrade Spring Data to use JPA 2.0 without losing functionality
  2. Structure the webapp so that it can be deployed to WebSphere

Assumptions

  1. You have a working knowledge of Spring Boot and Spring Data. If you do not take a look at Accessing Data with JPA
  2. You are using Gradle to build your app. If you are not adapting to Maven should not be difficult. If you aren't using Gradle, try it out, it's amazing.
  3. You have a working Spring Boot JPA app and need to convert it so that it works with WebSphere

Tested Platform

  • Windows Server 2012 R2
  • Websphere 8.5.5.7
  • IBM WebSphere Application Server - Express 7.1.3.10 (The IBM version of Java 7)
  • Oracle 11g

Steps

  1. Modify your build.gradle to downgrade JPA
  2. Change how Spring initializes JPA
  3. Modify Application.properties
  4. Deploy to WebSphere

Modifying build.gradle

You need to downgrade the Hibernate that comes with Spring Boot Data. This is as simple as adding an exclude to the spring boot data jpa dependencies block and then adding in the hibernate versions. The hibernate versions shown below were as of this writing the last ones to support JPA 2.0.

Note the providedRuntime below, this is so that Spring Boot doesn't include the embedded tomcat. 

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.boot:spring-boot-starter-data-jpa") {
        exclude group: 'org.hibernate'
    }
    compile('org.hibernate:hibernate-core:4.2.20.Final')
    compile('org.hibernate:hibernate-entitymanager:4.2.20.Final')
    providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
    testCompile("junit:junit")
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

It is also useful to prevent bootRepackage from processing the war file. I really don't know why Spring Boot does this by default as when it processes the war file you get all the extra stuff in the war that a jar file needs to autoexecute as a single jar.

bootRepackage {
    withJarTask = jar
}

Change how Spring initializes JPA

Spring Data handles most of the heavy lifting to enable JPA. We will need to bootstrap some of JPA ourselves since Spring Data can no longer do it for us. The good news is that all the other features of Spring Data such as the CrudRepository interface still work.

Here is my configuration class for JPA. You will obviously need to modify it slightly to your usecase.

@Configuration
public class JpaConfig {

    private static final Logger logger = LoggerFactory.getLogger(JpaConfig.class);

    @Autowired
    private AppProperties appProperties;

    @Autowired
    private javax.sql.DataSource dataSource;

    @Bean
    LocalSessionFactoryBean sessionFactory() throws IOException {
        LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
        factoryBean.setHibernateProperties(getHibernateProperties());
        factoryBean.setDataSource(dataSource);
        factoryBean.setPackagesToScan("your.entity.classes.package");
        return factoryBean;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    private Properties getHibernateProperties() {
        Properties properties = new Properties();
            // This is to get around a Websphere Bug
        properties.put("hibernate.dialect", Oracle10gDialect.class.getName());
        properties.put("hibernate.show_sql", "false");
        return properties;
    }
}

Application.properties changes for Oracle

It looks like WebSphere has a bug or at least a poorly written piece of code around its' wrapped version of the DatabaseMetaData class that comes from it's wrapped Connection Object. When Hibernate asks the Oracle connection what features it supports, WebSphere throws a SQLException for each unsupported operation instead of just returning false. This meant that Hibernate had no way to figure out how to setup database support.  If you add the following to application.properties it will resolve it. It tells Hibernate NOT to use the connection to get its database support information but instead to use the database dialect you gave it. I did not test this with other databases so it may be an Oracle only problem.

spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false

Deploy to WebSphere

When you deploy the webapp you need to make sure the following two things are set on the webapp:

  1. Change the Class loader order to: Classes loaded with local class loader first (parent last)
  2. Make sure War Class loader policy is set to: Class loader for each WAR file in application
Have more questions? Submit a request

Comments

  • Avatar
    Rodrigo Ribeiro

    I'm following your guide, but my .war still has some tomcat dependencies like tomcat-juli-7.0.59.jar and tomcat-jdbc-7.0.59.jar because they are transitive dependencies of spring-boot-starter-data-jpa

    Did you exclude them as well? Changed to runtime provided?

    When I try to remove them I'm getting Caused by: java.lang.ClassNotFoundException: org.postgresql.Driver

    It's weird because I have the dependency to the PostgreSQL driver already added.

  • Avatar
    David Parish

    The tomcat dependencies shouldn't have an affect on the postgres driver so that's a weird one.  If you are using gradle run:

    gradle dependencies

    That will show you where and how dependencies are being pulled in.

    As for the tomcat deps, here is how I'm declaring that in build.gradle:

    providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
  • Avatar
    Zachary Lee

    David,

    First off, thank you for sharing your experience!

    Can you please clarify if you had to package your WAR file as suggested here: Create a deployable war file, in addition to making tomcat "provided". I followed the suggestion there and was able to deploy my WAR file to Tomcat and WebSphere. It runs perfect with Tomcat with no problem BUT it does not work with WebSphere (Got "Error 404: SRVE0190E: File not found: {0}" when trying to access a page (any page) ). 

    About my environment: WebSphere Server 8.5.5.0 with either Java 1.7 or 1.6. My Spring Boot project does not use JPA.

    Any comment/help would be greatly appreciated. 

    Regards.

    Zach

  • Avatar
    David Parish

    >>Got "Error 404: SRVE0190E: File not found: {0}" when trying to access a page (any page) 

    That's probably not a spring boot problem at all.  Take a look at your SystemOut.log file and see if there is an error when the webapp starts.

  • Avatar
    Rodrigo Ribeiro

    This is a pom.xml file for enabling spring-boot to run on WAS 8.5.5.5 and jdk1.6

    https://gist.github.com/rodrik/5eae35432d8ab49d91ad

    I could successfully deploy and run it.

     

    Thanks David for the guide.

  • Avatar
    David Parish

    Rodrigo,

    Thanks for contributing. That's very helpful.

  • Avatar
    Swapnil Khandelwal

    I follow all above steps but got error Error 404: SRVE0190E: File not found: {0}

    i use db2 database and created simple rest api using spring boot my server log is as bellow

    [13/07/16 12:19:49:766 IST] 00000001 SystemErr R java.io.FileNotFoundException: D:\user\local\was\was85\profiles\AppSrv01\temp\swapnil\server1\_extensionregistry\loader.xml (The system cannot find the file specified.)
    [13/07/16 12:19:49:766 IST] 00000001 SystemErr R at java.io.FileInputStream.<init>(FileInputStream.java:124)
    [13/07/16 12:19:49:766 IST] 00000001 SystemErr R at java.io.FileInputStream.<init>(FileInputStream.java:84)
    [13/07/16 12:19:49:766 IST] 00000001 SystemErr R at com.ibm.wkplc.extensionregistry.RegistryLoader.restore(RegistryLoader.java:120)
    [13/07/16 12:19:49:766 IST] 00000001 SystemErr R at com.ibm.wkplc.extensionregistry.ExtensionRegistry.<init>(ExtensionRegistry.java:62)
    [13/07/16 12:19:49:766 IST] 00000001 SystemErr R at com.ibm.wkplc.extensionregistry.ExtensionRegistryFactory.<clinit>(ExtensionRegistryFactory.java:47)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at java.lang.J9VMInternals.initializeImpl(Native Method)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at java.lang.J9VMInternals.initialize(J9VMInternals.java:236)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.wkplc.extensionregistry.wasservice.ExtensionRegistryService.start(ExtensionRegistryService.java:250)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.ws.runtime.component.ContainerHelper.startComponents(ContainerHelper.java:539)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.ws.runtime.component.ContainerImpl.startComponents(ContainerImpl.java:627)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.ws.runtime.component.ContainerImpl.start(ContainerImpl.java:618)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.ws.runtime.component.ApplicationServerImpl.start(ApplicationServerImpl.java:252)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.ws.runtime.component.ContainerHelper.startComponents(ContainerHelper.java:539)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.ws.runtime.component.ContainerImpl.startComponents(ContainerImpl.java:627)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.ws.runtime.component.ContainerImpl.start(ContainerImpl.java:618)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.ws.runtime.component.ServerImpl.start(ServerImpl.java:523)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.ws.runtime.WsServerImpl.bootServerContainer(WsServerImpl.java:310)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.ws.runtime.WsServerImpl.start(WsServerImpl.java:223)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.ws.runtime.WsServerImpl.main(WsServerImpl.java:686)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.ws.runtime.WsServer.main(WsServer.java:59)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:60)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at java.lang.reflect.Method.invoke(Method.java:611)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.wsspi.bootstrap.WSLauncher.launchMain(WSLauncher.java:234)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.wsspi.bootstrap.WSLauncher.main(WSLauncher.java:96)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.wsspi.bootstrap.WSLauncher.run(WSLauncher.java:77)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:60)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at java.lang.reflect.Method.invoke(Method.java:611)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at org.eclipse.equinox.internal.app.EclipseAppContainer.callMethodWithException(EclipseAppContainer.java:587)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:198)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:369)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:60)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at java.lang.reflect.Method.invoke(Method.java:611)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at org.eclipse.core.launcher.Main.invokeFramework(Main.java:340)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at org.eclipse.core.launcher.Main.basicRun(Main.java:282)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at org.eclipse.core.launcher.Main.run(Main.java:981)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.wsspi.bootstrap.WSPreLauncher.launchEclipse(WSPreLauncher.java:379)
    [13/07/16 12:19:49:782 IST] 00000001 SystemErr R at com.ibm.wsspi.bootstrap.WSPreLauncher.main(WSPreLauncher.java:150)

Powered by Zendesk