DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report

jar-with-deps don’t like META-INF/services

Andrew Phillips user avatar by
Andrew Phillips
·
Jul. 21, 11 · Interview
Like (0)
Save
Tweet
Share
6.01K Views

Join the DZone community and get the full member experience.

Join For Free

recently, i was preparing a connection checker for deployit’s powerful remote execution framework overthere . to make the checker, as compact as possible, i put together a jar-with-deps 1 for distribution.
tests and trial runs from the ide worked, so i was expected the dry-run of the distribution to be a quick formality. instead: boom!
turns out that one of the libraries used by overthere, truezip – or indeed any code that utilizes java’s spi mechanism 2 – doesn’t play well with the jar-with-deps idea.

seeing double

truezip uses a de.schlichtherle.truezip.fs.spi.fsdriverservice provider configuration file in meta-inf/services to register drivers for various archive formats, and the checker was failing because one of the required drivers wasn’t being loaded, even though the code was correctly included in the jar-with-deps .

the duplicate option of ant’s jar task 3 and gradle issue 1050 , which talks about merging files during archive creation, hint at what the problem is: once packaged up in a single jar-with-deps archive, only one of truezip’s provider configuration files was being found.

maven, gradle and java

once i examined the jar-with-deps constructed by maven 4 , it was pretty clear why: the jar only contains one of the configuration files – looks like the first one encountered in my case, but that may be non-deterministic. presumably, this happens because maven uses a temporary directory to prepare the archive contents, which of course can’t hold multiple files of the same name.

with gradle 5 , things are a bit more interesting because the archive does indeed contain all the configuration files – the zip format supports duplicate entries 6 . however, classloader.getresources doesn’t play along, so again only one entry is found.

there can be only one

basically, it seems that merging the affected files is the only feasible way around this. here’s a possible gradle snippet 7 :

task jarwithdeps(type: jar, dependson: classes) {
    mergedir = "${builddir}/merge"
    // might need a lazy var in a multi-module project where deps are inherited
    runtimedeps = configurations.runtime.collect { ziptree(it) }
 
    dofirst {
        new file(mergedir).delete()
        mergefiles(mergedir, runtimedeps, file-to-merge)
        // ... possibly other files too
    }
 
    // this project's classes and all deps
    from sourcesets*.classesdir
    from(runtimedeps) {
        exclude file-to-merge
    }
    from mergedir
}
 
private def mergefiles(targetdir, filetrees, relativepath) {
  // prepare the merge
  mergedfile = new file("${targetdir}/${relativepath}")
  new file(mergedfile.parent).mkdirs()
 
  filetrees*.matching({ include "**/${relativepath}" })*.each {
    mergedfile << it.bytes
  }
}

unfortunately, i haven’t been able to find a similar option for the maven archiver , but i guess there must be something out there in the maven ecosystem ;-)

footnotes
  1. a.k.a. “farjar”. for an equivalent gradle task definition, see here .
  2. see multispi for an approach that provides more flexibility and “modern alternatives” for service providers.
  3. note that the jar utility itself doesn’t provide a similar option.
  4. run mvn clean package
  5. run gradle clean jarwithdeps
  6. although the zipoutputstream evidently wasn’t too happy with them and, judging by the 1.6.0_20 implementation, will still throw a zipexception
  7. would be interesting to try to do the merging at jar construction time, perhaps by keeping “merge-so-far(s)” in the mergedir and using eachfile to replace the unmerged files and update the “merge-so-far(s)” at the same time.

from http://blog.xebia.com/2011/07/jar-with-deps-dont-like-meta-infservices/

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Full Lifecycle API Management Is Dead
  • Building Microservice in Golang
  • Stop Using Spring Profiles Per Environment
  • Custom Validators in Quarkus

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: