java.lang.IncompatibleClassChangeError: Implementing class Nedir ve Analizi Nasıl Yapılır?

Bu hata genelde bir interface ya da üst sınıf değişikliğe uğradıktan sonra, classpath içinde bu sınıfı eski haliyle implemente etmiş/genişletmiş bir sınıfın, üst sınıf tekrar kullanılarak derlenmeden kullanılması sonucu ortaya çıkan bir hata türüdür.

Çalıştığım projede bir web uygulamasını deploy ederken hatanın şu hali ile karşılaştım:

javax.servlet.ServletException: Error instantiating servlet class xxx.xxx.emobility.webapi.EMobilityUMAdminServlet
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    com.vw.mbbc.dispatcher.access.webapi.WebServerSimulator.invoke(WebServerSimulator.java:140)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    java.lang.Thread.run(Thread.java:744)
root cause

java.lang.IncompatibleClassChangeError: Implementing class
    java.lang.ClassLoader.defineClass1(Native Method)
    java.lang.ClassLoader.defineClass(ClassLoader.java:800)
    java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2918)
    org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1174)
    org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1669)
    org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1547)
    org.hibernate.ejb.Ejb3Configuration.<clinit>(Ejb3Configuration.java:107)
    org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:124)
    javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:48)
    javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:32)

Bu hatayı analiz etmek için HibernatePersistence sınıfının 124. satırından yola çıkıyoruz. Bu satırda Ejb3Configuration sınıfından bir nesne oluşturuluyor. Ejb3Configuration sınıfının 107. satırına göz attığımızda AnnotationConfiguration sınıfından bir nesne oluşturulduğunu görüyoruz. Stacktrace e göz attığımızda sınıf yükleyicisinin (classloader) bu sınıfı classpath içinde bulmak üzere işlem yaptığını görüyoruz.

AnnotationConfiguration sınıfı Configuration sınıfını genişletiyor. IncompatibleClassChangeError hatasının oluşmasının ana sebebi ise şu: org.hibernate.cfg.Configuration sınıfı AnnotationConfiguration sınıfı ile aynı jar dosyası içinde değil. Eğer aynı jar dosyasında olsalardı Classloader doğru Configuration sınıfını bulurdu ve IncompatibleClassChangeError hatası oluşmazdı. Benim ortamında org.hibernate.cfg.Configuration sınıfının iki değişik sürümü iki değişik jar dosyasındaydı ve classloader tesadüf eseri kullanımda olan AnnotationConfiguration sınıfı için değişikliğe uğramış olan Configuration sınıfını önce buldu ve yükledi. Classloader uyumsuzluğun farkına vardığı için IncompatibleClassChangeError hatası oluştu. 

Bu sorunu çözmek için değişikliğe uğramış olan üst sınıfı bulup, classpath dan uzaklaştırmak gerekiyor. Benim örneğimde iki Configuration sınıfı bulunmakta. Çoğu zaman classpath içinde değişikliğe uğramış bir üst sınıf yer alacaktır. Bu durumda üst sınıfı genişleten alt sınıfı tekrar derleyerek bu sorun çözülebilir.


EOF (Enf Of Fun)
Özcan Acar