This seems to be a particularly nasty problem. I've got some unit tests which test the interface to our business layer, which uses Hibernate to do its ORM. I can run the tests just fine within Eclipse. However, when I check my code in to source control, we have a build machine configured to automatically build the project and run all unit tests. On the build machine, the unit tests which require the use of a Hibernate Session (and therefore the creation of a SessionFactory) fail, because the SessionFactory cannot be instantiated. The following exception is thrown:
Code:
java.lang.ExceptionInInitializerError
at net.sf.hibernate.connection.ConnectionProviderFactory.newConnectionProvider(ConnectionProviderFactory.java:78)
at net.sf.hibernate.cfg.SettingsFactory.buildSettings(SettingsFactory.java:76)
at net.sf.hibernate.cfg.Configuration.buildSettings(Configuration.java:1172)
at net.sf.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:803)
at edu.taylor.util.SessionGetter.<clinit>(SessionGetter.java:27)
at edu.taylor.database.DatabaseActionManager.getSession(DatabaseActionManager.java:35)
at edu.taylor.database.DatabaseActionManager.getResult(DatabaseActionManager.java:112)
at edu.taylor.database.DatabaseActionManager.execute(DatabaseActionManager.java:172)
at edu.taylor.database.DatabaseActionManager.execute(DatabaseActionManager.java:179)
at edu.taylor.domain.services.RosterService.getStudentById(RosterService.java:398)
at edu.taylor.domain.services.TestRosterService.testGetStudentById(TestRosterService.java:107)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:325)
at org.apache.tools.ant.taskdefs.optional.junit.JUnitTask.executeInVM(JUnitTask.java:848)
at org.apache.tools.ant.taskdefs.optional.junit.JUnitTask.execute(JUnitTask.java:556)
at org.apache.tools.ant.taskdefs.optional.junit.JUnitTask.execute(JUnitTask.java:532)
at org.apache.tools.ant.Task.perform(Task.java:341)
at org.apache.commons.jelly.tags.ant.AntTag.doTag(AntTag.java:185)
at org.apache.commons.jelly.impl.TagScript.run(TagScript.java:279)
at org.apache.commons.jelly.impl.ScriptBlock.run(ScriptBlock.java:135)
at org.apache.commons.jelly.TagSupport.invokeBody(TagSupport.java:233)
at org.apache.commons.jelly.tags.core.IfTag.doTag(IfTag.java:88)
at org.apache.commons.jelly.impl.TagScript.run(TagScript.java:279)
at org.apache.commons.jelly.impl.ScriptBlock.run(ScriptBlock.java:135)
at org.apache.maven.jelly.tags.werkz.MavenGoalTag.runBodyTag(MavenGoalTag.java:79)
at org.apache.maven.jelly.tags.werkz.MavenGoalTag$MavenGoalAction.performAction(MavenGoalTag.java:110)
at com.werken.werkz.Goal.fire(Goal.java:639)
at com.werken.werkz.Goal.attain(Goal.java:575)
at com.werken.werkz.Goal.attainPrecursors(Goal.java:488)
at com.werken.werkz.Goal.attain(Goal.java:573)
at com.werken.werkz.Goal.attainPrecursors(Goal.java:488)
at com.werken.werkz.Goal.attain(Goal.java:573)
at org.apache.maven.plugin.PluginManager.attainGoals(PluginManager.java:671)
at org.apache.maven.MavenSession.attainGoals(MavenSession.java:263)
at org.apache.maven.jelly.tags.maven.ReactorTag.doTag(ReactorTag.java:368)
at org.apache.commons.jelly.impl.TagScript.run(TagScript.java:279)
at org.apache.commons.jelly.impl.ScriptBlock.run(ScriptBlock.java:135)
at org.apache.commons.jelly.TagSupport.invokeBody(TagSupport.java:233)
at org.apache.commons.jelly.tags.core.IfTag.doTag(IfTag.java:88)
at org.apache.commons.jelly.impl.TagScript.run(TagScript.java:279)
at org.apache.commons.jelly.impl.ScriptBlock.run(ScriptBlock.java:135)
at org.apache.maven.jelly.tags.werkz.MavenGoalTag.runBodyTag(MavenGoalTag.java:79)
at org.apache.maven.jelly.tags.werkz.MavenGoalTag$MavenGoalAction.performAction(MavenGoalTag.java:110)
at com.werken.werkz.Goal.fire(Goal.java:639)
at com.werken.werkz.Goal.attain(Goal.java:575)
at com.werken.werkz.Goal.attainPrecursors(Goal.java:488)
at com.werken.werkz.Goal.attain(Goal.java:573)
at com.werken.werkz.Goal.attainPrecursors(Goal.java:488)
at com.werken.werkz.Goal.attain(Goal.java:573)
at org.apache.maven.plugin.PluginManager.attainGoals(PluginManager.java:671)
at org.apache.maven.MavenSession.attainGoals(MavenSession.java:263)
at org.apache.maven.cli.App.doMain(App.java:488)
at org.apache.maven.cli.App.main(App.java:1239)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at com.werken.forehead.Forehead.run(Forehead.java:551)
at com.werken.forehead.Forehead.main(Forehead.java:581)
Caused by: org.apache.commons.logging.LogConfigurationException: java.lang.IllegalAccessError: class sun.reflect.GeneratedConstructorAccessor64 cannot access its superclass sun.reflect.ConstructorAccessorImpl
at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:532)
at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:272)
at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:246)
at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:395)
at net.sf.hibernate.connection.DriverManagerConnectionProvider.<clinit>(DriverManagerConnectionProvider.java:35)
... 70 more
Caused by: java.lang.IllegalAccessError: class sun.reflect.GeneratedConstructorAccessor64 cannot access its superclass sun.reflect.ConstructorAccessorImpl
at sun.misc.Unsafe.defineClass(Native Method)
at sun.reflect.ClassDefiner.defineClass(ClassDefiner.java:45)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:381)
at java.security.AccessController.doPrivileged(Native Method)
at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:377)
at sun.reflect.MethodAccessorGenerator.generateConstructor(MethodAccessorGenerator.java:76)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:30)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:494)
at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:525)
... 74 more
My apologies for the gigantic stack trace, but having it all helps a bit to see what's going on. What it all comes down to is that IllegalAccessError that gets thrown when GeneratedConstructorAccessor64 attempts to access its superclass, ConstructorAccessor. I'll be honest, I don't really know this stuff does. Perhaps some of you java gurus know what these classes are doing. At any rate, after reading these articles:
http://www.qos.ch/logging/thinkAgain.jsp
http://www.qos.ch/logging/classloader.jsp
I think that ConstructorAccessorImpl and its child class are being loaded by different class loaders. Detailed maven logs show that ConstructorAccessorImpl is loaded by the ant class loader while the junit test is running. Apparently the child class is loaded somewhere else (or something even weirder is going on?), and so everything blows up.
Two questions:
First, can I fix it? Is there a place where I can put log4j.jar, or some other jar, that will cause the correct classes to be loaded by the correct class loaders? If not, then unit testing becomes nearly useless to us, since so many tests will fail all the time on the build machine.
Second, and this is just out of curiousity, not a demand for change - why does Hibernate depend on both commons-logging AND log4j? If wrapping logging functionality at a higher level with commons-logging is felt to be important, shouldn't dependencies on other logging packages be avoided so that the user can choose? And if it isn't that important, why bother, given the documented issues with commons logging?
Thanks in advance for any comments, help, or angry rebuttals ;)