Jooq: Add javax.xml.bind:jaxb-api dependency and avoid using the implementation

Created on 30 Nov 2017  Â·  12Comments  Â·  Source: jOOQ/jOOQ

When running the code generator (or certain parts of the jOOQ runtime) with JDK 9, there may be issues related to jOOQ's implicit JAXB dependency: https://github.com/jOOQ/jOOQ/issues/6477

JAXB wasn't a "JDK-external" dependency up until recently, but it has become one in Java 9. We should avoid this dependency in jOOQ's internals:

  1. JAXB isn't really used for anything significant in jOOQ, only for unmarshalling things like the code generator configuration or the runtime settings. That unmarshalling could be easily done manually as well
  2. The annotations can stay present on the JAXB-annotated objects. As far as I know, annotations that cannot be loaded by the class loader will simply be ignored. If they stay present, then the change in 1. (manual unmarshalling) would be backwards compatible, e.g. for the third-party gradle plugin, which reads the annotations. Up to them to figure out how to include the dependency.
  3. Compiling the annotations can be done by depending on the javax.xml.bind:jaxb-api dependency only. It's sufficient to add this dependency to the pom.xml, and the result will work for both Java 8 and 9

Some interesting additional info:

Functionality Medium Fixed Enhancement

Most helpful comment

This did the trick for me:

When Java 9 introduced modularization efforts, our JOOQ codegen phase was suddenly broken due to missing JAXB dependency. The simplest solution was to add org.gradle.jvmargs=--add-modules java.xml.bind to our gradle.properties. In Java 11, —-add-modules is no more allowed...JAXB has been removed from the JDK in JDK 11. It causes jOOQ users some extra trouble to configure when setting up jOOQ for the first time.

The updated classpath and JOOQ runtime support in build.gradle did the trick for me.

// file: build.gradle
buildscript {
    dependencies {
        classpath 'org.glassfish.jaxb:jaxb-runtime:2.3.1'
    }
}
plugins {
    ...
    id 'nu.studer.jooq' version '3.0.2'
    ...
}
dependencies {
   ...

   jooqRuntime group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '2.3.1'

   jooqRuntime group: 'com.sun.activation', name: 'javax.activation', version: '1.2.0'
   ...
}

Credit: https://medium.com/@marekscholle/jooq-with-gradle-and-java-11-a6f10efff297

All 12 comments

I recently upgraded to 3.11 and was hoping to remove my workarounds for the jaxb stuff on JDK9 (technically JDK10 but probably same issue), but it didn't seem to work.

My work around is

jooqRuntime 'javax.activation:activation'
jooqRuntime 'javax.xml.bind:jaxb-api'
jooqRuntime 'com.sun.xml.bind:jaxb-core'
jooqRuntime 'com.sun.xml.bind:jaxb-impl'

and removing it results in the below exception (pretty sure it's what I saw back when I updated to JDK9 with 3.10).

org.jooq.codegen.GeneratorException: Error while reading XML configuration
        at org.jooq.codegen.GenerationTool.load(GenerationTool.java:925)
        at org.jooq.codegen.GenerationTool.main(GenerationTool.java:192)
Caused by: javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
 - with linked exception:
[java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory]
        at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:278)
        at javax.xml.bind.ContextFinder.find(ContextFinder.java:421)
        at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721)
        at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662)
        at org.jooq.codegen.GenerationTool.load(GenerationTool.java:912)
        ... 1 more
Caused by: java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
        at javax.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.java:122)
        at javax.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.java:155)
        at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:276)
        ... 5 more

@anuraaga Thanks for your feedback. Indeed, this is a bit of a pity and there isn't really a workaround, until jOOQ is modularised, which happens in jOOQ 3.12 (#6612). Once jOOQ is modularised, it can make the dependency on all of these things explicit from the module-info.java file, not just the jaxb-api dependency. We shouldn't add a dependency on jaxb-core and jaxb-impl from Maven because:

  • Maven adds dependencies including version numbers
  • Maven dependencies are added transitively unlike JPMS dependencies, whose transitiveness has to be made explicit

Thanks for clarifying - I had interpreted this issue, and it being closed, as the error being unexpected but obviously it is still expected. Will keep the workarounds until 3.12 :)

I'm trying to get jOOQ code generation to work on openjdk 10 using the Gradle plugin without success.

I have added the following to my build file like @anuraaga mentioned:

jooqRuntime "javax.activation:activation:1.1.1"
jooqRuntime "javax.xml.bind:jaxb-api:2.2.12"
jooqRuntime "com.sun.xml.bind:jaxb-core:2.2.11"
jooqRuntime "com.sun.xml.bind:jaxb-impl:2.2.11"

