2

I have the following build.gradle in my project

plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.oracle.database.jdbc:ojdbc11-production:21.3.0.0'
    implementation 'com.microsoft.sqlserver:mssql-jdbc:9.4.0.jre8'
}

tasks.named('jar') {
    setDuplicatesStrategy(DuplicatesStrategy.WARN)
    manifest {
        attributes 'Main-Class': 'com.example.App'
    }
    from {
        configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

File META-INF/services/java.sql.Driver is present in both of imported jars. Actually this Oracle JDBC dependency imports multiple jars and they have more conflicting files (with differing contents) among themselves (e.g. META-INF/native-image/native-image.properties and others in this directory).

when DuplicatesStrategy is set to WARN, the Oracle driver overwrites SQL Server JDBC driver's META-INF/services/java.sql.Driver. What are the consequences of this (and others) file being overwritten?

How should I handle these duplicate files? Is there any way to have all files from all jars? The chance of jar getting some of its files overwritten makes me feel uneasy.

Are conflicting files common?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
EMEM
  • 33
  • 6

1 Answers1

2

Consider not building a fatjar. Otherwise, you will need to merge META-INF/services/java.sql.Driver from the various drivers into a single file (either automatically, or provide your own). If you do not merge, automatic driver loading will not work for the driver(s) whose service definition file was not included. If you cannot or do not want to merge (or provide your own version), you will need to resort to explicitly loading drivers using Class.forName. See also How is driver class located in JDBC4.

As to the DuplicateStrategy to use, it is probably best to use FAIL and explicitly exclude files or locations, or come up with a strategy to merge them, but this might be cumbersome.

As to the other files, I cannot really answer, but META-INF/native-image/* seems to be related to using the library in GraalVM, so unless you're using that, you can probably safely ignore files in that directory.

However, if this really concerns you, or you don't know what files do, the better option is to not use fatjars (a.k.a uberjars), and instead use a way of deployment that correctly generates a class-path (e.g. using the manifest, or a launcher file) and keeps the various libraries used by your application separate. For example, see Add classpath in manifest using Gradle.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • Thanks for responding! It seems that loading the drivers isn't necessary to get a connection through DataSource so the lack of `META-INF/services/java.sql.Driver` is not an issue (neither is `META-INF/native-image/*`). `FAIL` + `exclude` indeed seems to be the least error prone strategy however licenses also get overwritten and they need to stay in the jar. Do you know of a way to specify separate DuplicateStrategy for a file? As for specifying the classpath in `MANIFEST.MF` the dependencies would need to be downloaded and zipped to some directory, right? – EMEM Oct 06 '21 at 20:52
  • 1
    @EMEM You're likely specifying the name of the driver as part of the data source configuration (or using a data source from the driver itself), so that's why automatic driver loading is not necessary. The downloading of the dependencies has already been done by Gradle, you'd just need to configure Gradle to copy them to the right location. As to to specifying a duplicate strategy per file, I'm not aware of such option, but it can probably be achieved with multiple copy tasks. – Mark Rotteveel Oct 07 '21 at 07:48
  • I am indeed using a datasource from the driver. – EMEM Oct 07 '21 at 09:18