Lukasz Klosinski
Lukasz Klosinski

Reputation: 13

Gradle -> Geotools -> FatJar -> missing classes

I have a project that I build FatJar from. Recently I have included additional dependencies from geotools. What I quickly discovered is that due to the geotools architecture I need to merge the META-INF/services, which I do using the mergeServiceFiles() in my shadowJar task. However, even with the mergeServiceFiles I still get an error: WARNING: Can't load a service for category "Operation". Cause is "ServiceConfigurationError: org.geotools.api.coverage.processing.Operation: Provider org.geotools.coverage.processing.operation.ShadedRelief could not be instantiated".

Both, org.geotools.api.coverage.processing.Operation and the org.geotools.coverage.processing.operation.ShadedRelief are present in my dependencies (gt-api and gt-coverage) and are also included in the services.

Also, after inspecting the generated FatJar I can see that classes org.geotools.api.coverage.processing.Operation and org.geotools.coverage.processing.operation.ShadedRelief are present in the jar and org.geotools.api.coverage.processing.Operation is present in the META-INF/services and it does contain org.geotools.coverage.processing.operation.ShadedRelief.

My Gradle settings is in line with the GeoTools for creating FatJars FAQ instruction - I am using Gradle's shadowJar plugin with the mergeServiceFiles task which is equivalent of the Maven's shadow plugin with the ServicesResourceTransformer:

plugins {
    id("com.gradleup.shadow") version "8.3.0"
    java
}

group = "test.it"
version = "0.0.1"

application {
    mainClass.set("test.it.ApplicationKt")
}

repositories {
    maven { url = uri("https://repo.osgeo.org/repository/release/") }
    mavenCentral()
}

dependencies {
    implementation("org.geotools:gt-coverage:31.2")
    implementation("org.geotools:gt-main:31.2")
    implementation("org.geotools:gt-api:31.2")
    implementation("org.geotools:gt-geotiff:31.2")
    implementation("org.geotools:gt-tile-client:31.2")
    implementation("org.geotools.xsd:gt-xsd-kml:31.2")
    implementation("org.geotools:gt-epsg-hsql:31.2")
    implementation("org.osgeo:proj4j:0.1.0")

}

tasks {
    shadowJar {
        mergeServiceFiles()
        isZip64 = true
        archiveFileName.set("${project.name}-${project.version}.jar")
        manifest {
            attributes["Main-Class"] = "test.it.ApplicationKt"
            //fix for the imageio vendor!=null error
            //https://stackoverflow.com/questions/7051603/jai-vendorname-null/18495658#18495658
            attributes["Specification-Title"] = "Java Advanced Imaging Image I/O Tools"
            attributes["Specification-Version"] = version
            attributes["Specification-Vendor"] = "Sun Microsystems, Inc."
            attributes["Implementation-Title"] = "com.sun.media.imageio"
            attributes["Implementation-Version"] = version
            attributes["Implementation-Vendor"] = "Sun Microsystems, Inc."
        }
    }
}

I tried (with no luck):

It works fine when run with the -classpath including locations of the gt-api and gt-coverage jars.

UPADATE 12/09/2024

A temporary solution - adding exclude("META-INF/services/org.geotools.api.coverage.processing.*") to the shadowJar task helps. This resolves the issue for that particular missing Class throwing the error.

Upvotes: 0

Views: 74

Answers (1)

Ian Turton
Ian Turton

Reputation: 10976

As described in the FAQ, you need to take care when combining modules with META-INF/services directories containing files of the same name. The recommendation is to use the Maven shade plugin, as far as I know none of the GeoTools developers uses gradle but I would expect there to be an equivalent plugin that can combine files rather than overwriting them.

If you work it out then could you add the information to the GeoTools FAQ for the benefit of others.

Upvotes: 0

Related Questions