and gw -q dependencies --configuration jooqRuntime gives me:

jooqRuntime - The classpath used to invoke the jOOQ generator. Add your JDBC drivers or generator extensions here.
Errors occurred while build effective model from /Users/marceloverdijk/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-core/2.2.11/db0f76866c6b1e50084e03ee8cf9ce6b19becdb3/jaxb-core-2.2.11.pom:
    'dependencyManagement.dependencies.dependency.systemPath' for com.sun:tools:jar must specify an absolute path but is ${tools.jar} in com.sun.xml.bind:jaxb-core:2.2.11
Errors occurred while build effective model from /Users/marceloverdijk/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-impl/2.2.11/2d4b554997fd01d1a2233b1529b22fc9ecc0cf5c/jaxb-impl-2.2.11.pom:
    'dependencyManagement.dependencies.dependency.systemPath' for com.sun:tools:jar must specify an absolute path but is ${tools.jar} in com.sun.xml.bind:jaxb-impl:2.2.11
+--- org.jooq:jooq-codegen -> 3.11.4
|    +--- org.jooq:jooq:3.11.4
|    |    \--- javax.xml.bind:jaxb-api:2.2.12
|    \--- org.jooq:jooq-meta:3.11.4
|         \--- org.jooq:jooq:3.11.4 (*)
+--- javax.activation:activation:1.1.1
+--- javax.xml.bind:jaxb-api:2.2.12
+--- com.sun.xml.bind:jaxb-core:2.2.11
+--- com.sun.xml.bind:jaxb-impl:2.2.11
\--- org.xerial:sqlite-jdbc:3.23.1

but when running the task to do the code generation I get the same javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath error:

$ gw clean generateMydbJooqSchemaSource --stacktrace
Using gradle at '/Users/marceloverdijk/workspace/my-project/gradlew' to run buildfile '/Users/marceloverdijk/workspace/my-project/build.gradle':

> Task :generateMydbJooqSchemaSource FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':generateMydbJooqSchemaSource'.
> javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
   - with linked exception:
  [java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory]

* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':generateMydbJooqSchemaSource'.
        at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:49)
        at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:101)
        at org.gradle.api.internal.tasks.execution.FinalizeInputFilePropertiesTaskExecuter.execute(FinalizeInputFilePropertiesTaskExecuter.java:44)
        at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:91)
        at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:62)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:59)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
        at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.run(EventFiringTaskExecuter.java:51)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:300)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:292)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:174)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
        at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:46)
        at org.gradle.execution.taskgraph.LocalTaskInfoExecutor.execute(LocalTaskInfoExecutor.java:42)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:273)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:258)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:135)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:130)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.execute(DefaultTaskPlanExecutor.java:200)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.executeWithWork(DefaultTaskPlanExecutor.java:191)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.run(DefaultTaskPlanExecutor.java:130)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
Caused by: org.gradle.internal.UncheckedException: javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
 - with linked exception:
[java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory]
        at org.gradle.internal.UncheckedException.throwAsUncheckedException(UncheckedException.java:63)
        at org.gradle.internal.UncheckedException.throwAsUncheckedException(UncheckedException.java:40)
        at org.gradle.api.internal.tasks.properties.bean.AbstractNestedRuntimeBeanNode$DefaultPropertyValue$1$1.create(AbstractNestedRuntimeBeanNode.java:85)
        at org.gradle.util.SingleMessageLogger.whileDisabled(SingleMessageLogger.java:200)
        at org.gradle.api.internal.tasks.properties.bean.AbstractNestedRuntimeBeanNode$DefaultPropertyValue$1.get(AbstractNestedRuntimeBeanNode.java:80)
        at com.google.common.base.Suppliers$MemoizingSupplier.get(Suppliers.java:125)
        at org.gradle.api.internal.tasks.properties.bean.AbstractNestedRuntimeBeanNode$DefaultPropertyValue.getValue(AbstractNestedRuntimeBeanNode.java:138)
        at org.gradle.api.internal.tasks.properties.bean.AbstractNestedRuntimeBeanNode$DefaultPropertyValue.validate(AbstractNestedRuntimeBeanNode.java:149)
        at org.gradle.api.internal.tasks.DefaultTaskInputPropertySpec.validate(DefaultTaskInputPropertySpec.java:61)
        at org.gradle.api.internal.tasks.execution.DefaultTaskProperties.validate(DefaultTaskProperties.java:193)
        at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:47)
        ... 26 more
Caused by: javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
 - with linked exception:
