Search code examples
javaspringtomcatclassloadertomcat8

java.lang.NoSuchMethodError because of duplicate class available in lib folder of Tomcat


I am facing

java.lang.NoSuchMethodError:javax.persistence.PersistenceContext.synchronization()Ljavax/persistence/SynchronizationType

while deploying my application in Tomcat 8 because tomcat class loader is loading PersistenceContext class from annotations-api.jar file located at TOMCAT_HOME\lib folder and PersistenceContext class from annotations-api.jar does not contain synchronization method as follows

package javax.persistence;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PersistenceContext {
  String name() default "";

  String unitName() default "";

  PersistenceContextType type() default PersistenceContextType.TRANSACTION;

  PersistenceProperty[] properties() default {};
}

While I want tomcat to load PersistenceContext class from javax.persitence-api-2.2 jar which I have copied to TOMCAT_HOME\lib folder instead of annotations-api.jar present at TOMCAT_HOME\lib folder because PersistenceContext class from javax.persitence-api-2.2 jar contains synchronization method as follows

package javax.persistence;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Repeatable(PersistenceContexts.class)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PersistenceContext {
  String name() default "";

  String unitName() default "";

  PersistenceContextType type() default PersistenceContextType.TRANSACTION;

  SynchronizationType synchronization() default SynchronizationType.SYNCHRONIZED;

  PersistenceProperty[] properties() default {};
}

So that tomcat will not give java.lang.NoSuchMethodError: javax.persistence.PersistenceContext.synchronization()Ljavax/persistence/SynchronizationType while deploying my application.

How to give preference in Tomcat 8 such that Tomcat 8 will load PersistenceContext class from javax.persistence-api-2.2.jar instead of annotations-api.jar ?

Edit : Tomcat 8 lib folder already contains annotations-api.jar and I had copied javax.persistence-api-2.2.jar in lib folder of Tomcat for eclipselink support in Tomcat 8 so exclude annotations-api.jar option in my dependencies.gradle is not going to help as annotations-api.jar is already part of lib folder of Tomcat. I have not added annotations-api.jar by myself.

Note : Same application is running file in Tomcat 9 because annotations-api.jar present in Tomcat 9 does not contain javax.persistence package.


Solution

  • Try to add javax.persistence-api-2.2.jar as a dependency in your project and remove it from tomcat lib, the reason for that the /WEB-INF/lib/*.jar of your web application will be loaded first if web application class loader is not configured with delegate="true

    Normal Order:

    1. Bootstrap classes of your JVM (Core java classes)
    2. /WEB-INF/classes of your web application
    3. /WEB-INF/lib/*.jar of your web application
    4. System class loader classes (Tomcat / Classpath specific classes)
    5. Common class loader classes (classes common to all web apps)

    With delegate="true" then order:

    1. Bootstrap classes of your JVM (Core java classes)
    2. System class loader classes (Tomcat / Classpath specific classes)
    3. Common class loader classes (classes common to all web apps)
    4. /WEB-INF/classes of your web application
    5. /WEB-INF/lib/*.jar of your web application