ReflectUtils breaks with Java 9 build 148+
See original GitHub issueSince Java 9 build 148, it is no longer possible to do setAccessible on any public Java runtime API class (except sun.misc.Unsafe as special case). This affects ClassLoader#defineClass (protected method).
Because of this any mocking library (Mockito, EasyMock,…) that uses CGLIB breaks with ExceptionOnInitializer.
Example in Apache Solr (Mockito/Easymock):
[junit4] ERROR 0.38s J2 | TestManagedSchemaThreadSafety.testThreadSafety <<<
[junit4] > Throwable #1: java.lang.ExceptionInInitializerError
[junit4] > at __randomizedtesting.SeedInfo.seed([8E654E5E1F32A757:142F5A06CCAD15A1]:0)
[junit4] > at org.mockito.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:167)
[junit4] > at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
[junit4] > at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217)
[junit4] > at org.mockito.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145)
[junit4] > at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:117)
[junit4] > at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:109)
[junit4] > at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:105)
[junit4] > at org.mockito.cglib.proxy.Enhancer.<clinit>(Enhancer.java:70)
[junit4] > at org.mockito.internal.creation.jmock.ClassImposterizer.createProxyClass(ClassImposterizer.java:85)
[junit4] > at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:62)
[junit4] > at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:56)
[junit4] > at org.mockito.internal.creation.CglibMockMaker.createMock(CglibMockMaker.java:23)
[junit4] > at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:26)
[junit4] > at org.mockito.internal.MockitoCore.mock(MockitoCore.java:51)
[junit4] > at org.mockito.Mockito.mock(Mockito.java:1243)
[junit4] > at org.apache.solr.schema.TestManagedSchemaThreadSafety.createZkController(TestManagedSchemaThreadSafety.java:135)
[junit4] > at org.apache.solr.schema.TestManagedSchemaThreadSafety.testThreadSafety(TestManagedSchemaThreadSafety.java:118)
[junit4] > at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[junit4] > at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[junit4] > at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[junit4] > at java.base/java.lang.reflect.Method.invoke(Method.java:538)
[junit4] > at java.base/java.lang.Thread.run(Thread.java:844)
[junit4] > Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @4cbd6df7
[junit4] > at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:207)
[junit4] > at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:192)
[junit4] > at java.base/java.lang.reflect.Method.setAccessible(Method.java:186)
[junit4] > at org.mockito.cglib.core.ReflectUtils$2.run(ReflectUtils.java:57)
[junit4] > at java.base/java.security.AccessController.doPrivileged(Native Method)
[junit4] > at org.mockito.cglib.core.ReflectUtils.<clinit>(ReflectUtils.java:47)
[junit4] > ... 55 more
[junit4] 2> 64469 INFO (SUITE-TestManagedSchemaThreadSafety-seed#[8E654E5E1F32A757]-worker) [ ] o.a.s.c.ZkTestServer connecting to 127.0.0.1:51648 51648
[junit4] 2> 64474 INFO (Thread-158) [ ] o.a.s.c.ZkTestServer connecting to 127.0.0.1:51648 51648
[junit4] 2> 64478 INFO (SUITE-TestManagedSchemaThreadSafety-seed#[8E654E5E1F32A757]-worker) [ ] o.a.s.SolrTestCaseJ4 ###deleteCore
[junit4] 2> NOTE: leaving temporary files on disk at: C:\Users\Uwe Schindler\Projects\lucene\trunk-lusolr1\solr\build\solr-core\test\J2\temp\solr.schema.TestManagedSchemaThreadSafety_8E654E5E1F32A757-001
[junit4] 2> Dez. 26, 2016 8:47:14 NACHM. com.carrotsearch.randomizedtesting.ThreadLeakControl checkThreadLeaks
[junit4] 2> WARNUNG: Will linger awaiting termination of 2 leaked thread(s).
[junit4] 2> NOTE: test params are: codec=Lucene70, sim=RandomSimilarity(queryNorm=false): {}, locale=so-KE, timezone=Etc/GMT+4
[junit4] 2> NOTE: Windows 10 10.0 amd64/Oracle Corporation 9-ea (64-bit)/cpus=4,threads=1,free=188820656,total=266338304
[junit4] 2> NOTE: All tests run in this JVM: [TestRandomFaceting, TestCharFilters, BlockJoinFacetSimpleTest, SolrCmdDistributorTest, TestTolerantUpdateProcessorRandomCloud, TestManagedSchemaThreadSafety]
[junit4] Completed [31/670 (1!)] on J2 in 0.83s, 1 test, 1 error <<< FAILURES!
I have no idea why CGLib needs to do this (instead of just subclassing ClassLoader). I have the feeling that mocking libs need to “inject” classes into existing classloaders, so the workaround is needed.
A trick to solve this might be using MethodHandles instead of Reflection using a “temporary” child class of ClassLoader (just to get a MethodHandle to the defineClass method). But this would require minimum Java 7. I may provide a PR to do this.
Issue Analytics
- State:
- Created 7 years ago
- Comments:29 (12 by maintainers)
Top GitHub Comments
It’s not that easy. Creating a new class loader causes the generated code to be loaded by another class loader. Even if the class’s package name is the same, the loaded class will exist in a different runtime package what limits the scope of classes that can be subclassed. There is no backwards-compatible way for us to resolve this.
This issue is not easy to workaround and I still hoping that this will not make the final release of Java 9, this change would break thousands of projects. For now, I suggest to wait and see as creating a new class loader every time is a very breaking change.
This is also a major performance issue as class loaders are quite expensive to create.
As for Mockito: It does no longer use cglib in more recent versions but the problem prevails for any other code generation library.
I stumbled upon the same problem, would have been too good to be true.