Gravity Grave
Gravity Grave

Reputation: 2862

Gradle jlink plugin fails during createMergedModule with "the service implementation does not have a default constructor"

I'm using Gradle (4.10.3, but I've tried most versions up through 5.4.1) with JDK 12.0.1 and the org.beryx.jlink plugin (2.10.4), but am running into this error every time I attempt to create the jlink image:

-> Task :createMergedModule

Cannot derive uses clause from service loader invocation in: com/fasterxml/jackson/databind/ObjectMapper$2.run().

Cannot derive uses clause from service loader invocation in: com/fasterxml/jackson/databind/ObjectMapper.secureGetServiceLoader().

Cannot derive uses clause from service loader invocation in: org/apache/commons/compress/utils/ServiceLoaderIterator.().

C:\Users\MyName\IdeaProjects\myapp\build\jlinkbase\tmpjars\myapp.merged.module\module-info.java:393: error: the service implementation does not have a default constructor: XBeansXPath provides org.apache.xmlbeans.impl.store.PathDelegate.SelectPathInterface with org.apache.xmlbeans.impl.xpath.saxon.XBeansXPath;

C:\Users\MyName\IdeaProjects\myapp\build\jlinkbase\tmpjars\myapp.merged.module\module-info.java:394: error: the service implementation does not have a default constructor: XBeansXQuery provides org.apache.xmlbeans.impl.store.QueryDelegate.QueryInterface with org.apache.xmlbeans.impl.xquery.saxon.XBeansXQuery; 2 errors

-> Task :createMergedModule FAILED

When I click through to the lines throwing the errors in the merged module-info.java, it points to these two:

provides org.apache.xmlbeans.impl.store.PathDelegate.SelectPathInterface with org.apache.xmlbeans.impl.xpath.saxon.XBeansXPath;
provides org.apache.xmlbeans.impl.store.QueryDelegate.QueryInterface with org.apache.xmlbeans.impl.xquery.saxon.XBeansXQuery;

My build.gradle file looks like this:

plugins {
    id 'application'
    id 'idea'
    id 'java'
    id 'org.openjfx.javafxplugin' version '0.0.7'
    id 'org.beryx.jlink' version '2.10.4'
}

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.apache.commons:commons-csv:1.6'
    compile 'org.apache.poi:poi-ooxml:4.1.0'
    compile 'com.fasterxml.jackson.core:jackson-core:2.9.9'
    compile 'com.fasterxml.jackson.core:jackson-annotations:2.9.9'
    compile 'com.fasterxml.jackson.core:jackson-databind:2.9.9'
    testCompile 'org.junit.jupiter:junit-jupiter-api:5.5.0-M1'
    testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.5.0-M1'
}

// ### Application plugin settings
application {
    mainClassName = "$moduleName/path.to.myapp"
}

// ### Idea plugin settings
idea {
    module {
        outputDir = file("out/production/classes")
    }
}

// ### Java plugin settings
sourceCompatibility = JavaVersion.VERSION_11

compileJava {
    options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
}

test {
    useJUnitPlatform()
    testLogging {
        events 'PASSED', 'FAILED', 'SKIPPED'
    }
}

// ### JavaFX plugin settings
javafx {
    version = "12.0.1"
    modules = ['javafx.controls', 'javafx.fxml']
}

// ### jlink plugin settings
jlink {
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
    launcher {
        name = 'myapp'
    }
}

Any ideas on how I can address this?

Upvotes: 2

Views: 2522

Answers (1)

Jos&#233; Pereda
Jos&#233; Pereda

Reputation: 45456

The following possible solution is based just on your build file, without knowing about your module-info or the project's code, so it might not work.

It is also based on this issue at the jlink plugin's repository.

First, let me explain how this plugin works: since the (JDK) jlink tool doesn't allow non-modular dependencies, the plugin will try to collect all of them and create on the fly a module, that can be added to the module-path. This module is named $yourModuleName.mergedModule.

Now I'm adding your dependencies to a simple JavaFX project. When I run ./gradlew jlink I get the same errors you have posted. If you check the errors:

myapp\build\jlinkbase\tmpjars\myapp.merged.module\module-info.java:394: error

This shows that the created module is named myapp.merged.module, and that it has a module-info descriptor. If you open it you will see all the exports (the module will export every package of the dependencies by default), the requires and the provides:

open module myapp.merged.module {
    exports com.fasterxml.jackson.annotation;
    exports com.fasterxml.jackson.core;
    exports com.fasterxml.jackson.core.async;
    ...
    exports schemaorg_apache_xmlbeans.system.sXMLTOOLS;
    requires java.xml.crypto;
    requires java.logging;
    requires java.sql;
    requires java.xml;
    requires java.desktop;
    requires java.security.jgss;
    requires jdk.javadoc;
    uses java.nio.file.spi.FileSystemProvider;
    provides com.fasterxml.jackson.core.JsonFactory with com.fasterxml.jackson.core.JsonFactory;
    provides com.fasterxml.jackson.core.ObjectCodec with com.fasterxml.jackson.databind.ObjectMapper;
    provides org.apache.xmlbeans.impl.store.QueryDelegate.QueryInterface with org.apache.xmlbeans.impl.xquery.saxon.XBeansXQuery;
    provides org.apache.xmlbeans.impl.store.PathDelegate.SelectPathInterface with org.apache.xmlbeans.impl.xpath.saxon.XBeansXPath;
}

So now the question is: should you add some more requirements to your own module? The answer is explained here: that will increase unnecessary your project's size. A better way is to use the mergedModule script block:

The mergedModule block allows you to configure the module descriptor of the merged module

And:

the module descriptor is created using the algorithm implemented by the suggestMergedModuleInfo task.

If you run ./gradlew suggestMergedModuleInfo you will basically get the same contents for the merged module descriptor as above.

A possible solution is to start just adding some requires to the merged module descriptor, and try again.

I started with:

jlink {

    mergedModule {
        requires "java.xml"
    }

    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
    launcher {
        name = 'myapp'
    }
}

and I could run ./gradlew jlink successfully.

If you check the merged module descriptor now, it will look like:

open module myapp.merged.module {
    requires java.xml;

    exports com.fasterxml.jackson.annotation;
    exports com.fasterxml.jackson.core;
    exports com.fasterxml.jackson.core.async;
    ...
    exports schemaorg_apache_xmlbeans.system.sXMLTOOLS;
}

containing only the explicit requires, all the exports, but without provides, so the errors will be gone.

Since I don't have your code, I can't say if this will work for you, but it did for me with the same dependencies.

Upvotes: 9

Related Questions