Search code examples
javahibernatejpatomcatwebsphere

ClassPathXmlApplicationContext: Error creating bean with name 'X'; nested exception is java.lang.NoSuchMethodError: javax.persistence.Table.indexes()


I'm currently moving a legacy websphere application/service to run on tomcat instead. This all work fine for most of what the service need to do except for a particular call that need to access a sqlServer db.

When trying to load the context it give the following error:

ClassPathXmlApplicationContext           : Exception encountered during context initialization - cancelling refresh attempt: 
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [SpringConfigDataAccess.xml]: 
Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: javax.persistence.Table.indexes()[Ljavax/persistence/Index; 

However, there seem to be a conflict with the use jpa 2.1 I suspect it might be one of the WAS lib that is added to make the app work on tomcat or the WAS specific class in the db config.

Here some general info and dependencies tree:

WAS was using ibmjdk for java 1.8

Tomcat is using openJDk for java 1.8

Previous config

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">         
    <property name="jndiName" value="jdbc/dbName"/>         
    <property name="lookupOnStartup" value="false"/>         
    <property name="cache" value="true" />         
    <property name="proxyInterface"  value="javax.sql.DataSource" />     
</bean> 

new config

    <bean id="dataSource" destroy-method="close"
          class="org.apache.commons.dbcp2.BasicDataSource" scope="prototype">
        <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
        <property name="username" value="username" />
        <property name="password" value="pass" />
        <property name="url" value="server_url"/>
    </bean>

This changed required the need to add commons-dbcp2 in the maven project dependencies

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
            <version>2.10.0</version>
        </dependency>

The session factory config for that db is:

    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="annotatedClasses">
            <list>
            ...
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
                <prop key="hibernate.cache.use_second_level_cache">false</prop>
                <prop key="hibernate.max_fetch_depth">6</prop>
                <prop key="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</prop>

                <!--           IBM WAS SPECIFIC             -->          
                  <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.CMTTransactionFactory</prop>
                  <prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.WebSphereJTATransactionLookup</prop>
                  <prop key="hibernate.transaction.jta.platform">org.hibernate.service.jta.platform.internal.WebSphereJtaPlatform</prop>                  
                <!--           /END IBM WAS SPECIFIC        -->
                
                <!-- added in order for a JUnit test to load properly -->
                <prop key="javax.persistence.validation.mode">none</prop>
            </props>
        </property> 
        
<!--          Previous WAS SPECIFIC transaction manager             -->  
<!--    <bean id="transactionManager" class="org.springframework.transaction.jta.WebSphereUowTransactionManager"/>-->
    <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"/>      

Finally the dependency tree

 +- com.project.app:main-project-compose:jar:1.0.0-SNAPSHOT:compile
 |  +- com.project-common.data-access:data-access:jar:5.0.0.2:compile
 |  |  +- org.hibernate.javax.persistence:hibernate-jpa-2.1-api:jar:1.0.0.Final:compile
 |  |  \- com.microsoft:sqlserver.jdbc:jar:4.0:runtime
 |  +- org.hibernate:hibernate-core:jar:5.6.4.Final:compile
 |  |  +- org.jboss.logging:jboss-logging:jar:3.4.3.Final:compile
 |  |  +- net.bytebuddy:byte-buddy:jar:1.12.7:compile
 |  |  +- antlr:antlr:jar:2.7.7:compile
 |  |  +- org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:jar:1.1.1.Final:compile
 |  |  +- org.jboss:jandex:jar:2.4.2.Final:compile
 |  |  +- com.fasterxml:classmate:jar:1.5.1:compile
 |  |  +- javax.activation:javax.activation-api:jar:1.2.0:compile
 |  |  +- org.hibernate.common:hibernate-commons-annotations:jar:5.1.2.Final:compile
 |  |  +- javax.xml.bind:jaxb-api:jar:2.3.1:compile
 |  |  \- org.glassfish.jaxb:jaxb-runtime:jar:2.3.1:compile
 |  |     +- org.glassfish.jaxb:txw2:jar:2.3.1:compile
 |  |     +- com.sun.istack:istack-commons-runtime:jar:3.0.7:compile
 |  |     +- org.jvnet.staxex:stax-ex:jar:1.8:compile
 |  |     \- com.sun.xml.fastinfoset:FastInfoset:jar:1.2.15:compile
 |  \- org.apache.commons:commons-collections4:jar:4.1:compile
 +- org.springframework:spring-context:jar:5.3.13:compile
 |  +- org.springframework:spring-aop:jar:5.3.13:compile
 |  +- org.springframework:spring-beans:jar:5.3.13:compile
 |  +- org.springframework:spring-core:jar:5.3.13:compile
 |  |  \- org.springframework:spring-jcl:jar:5.3.13:compile
 |  \- org.springframework:spring-expression:jar:5.3.13:compile
 +- org.springframework:spring-orm:jar:5.3.13:compile
 |  +- org.springframework:spring-jdbc:jar:5.3.13:compile
 |  \- org.springframework:spring-tx:jar:5.3.13:compile
 +- junit:junit:jar:4.11:test
 |  \- org.hamcrest:hamcrest-core:jar:1.3:test
 +- xerces:xercesImpl:jar:2.12.2:compile
 |  \- xml-apis:xml-apis:jar:1.4.01:compile
 +- com.commons:websphere-mq-dependencies:pom:7.0.1.5.0:test
 |  +- com.ibm.websphere:com.ibm.mq:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.mqjms:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.mq.jmqi:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.mq.jmqi.local:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.mq.jmqi.remote:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.mq.jmqi.system:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.msg.client.commonservices:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.msg.client.jms.internal:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.msg.client.jms:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.msg.client.provider:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.msg.client.wmq:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.msg.client.wmq.common:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.mq.connector:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.mq.commonservices:jar:7.0.1.5:test
 |  +- com.ibm.websphere:com.ibm.mq.headers:jar:7.0.1.5:test
 |  \- com.ibm.websphere:com.ibm.mq.pcf:jar:7.0.1.5:test
 +- com.ibm.websphere:com.ibm.ws.ejb.thinclient:jar:8.0.0:test
 +- com.project.app:main-project-packaging:jar:1.0.0-SNAPSHOT:compile
 |  +- org.apache.pdfbox:pdfbox:jar:3.0.0-alpha3:compile
 |  |  +- org.apache.pdfbox:io:jar:3.0.0-alpha3:compile
 |  |  \- org.apache.pdfbox:fontbox:jar:3.0.0-alpha3:compile
 |  +- org.bouncycastle:bcprov-jdk18on:jar:1.72:compile
 |  +- net.sf.opencsv:opencsv:jar:2.4:compile
 |  \- com.commons:dependencies-emf-ecore-sdo:pom:2.2.1.0:runtime
 |     +- org.eclipse.persistence:commonj.sdo:jar:2.1.1:runtime
 |     +- org.eclipse.emf:org.eclipse.emf.ecore:jar:2.2.1.v200609210005:runtime
 |     +- org.eclipse.emf:org.eclipse.emf.common:jar:2.2.1.v200609210005:runtime
 |     +- org.eclipse.emf:org.eclipse.emf.ecore.sdo:jar:2.2.0.v200609210005:runtime
 |     +- org.eclipse.emf:org.eclipse.emf.ecore.xmi:jar:2.2.1.v200609210005:runtime
 |     \- org.eclipse.emf:org.eclipse.emf.ecore.change:jar:2.2.0.v200609210005:runtime
 +- javax.persistence:javax.persistence-api:jar:2.2:compile
 \- org.apache.commons:commons-dbcp2:jar:2.10.0:compile
    +- org.apache.commons:commons-pool2:jar:2.11.1:compile
    +- commons-logging:commons-logging:jar:1.2:compile
    \- jakarta.transaction:jakarta.transaction-api:jar:1.3.1:compile

UPDATE:

Data access was using jpa 2.1 but hibernate.core is loading jpa 2.2 (javax.persistence-api-2.2), since data-access is a internal lib I migrated it to jpa 2.2.

However, the exception still happen at runtime. With some digging it seem that 3 librairies have the package javax.persistence in their project that seem to interfer with javax.persistence-api-2.2. (the lib with the problem were all libs from ibm websphere, ibm.ws.thinclient, ibm.ws.runtime & ibm.ws.ejb.) Just to hit this point home here the structure of one of them

> com.ibm.websphere:com.ibm.ws.ejb.thinclient:jar:8.5.0
---> com.ibm.*
    ----> has many package
---> javax.*
    ----> has many package
    ----> persistences
        -----> Table.java -> 2.0 interface

In all project that had these issues I've put javax.persistence-api-2.2 first in the dependencies tree so it will be the first jpa lib in the classpath. It seem to be the case when I run mvn dependency:build-classpath -Dmdep.includeScope=compile

However, the exception still happen even after that change.

Update 2:

After re-packaging ibm.ws.thinclient & ibm.ws.ejb.embeddableContainer to exclude the folder src/javax/persistense from these project, the app build correctly and no longer have the NoSuchMethodError. It has a another exception which (NoSuchBeanDefinitionException) which seem to be unrelated. However, removing the folder from the jar is not ideal and probably using a solution involving the classloader is probably a cleaner alternative.


Solution

  • Like explain in the update, the problem come from two libs from ibm (ibm.ws.thinclient & ibm.ws.ejb.embeddableContainer) which included a implementation on jpa directly in thier libs instead of a dependency that could be managed via maven.

    Solution:

    1. re-package ibm.ws.thinclient & ibm.ws.ejb.embeddableContainer to exclude the folder src/javax/persistense from these project, the app build correctly and no longer have the NoSuchMethodError. This however force us to have custom jar in our company nexus.

    2. The another way is to overide the classloader via -Djava.endorsed.dirs, but that will only work with java 8 and earlier version. https://tomcat.apache.org/tomcat-8.5-doc/class-loader-howto.html

    We tested both and they work but since this project might need to be migrated to a more recent version of java we decided to go with the first option.

    Hope this can help someone with the same problem and save them some precious time.