ontop: InaccessibleObjectException on Java 16

Description

ontop requires Java 8. As Java is largely backwards compatible, this should allow it to run on Java 16, however it throws a java.lang.reflect.InaccessibleObjectException.

Steps to Reproduce

ontop materialize -m r2rml/citation.r2rml.ttl -o output/citation.ontop.nt -f ntriples -p hito.properties

Expected behavior: A program developed for Java 8 should also run under Java 16. Alternatively it should be mentioned that it only works with exactly Java 8.

Actual behavior: Java 16 throws a java.lang.reflect.InaccessibleObjectException, see below. Java 11 gives the following warning:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.inject.internal.cglib.core.$ReflectUtils$1 (file:/home/konrad/opt/ontop/lib/guice-4.1.0.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of com.google.inject.internal.cglib.core.$ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

Reproduces how often: Every time.

Workaround Change the line ONTOP_JAVA_ARGS="-Xmx512m" to ONTOP_JAVA_ARGS="-Xmx512m --illegal-access=permit" in the ontop script. However that is discouraged and may not work in Java 17.

Attached material

$ ontop materialize -m r2rml/citation.r2rml.ttl -o output/citation.ontop.nt -f ntriples -p hito.properties
Exception in thread "main" com.google.common.util.concurrent.ExecutionError: com.google.common.util.concurrent.ExecutionError: java.lang.ExceptionInInitializerError
	at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2201)
	at com.google.common.cache.LocalCache.get(LocalCache.java:3937)
	at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3941)
	at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4824)
	at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4830)
	at com.google.inject.internal.FailableCache.get(FailableCache.java:48)
	at com.google.inject.internal.ConstructorInjectorStore.get(ConstructorInjectorStore.java:50)
	at com.google.inject.internal.ConstructorBindingImpl.initialize(ConstructorBindingImpl.java:138)
	at com.google.inject.internal.InjectorImpl.initializeJitBinding(InjectorImpl.java:550)
	at com.google.inject.internal.InjectorImpl.createJustInTimeBinding(InjectorImpl.java:887)
	at com.google.inject.internal.InjectorImpl.createJustInTimeBindingRecursive(InjectorImpl.java:808)
	at com.google.inject.internal.InjectorImpl.getJustInTimeBinding(InjectorImpl.java:285)
	at com.google.inject.internal.InjectorImpl.getBindingOrThrow(InjectorImpl.java:217)
	at com.google.inject.internal.InjectorImpl.getInternalFactory(InjectorImpl.java:893)
	at com.google.inject.internal.FactoryProxy.notify(FactoryProxy.java:46)
	at com.google.inject.internal.ProcessedBindingData.runCreationListeners(ProcessedBindingData.java:50)
	at com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:134)
	at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:107)
	at com.google.inject.Guice.createInjector(Guice.java:99)
	at com.google.inject.Guice.createInjector(Guice.java:73)
	at it.unibz.inf.ontop.injection.impl.OntopModelConfigurationImpl.getInjector(OntopModelConfigurationImpl.java:95)
	at it.unibz.inf.ontop.materialization.impl.DefaultOntopRDFMaterializer.<init>(DefaultOntopRDFMaterializer.java:45)
	at it.unibz.inf.ontop.rdf4j.materialization.impl.DefaultRDF4JMaterializer.<init>(DefaultRDF4JMaterializer.java:59)
	at it.unibz.inf.ontop.rdf4j.materialization.RDF4JMaterializer.defaultMaterializer(RDF4JMaterializer.java:34)
	at it.unibz.inf.ontop.cli.OntopMaterialize.createMaterializer(OntopMaterialize.java:121)
	at it.unibz.inf.ontop.cli.OntopMaterialize.run(OntopMaterialize.java:102)
	at it.unibz.inf.ontop.cli.Ontop.main(Ontop.java:20)
Caused by: com.google.common.util.concurrent.ExecutionError: java.lang.ExceptionInInitializerError
	at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2201)
	at com.google.common.cache.LocalCache.get(LocalCache.java:3937)
	at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3941)
	at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4824)
	at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4830)
	at com.google.inject.internal.FailableCache.get(FailableCache.java:48)
	at com.google.inject.internal.MembersInjectorStore.get(MembersInjectorStore.java:68)
	at com.google.inject.internal.ConstructorInjectorStore.createConstructor(ConstructorInjectorStore.java:75)
	at com.google.inject.internal.ConstructorInjectorStore.access$000(ConstructorInjectorStore.java:29)
	at com.google.inject.internal.ConstructorInjectorStore$1.create(ConstructorInjectorStore.java:37)
	at com.google.inject.internal.ConstructorInjectorStore$1.create(ConstructorInjectorStore.java:33)
	at com.google.inject.internal.FailableCache$1.load(FailableCache.java:37)
	at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3527)
	at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2319)
	at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2282)
	at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2197)
	... 26 more
