Handling duplicate entries in shaded (uber/fat) jar file - java

I created a jar file, created from multiple jars (I used Gradle Shadow plugin in this case).
It turned out that the file contains duplicate files in /META-INF. Most of them are multiple license.txt from the original jar files, but some of them are Spring factories, which I'm interested in.
This behaviour seems common when using various fatjar-like tools with Maven or Gradle, and it looks like it is not prohibited by any means.
I've checked that java.lang.ClassLoader#getResources will return these entries as a list of the same URLs, eg.:
jar:file:/C:/temp/myJar.jar!/META-INF/spring.factories
jar:file:/C:/temp/myJar.jar!/META-INF/spring.factories
so once I get the URL, there's no way back to getting the contents of all duplicated files, programmatically.
Can Java process such duplicate files, somehow?
Or, maybe more general, is it a valid and supported situation, from specs' point of view, to have a jar with duplicate files inside? I've checked Jar File Specification and it didn't give me a clue, either.

In maven-shade-plugin this is done with an AppendingTransformer.
My interpretation of the gradle plugin documentation you are using is that this is done this way:
shadowJar {
append('META-INF/spring.factories')
}

Related

Replace properties in jar

I'm using Springboot and would like to replace the property values in the org.apache.catalina.util ServerInfo.properties file when building the project. The file is buried in a dependent jar: tomcat-embed-core-8.5.2.3.jar. So my app jar will have the replaced values but the tomcat-embed-core-8.5.2.3.jar in the repos will remain as is.
I looked at Maven filters but that looks like it only works if the property file is reachable within my project only (like in src/main/resources), not if the file is in another jar. Is that right?
What's the best way to do this? A Maven solution or something simpler is okay.

How to use jardesc in Eclipse?

I want to create an executable JAR-file from my eclipse project. It references other projects, which also reference other projects (and so on) and some JAR files.
According to this answer, everything should go "within 2 clicks". But not for me:
It reports a warning:
Problem writing mg/build/classes/META-INF/MANIFEST.MF to JAR: duplicate entry: META-INF/MANIFEST.MF duplicate entry: META-INF/MANIFEST.MF
It looks like it tries to include existing manifests from all projects, which simply doesn't make sense. I actually do not want to include any of them, just to generate a new one. I haven't found any way to switch it off. OK, it's just a warning.
It looks like I need to manually take care of all the referenced jar files... actually, Eclipse knows them, I do not.
I need to either include the content of all the referenced jar files or to copy all of them into the target folder and list them in the Manifest. I've got no idea how to do it.
I also wonder if the jardesc file is usable from ant build scripts.
I got the same error message ("duplicate entry") when, in my case
I checked the option to include an existing manifest file.
AND I specified to include MANIFEST.MF among the files to be included in the .jar.
Once I excluded MANIFEST.MF in the "Select the resources to export:" file tree of the jar generation wizard, the warning disappeared.
As for including referenced projects with your jar, I recommend making them into jar files as well, and including those in the project you want to make a jar of.
Then, make sure to set the manifest's classpath correctly.

Problems including jar files outside the jar file containing the manifest

Basically let me first explain what I am aiming to do. I have a dynamic ETL transformer app written in JAVA. Due to the dynamic nature of this app I have to be able to add plugins jars to the app in a location outside of the apps jar file.
Basically would like to have the following directory structure:
AppFolder
|- plugins/
|- configs/
|- mainApp.jar
If possible I would like to be able to use wildcards in my manifest to dynamically add jars located in the plugins folder.
Unfortunately all I have tried so far has failed. I have tried to use both relative paths and absolute paths neither have worked (with or without wildcard).
If I however include the plugins folder in the main app's jar file itself it works fine given that I don't use wildcards.
So my question is, is it actually possible to have dependencies outside of a jar or do they always have to be contained within.
The other question is regarding the usage of wildcards. i have looked at [the java documentation] (http://java.sun.com/javase/6/docs/technotes/tools/windows/classpath.html) to no prevail unfortunately.
some examples of what I have tried so far:
../plugins/*
../plugins/plugin.jar
/abolute/path/to/plugins/*
/abolute/path/to/plugins/plugin.jar
and unfortunately none of them have done the trick so any help would be very much appreciated...
Yes you can have dependencies outside the jar. But wildcards are not supported for specify dependant jars.
The jars need to be explicitly specified in your manifest, and the location needs to be relative to where the application is run from
A better option for you may be to use the Extension Mechanism
java -Djava.ext.dirs=/abolute/path/to/plugins/ ......
If you have control of the code you could always add a JarClassLoader and load the jars dynamically.
http://java.sun.com/docs/books/tutorial/deployment/jar/jarclassloader.html

Managing Data Dependecies of Java Classes that Load Data from the Classpath at Runtime

What is the simplest way to manage dependencies of Java classes to data files present in the classpath?
More specifically:
How should data dependencies be annotated? Perhaps using Java annotations (e.g., #Data)? Or rather some build entries in a build script or a properties file? Is there build tool that integrates and evaluates such information (Ant, Scons, ...)? Do you have examples?
Consider the following scenario:
A few lines of Ant create a Jar from my sources that includes everything found on the classpath. Then jarjar is used to remove all .class files that are not necessary to execute, say, class Foo. The problem is that all the data files that class Bar depends upon are still there in the Jar. The ideal deployment script, however, would recognize that the data files on which only class Bar depends can be removed while data files on which class Foo depends must be retained.
Any hints?
This is one of the many problems Maven has already solved with it's build, dependency, and resource management. Any maven project follows a standard directory layout which dictates where you should put your Data files: in the 'resources' directories. The conventional Maven directory structure is as follows...
/
/src/
/src/main/java/
/src/main/java/App.java
/src/main/resources/
/src/main/resources/my.prod.data.or.cfg.or.whatever
/src/test/java/
/src/test/java/AppTest.java
/src/test/resources/
/src/test/resources/my.test.data.or.cfg.or.whatever
/pom.xml
The benefit of this is that all files which are contained in the 'main' (prod) resources directories are available to your application at run-time from the Classpath. All of the 'test/resources' files are available to your code during build & unit test time but are NOT included in your final artifact.
I don't think a generic solution exists for the system you describe, however, I just had a stab at reading annotations on classes using ASM, since that is used by jarjar as well. It is not hard to read the annotation data that way (pass in a ClassVisitor to the accept() method on ClassReader, and do something useful on the visitAnnotation callback). This means you can either try and include your intended behavior to jarjar or you could add it as a custom step to your build process.
Can't you refactor your project so that you have submodules that each contain the relevant files for the project itself ; Bar class and Bar related files will be packaged in their bundle while Foo ones will packed into another?
Another possibility would be to use some package naming convention to be able to filter the files you want to see i your bundles.

Can you add multiple jars in a jar file and then launch that jar file

I am working on a GUI application and would rather distribute just one jar as opposed to multiple ones.
Can you control this with the manifest.
Another option is to use a custom class loader such as this one:
http://one-jar.sourceforge.net/
Merge your jars to one jar. See this thread.
You need to be careful when doing this. If the jars you are merging have manifest files with critical information - these can get lost, only the last file will get merged.
E.g. If you merge JavaMail - the manifest file is important. If you lose it - bad things csn happen.
The safest thing to do is to look at each jar file and check the manifest file.
Have a look here. Use Netbeans 6.7.1 to combine multiple jars into 1 jar
Create a Shaded jar which combines multiple jars into a single jar. Maven supports this functionality out of the box.
https://maven.apache.org/plugins/maven-shade-plugin/

Categories