[java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at nu.studer.gradle.jooq.JooqTask.generateConfigurationBytes(JooqTask.groovy:105)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at nu.studer.gradle.jooq.JooqTask.getConfigurationBytes(JooqTask.groovy:73)
        at nu.studer.gradle.jooq.JooqTask.getConfigurationHash(JooqTask.groovy:66)
        at nu.studer.gradle.jooq.JooqTask_Decorated.getConfigurationHash(Unknown Source)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at org.gradle.api.internal.tasks.properties.bean.AbstractNestedRuntimeBeanNode$DefaultPropertyValue$1$1.create(AbstractNestedRuntimeBeanNode.java:83)
        ... 34 more
Caused by: java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory
        ... 48 more

I also tried with only:

jooqRuntime "javax.xml.bind:jaxb-api:2.3.0"
jooqRuntime "org.glassfish.jaxb:jaxb-runtime:2.3.0"

as suggested in the linked SO article (https://stackoverflow.com/a/26413432/521799), but it still gives the same error.

Any ideas?

Hey - sorry for not updating this issue too. After more investigation I found that I needed those on _both_ jooqRuntime and the Gradle classpath.

https://github.com/etiennestuder/gradle-jooq-plugin/pull/78 provides some more context. Unfortunately it seems the Gradle plugin isn't really maintained anymore so getting this to work will probably always require some hacks :(

Thx @anuraaga so maybe better to not use the Gradle plugin but use the XML MarkupBuilder explained in https://www.jooq.org/doc/3.11/manual-single-page/#codegen-gradle

@anuraaga even adding the deps to buildSrc classpath gives the same error.
After removing the plugin and using the XML MarkupBuilder I'm able to generate the sources (note I also had to to add jaxb and activation deps to the classpath of course).

@anuraaga Why do you say the gradle plugin isn't mantained? It seems quite the opposite.

  • Taking months to address issues, especially one affecting this one

https://github.com/etiennestuder/gradle-jooq-plugin/issues/71
https://github.com/etiennestuder/gradle-jooq-plugin/issues/77

  • Still using Gradle wrapper 3.x making development in modern environments confusing

Sorry, I'm mostly ranting so please ignore if you don't like them.

But my standards for open source are much higher than that, especially for a project considered "official". The quality compared to jooq itself is abysmal.

This did the trick for me:

When Java 9 introduced modularization efforts, our JOOQ codegen phase was suddenly broken due to missing JAXB dependency. The simplest solution was to add org.gradle.jvmargs=--add-modules java.xml.bind to our gradle.properties. In Java 11, —-add-modules is no more allowed...JAXB has been removed from the JDK in JDK 11. It causes jOOQ users some extra trouble to configure when setting up jOOQ for the first time.

The updated classpath and JOOQ runtime support in build.gradle did the trick for me.

// file: build.gradle
buildscript {
    dependencies {
        classpath 'org.glassfish.jaxb:jaxb-runtime:2.3.1'
    }
}
plugins {
    ...
    id 'nu.studer.jooq' version '3.0.2'
    ...
}
dependencies {
   ...

   jooqRuntime group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '2.3.1'

   jooqRuntime group: 'com.sun.activation', name: 'javax.activation', version: '1.2.0'
   ...
}

Credit: https://medium.com/@marekscholle/jooq-with-gradle-and-java-11-a6f10efff297

@anuraaga But my standards for open source are much higher than that, especially for a project considered "official". The quality compared to jooq itself is abysmal.

I'm not sure what you're referring to specifically, but rest assured, the JAXB related changes around JDK 9-11 have been a major pain for all library and tool vendors out there. We've had this situation before in our ecosystem, namely when JAXB was added to the JDK in Java 6.

I remember having patched Weblogic several times with new endorsed dir configurations that then introduced regressions in other dependencies and what not. Such backwards incompatible changes do have tons of ripple effects, especially when every library and their dependencies have subtle fixes and/or regressions in minor / patch releases. A great case for jOOQ not to ever introduce any dependencies of its own... except JAXB :-(

Every question / answer you find on Stack Overflow is already outdated, because a lot of people have patched things for JDK 9/10, when in JDK 11, everything has changed again.

You can see how many issues we have in the issue tracker, just about JAXB:
https://github.com/jOOQ/jOOQ/search?q=jaxb&type=Issues

In hindsight, I deeply regret having ever used JAXB. jOOQ 3.12 will ship with its own "mini JAXB" implementation to get rid of this mess for most people, depending only on the API (annotations).

However, if you do have any additional specific criticism regarding the third party plugin, I would love to hear it. I'm sure there's always room for improvement.

Thanks,
Lukas

For the record, as of gradle-jooq-plugin 3.0.3, it is no longer necessary to declare the JAXB implementation dependencies when using JDK 9 or higher.

Was this page helpful?
0 / 5 - 0 ratings