Caused by: java.lang.ExceptionInInitializerError
	at com.google.inject.internal.cglib.reflect.$FastClass$Generator.getProtectionDomain(FastClass.java:73)
	at com.google.inject.internal.cglib.core.$AbstractClassGenerator.create(AbstractClassGenerator.java:206)
	at com.google.inject.internal.cglib.reflect.$FastClass$Generator.create(FastClass.java:65)
	at com.google.inject.internal.BytecodeGen.newFastClassForMember(BytecodeGen.java:252)
	at com.google.inject.internal.BytecodeGen.newFastClassForMember(BytecodeGen.java:203)
	at com.google.inject.internal.SingleMethodInjector.createMethodInvoker(SingleMethodInjector.java:47)
	at com.google.inject.internal.SingleMethodInjector.<init>(SingleMethodInjector.java:38)
	at com.google.inject.internal.MembersInjectorStore.getInjectors(MembersInjectorStore.java:131)
	at com.google.inject.internal.MembersInjectorStore.createWithListeners(MembersInjectorStore.java:98)
	at com.google.inject.internal.MembersInjectorStore.access$000(MembersInjectorStore.java:37)
	at com.google.inject.internal.MembersInjectorStore$1.create(MembersInjectorStore.java:45)
	at com.google.inject.internal.MembersInjectorStore$1.create(MembersInjectorStore.java:42)
	at com.google.inject.internal.FailableCache$1.load(FailableCache.java:37)
	at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3527)
	at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2319)
	at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2282)
	at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2197)
	... 41 more
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 @51cdd8a
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:357)
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
	at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
	at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
	at com.google.inject.internal.cglib.core.$ReflectUtils$1.run(ReflectUtils.java:52)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:312)
	at com.google.inject.internal.cglib.core.$ReflectUtils.<clinit>(ReflectUtils.java:42)
	... 58 more

Versions

Ontop 4.1 openjdk 16.0.2 2021-07-20 OpenJDK Runtime Environment (build 16.0.2+7) OpenJDK 64-Bit Server VM (build 16.0.2+7, mixed mode)

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 37 (35 by maintainers)

Most upvoted comments

@bcogrel: dependency removed, I forgot it sorry. I also revised all changes w.r.t. version4 branch and revised an imprecise import (shaded guava class vs correct guava class) and some formatting (tab/spaces). Also repeated the testing of all Java 8 / Java 17 combinations for compiling and running Ontop endpoint and Ontop Protégé plugin (note: only Java 8 can be used to run Protégé), and they all work.

In the meanwhile a decision is taken on the adapter class (see discussion), I changed branch protege-refactoring-reflection as follows:

  • updated both Guice and the maven bundle plugin to their latest version;
  • added the indication of Java / OS version in endpoint banner (for diagnostics)
  • in OntopEndpoint command, changed line System.setProperty("logback.configurationFile", "") into System.clearProperty("logback.configurationFile")
  • did some tests on Java 8 and 17

As a result:

  • Ontop CLI runs on both Java 8 and Java 17, independently on whether it is compiled with JDK 8 or JDK 17
  • Ontop Protégé plugin runs within Protégé on Java 8 (Protégé does not run with Java 17), independently on whether it is compiled with JDK 8 or JDK 17
  • (apparently) the log configuration issue #361 (which was appearing on Java 17 from the command line) has disappeared both on command line and IDE (Eclipse)

In branch feature/protege-refactoring-reflection there’s a new simpler solution – let’s call it (D) – that does not require neither an extra module nor the use of shading, but instead leverages Java reflection to address the only clash we have on Guava classes.

Briefly, in this solution (D):

  • we setup the Ontop Protégé plugin bundle not to import Guava from Protégé (v. 18.0), but instead to use its own internal & newer version of Guava (v. 30.1.1-jre)

  • to avoid NoMethodDefFound errors (at class load/resolution time) when Ontop Protégé classes refer to Protégé / OWL API methods that have Guava classes (e.g., Optional) as parameters or return types, we perform those calls via reflection, leveraging the fact that (after analysis) there is only one Ontop Protégé class affected: OntologyPrefixManager.

  • to simplify development, there is a new Adapter class exposing public static method that wrap calls via reflection of all (I hope so) problematic public Protégé / OWL API methods involving Guava classes (currently, we use only getOntologyIRI)

  • besides, I did some cleanup in Ontop Protégé pom.xml, in particular enabling embedding of transitive dependencies in the generated OSGI bundle, rather than listing them explicitly that is prone to errors (as we have to update the list every time some new transitive dependency is added to used Ontop modules)

I kept the previous solution (A) in its own branch feature/protege-refactoring-shade (renamed adding -shade).

New solution (D) pros, compared to (A):

  • it does not use shading
  • it does not require the new module ontop-protege-dependencies
  • it does not require module ontop-protege to depend on shaded module ontop-protege-dependencies that may be problematic in IDEs

Previous solution (A) pros, compared to (D):

  • it makes explicit at compile the presence of two versions of Guava (old one from Protégé, new relocated one from Ontop) and thus can errors in wrongly mixing the two can be spotted by the compiler rather than resulting in runtime NoMethodDefFound
  • it allows bridging the two versions with regular Java code, without the reflection tricks wrapped by new class Adapter
  • it may be feasibly extended to decouple other libraries (OWL API - although we do not need it, based on your feedback), whereas this will become increasingly messy with new solution (D)

Overall and considering the current situation (and particularly in light of unclear issues with IDEs), my two cents are for going with new solution (D) and keep (A) in its own branch for future reference, in case we would need to reconsider the decision.

Let me know what you think, and consider I still have some work to do in order to finalize both solutions, namely:

  • extensive tests of Ontop Protégé plugin within Protégé
  • tests with newer JDKs (to solve the original issue)
  • optimization of generated Ontop Protégé bundle, e.g., trimming down size by excluding embedding of unneeded libraries (I suspect we have a few of them)
  • documentation, e.g., of how to develop using Adapter class, for solution (D)

Merged with https://github.com/ontop/ontop/pull/479 . The issue should be now be fixed. Thanks again!