Spring Boot add files to classpath from command line execution - java

I am using Netbeans 8.2 to develop Spring applications. This specific app with which I am having trouble is a Spring Boot 1.5.3 app. I have a spring xml file and an application.properties that I keep in /config under the root project directory.
I am passing the spring xml file to my project via the #ImportResource annotation and a value property like #ImportResource(value="${config.xmlfile}").
When I click the 'Run Project' button in Netbeans my Spring app starts up and it correctly finds the application.properties file in my /config folder. However, any classpath references to other files in that folder are lost. For example, setting the config.xml file to classpath:config/file.xml or classpath:file.xml both fail to find the file but file:config/file.xml works.
Similarly, when running from the command line I have the following as my structure:
app/
|-- bin
| `-- app-run.sh
|-- config
| |-- application.properties
| |-- log4j2.xml
| |-- file.xml
`-- app-exec.jar
I am using the spring-boot-maven-plugin to make the jar as follows:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
</plugin>
and my app-run.sh script executes the following:
exec /bin/java -cp :.:../config/*:../app-exec.jar
-Dlogging.config=../config/log4j2.xml
-Dspring.config.location=../config/application.properties
-jar ../app-exec.jar
where /bin/java represents the location where I have java installed. The classpath set in -cp does not seem to be working here. Similarly to when running through the IDE, setting the config.xml file to classpath:config/file.xml or classpath:file.xml both fail to find the file but file:../config/file.xml works.
I would like to be able to set the classpath in both the IDE and from command line so that I can access files in Spring using classpath reference to make things easier. I do NOT want to put them all in src/main/resources and have them be packaged in the jar, as I need to edit these after packaging and deployment.
Does anybody have any ideas or helpful hints? Thanks in advance!

Updated answer:
You can follow the practice in my original answer, but we recently dropped this for a simpler and cleaner option that is more standard (the "Java" way). We made the change because we needed to dynamically load dependent libraries at runtime that were not available at compile time (in their exact version). In our case we wanted to load dependent jars only from separate folder(s) and not from an executable jar. We ended up having duplicate dependencies in the executable jars and in separate folder(s), so we decided to drop the executable jar Properties Launcher and instead only load dependencies from separate folders. This is often NOT the best option and should be evaluated for your use case. I prefer reading the standard Java classpath.
To run a Spring Boot app without an executable jar, we used Maven Assembly to put the dependent jars in a /libs directory and dropped the spring-boot-maven-plugin. The steps and some code for this are below:
Remove the spring-boot-maven-plugin that creates the executable jar in ZIP format
Add the following to your assembly XML
<dependencySets>
<dependencySet>
<outputDirectory>where you want the libs to go</outputDirectory>
<useProjectArtifact>whether you want to include the project
artifact here</useProjectArtifact>
</dependencySet>
</dependencySets>
Run your code from the main class and include the dependent jar folder(s) on the classpath. Use the standard classpath notation on your OS and not the custom, awkward PropertiesLauncher loader path syntax
java -cp <standard-classpath> <main-class>
An example of an actual call:
java -cp $CLASSPATH:./lib/*:./cfg/*:my-app.jar Application.class
In this way you execute the Spring Boot app via standard java execution call, no custom Spring loading syntax. You just need to ensure that all of your dependencies are available on the classpath at runtime. We found this much easier to maintain and made this the standard for all of our apps.
Original answer:
After some researching, and thanks to #TuyenNguyen's helpful answer I was able to get the following working:
I added the following to my spring-boot-maven-plugin so that when I run from the command line it uses the PropertiesLauncher instead of the JarLauncher:
<configuration>
<mainClass>${mainClass}</mainClass>
<layout>ZIP</layout> //THIS IS THE IMPORTANT PART
</configuration>
See here and here for more about the PropertiesLauncher options. It allows you to set the classpath, among other things.
See here, here, and here for where I found the answer to this problem. Using format ZIP makes the PropertiesLauncher be used.
From there, I was able to use this command to launch the application as I intended:
java -Dloader.path=../config,../ -Dloader.config.location=classpath:application.properties -jar ../app-exec.jar
Another important note: when specifying the -Dloader.path make sure to use comma-separated values and only directories and files, as described here. Also, be sure to put the -D args before you specify -jar jar or they will not be set.
If anyone has any suggestions or edits to further improve this answer or the original question in order to help additional users, please let me know or make the edits yourself!

If you don't put your files in src/main/resources then you can put it inside any folder that you want, BUT you must set your folder as a resources folder. Because classpath is always point to resources folder. Once you make your folder as a resource folder, it will be packaged into the jar. If you want to edit your resource file, just using 7 zip tool to open your jar -> edit files -> save -> it will update your change in the jar.
Another solution is create a folder, put all files you want to edit and not packaged in that, then set classpath manually to that folder every time you run, but the way you set above is not correct, try this solution for set classpath correct way.

Related

exclude file from maven's resource directory

I am using maven for my spring boot application(1.5 version). There are some files in src/main/resources like abc.properties, app.json. Below are some pointer what i want to achieve.
Exclude these files getting into the jar.
When i run my application through intellij these files should be available in classpath.
I looked at related answers on SO but none matches my case. Any suggestion?
you can use the resouce tag in maven pom file:
<resources>
<resource>
<directory>[your directory]</directory>
<excludes>
<exclude>[non-resource file #1]</exclude>
<exclude>[non-resource file #2]</exclude>
<exclude>[non-resource file #3]</exclude>
...
<exclude>[non-resource file #n]</exclude>
</excludes>
</resource>
...
</resources>
For more informations see: https://maven.apache.org/plugins/maven-resources-plugin/examples/include-exclude.html
My understanding is
You want some config file, that is available in classpath during runtime
Such config file will be changed based on environment
The way I used to do is:
Create a separate directory for such kind of resources, e.g. src/main/appconfig/
Do NOT include this in POM's resources (i.e. they are not included in resulting JAR)
In IDE, add this directory manually as source folder (I usually put this in testResource in POM. When I use Eclipse + M2E, testResources will be added as source folder. Not sure how IntelliJ though)
For point 2, I used to do it slightly differently. Instead of excluding, I will include in the result JAR but in a separate directory (e.g. appconfig-template), so that people using the application can take this as reference to configure their environment.
An alternative of 3 is: create a separate profile which include appconfig as resource. Use this profile only for your IDE setup but not building your artifact.

Translate jardesc content into jar-command

I am making some WebFilters for our WebLogic server and I've got everything up and running.
The problem is how I package the filters into jar-files. If I use the following jardesc-file and create the jar-file from Eclipse, everything is working fine and WebLogic has no problems loading the webfilter class. But as soon as I try to manually create the jar-file using just jar.exe I am hitting ClassNotFoundExceptions when loading the webcontainer in WebLogic.
Working jardesc-file:
<?xml version="1.0" encoding="WINDOWS-1252" standalone="no"?>
<jardesc>
<jar path="C:/Workspace/Java/Jars/jars/corsfilter.jar"/>
<options buildIfNeeded="true" compress="true" descriptionLocation="/CorsFilter/corsfilter.jardesc" exportErrors="false" exportWarnings="true" includeDirectoryEntries="false" overwrite="false" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
<storedRefactorings deprecationInfo="true" structuralOnly="false"/>
<selectedProjects/>
<manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
<sealing sealJar="false">
<packagesToSeal/>
<packagesToUnSeal/>
</sealing>
</manifest>
<selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
<file path="/CorsFilter/.project"/>
<file path="/CorsFilter/.classpath"/>
<javaElement handleIdentifier="=CorsFilter/src"/>
</selectedElements>
</jardesc>
Current jar-command:
jar -cvf corsfilter.jar .project .classpath -C .\bin dk\akait\filters\cors\CorsFilter.class
The jar-command creates a jar-file that seems to be equivalent to the one generated using the jardesc-file in Eclipse, except for what looks like some kind of symbolic link to the META-INF-folder in the root of the jar-file.
Content of working jar-file:
Content of non-working jar-file
Can anyone explain what the right command for executing jar.exe is, given the jardesc-file?
Or
Can anyone explain what the META-INF file in the not working jar-file is?
Updated with output of jar-command run using jdk-1.8.0_111
As already mentioned in the comments. I would think this is more like a problem with WinRar as with the actually created jar-file. Probably eclipse doesn't use the jar command internally to create jar-files based on the jardesc descriptions. And the jar-files differ somehow in their internal structure.
However I'd suggest not to rely on the eclipse output, if you want to create a jar-file that you are going to distribute somewhere. I personally like gradle a lot and it is pretty easy to use.
Simply create a build.gradle file in you project root directory with the following content:
apply plugin: 'java'
// this is only needed, if you want to include the single file only
// by default all compiled files will be added to the output jar
jar {
include "dk/akait/filters/cors/CorsFilter.class"
}
task wrapper(type: Wrapper) {
gradleVersion = '3.3'
}
Run
%PATH_TO_YOUR_GRADLE_BINARIES%/gradle wrapper
This will generate a local wrapper bat file that is used to ensure you are using the desired gradle version.
Run
%PROJECT_ROOT%/gradlew.bat build
and locate the jar-file under
%PROJECT_ROOT%/build/libs
You will however need to follow some conventions and place your java sources under a folder called src/main/java for this to work out-of-the-box. Or follow this documentation to setup different source folders.
I don't think your command line is even correct.
As according to jar output
Usage: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point]
[-C dir] files ...
And also http://docs.oracle.com/javase/7/docs/technotes/tools/windows/jar.html
It is expect to be [manifest-file] and [entry-point] after [jar-file] name.
Try
jar -cvf corsfilter.jar -C . .project .classpath -C .\bin dk\akait\filters\cors\CorsFilter.class

Using Maven for modifying an archive

Please help me with evaluation whether maven could be used for a problem I am faced with. Hopefully you can give me some hints on maven or suggest using a different tool.
Problem:
We have a template archive containing resources. We need to create a new archive based on the template using custom files/data.
some files from the template have to be replaced with custom ones.
some files have to be processed and placeholders in these files have to be replaced with custom variables. (i.e ${filename} -> "my_file_name.txt")
The pom file should describe which files should be replaced and where custom files should be taken from. Also it should describe which files to be processed by place-holder processor and where to get the variables values.
It is possible to write a java program to do all these stuffs, but Java code must be changed again and again for a different set of custom files and new placeholders. Hopefully with help of maven (also custom plugins if required) this task could be a simple configuration task.
Example:
Template folder:
/src/main/resources/file1.xml
/src/main/resources/file2.xml
/META-INF/MANIFEST.MF
file2.xml:
<file>${file_name}</file>
Custom folder:
/src/main/resources/file1.xml
/src/main/resources2/file3.xml
Custom Property File:
file_name=my_file_name.xml
Result:
/src/main/resources/file1.xml
/src/main/resources/file2.xml
/src/main/resources2/file3.xml
/META-INF/MANIFEST.MF
file2.xml:
<file>my_file_name.xml</file>
Questions:
How to put the unmodified MANIFEST.MF into target folder.
I tried to use
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
<manifestFile>src/main/resources/template/META-INF/MANIFEST.MF</manifestFile>
</archive>
but it simply disables 'maven' part of the META-INF. The manifiest.mf was still generated by maven
How to implement placeholder/variable processor?
How to zip the resulting set of files into .zip file?
How to invoke a maven build from a java class?
Is there an alternative tool which might be more suitable for my task?
regards,
ilia
That's a lot of questions, but I'll try to answer:
Move the META-INF/MANIFEST.MF to /src/main/resources/
It is already implemented on http://maven.apache.org/plugins/maven-resources-plugin/ for replacing properties on resources files with ${propertyName}
Zips can be created with http://maven.apache.org/plugins/maven-assembly-plugin/
Don't do that, Maven is invoked from command line, IDE or continuous integration server.
On tool would be Gradle, or the older Ant for naming some of them.

How do I run a class in a WAR from the command line?

I have a Java class which has a main and I used to run as a standalone app from the command line e.g.
java -jar myjar.jar params
I needed to repackage the code to run under apache and all my code, including the entry point class from the old jar, has ended up in a WAR file for easy deplyment into the web server.
However, I still want to be able to run it from the command line and the code has not changed and is all in there, I just can't figure out how to get it to run.
Here's what I tried...
I presumed the WAR was just like a jar, so
java -jar mywar.war params
That failed saying there was no main class defined in the manifest.
I manually added a manifest to the war and tried again, with the same effect.
I noticed that in my war I had a folder called META-INF containing a manifest.mf, so I added a line to that declaring my main class as I would to a normal manifest...
Manifest-Version: 1.0
Main-Class: mypackage.MyEntryPointClass
This gave a noClassDefFoundError mypackage.MyEntryPointClass, which is progress of a sort. That led me to believe that it was just a path issue, so I tried
Manifest-Version: 1.0
Main-Class: WEB-INF.classes.mypackage.MyEntryPointClass
I now get the same error, but with a stack trace...
Exception in thread "main" java.lang.NoClassDefFoundError: WEB-INF/classes/mypackage/MyEntryPointClass (wrong name: mypackage/MyEntryPointClass)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access$100(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClassInternal(Unknown Source)
I've googled for a bit but can't find anything which answers my question, and I read a couple of other questions here which are slightly different, so I thought I would post.
Java 1.5, not that I think that should make any difference.
Similar to what Richard Detsch but with a bit easier to follow (works with packages as well)
Step 1: Unwrap the War file.
jar -xvf MyWar.war
Step 2: move into the directory
cd WEB-INF
Step 3: Run your main with all dependendecies
java -classpath "lib/*:classes/." my.packages.destination.FileToRun
You can do what Hudson (continuous integration project) does.
you download a war which can be deployed in tomcat or to execute using
java -jar hudson.war
(Because it has an embedded Jetty engine, running it from command line cause a server to be launched.) Anyway by looking at hudson's manifest I understand that they put a Main class in the root for the archive. In your case your war layout should be look like:
under root:
mypackage/MyEntryPointClass.class
WEB-INF/lib
WEB-INF/classes
META-INF/MANIFEST.MF
while the manifest should include the following line:
Main-Class: mypackage.MyEntryPointClass
please notice that the mypackage/MyEntryPointClass.class is accessable from the command line only, and the classes under WEB-INF/classes are accessable from the application server only.
HTH
A war is a webapp. If you want to have a console/standalone application reusing the same classes as you webapp, consider packaging your shared classes in a jar, which you can put in WEB-INF/lib. Then use that jar from the command line.
Thus you get both your console application, and you can use the same classes in your servlets, without making two different packages.
This, of course, is true when the war is exploded.
If you're using Maven, just follow the maven-war-plugin documentation about "How do I create a JAR containing the classes in my webapp?": add <attachClasses>true</attachClasses> to the <configuration> of the plugin:
<project>
...
<artifactId>mywebapp</artifactId>
<version>1.0-SNAPSHOT</version>
...
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<attachClasses>true</attachClasses>
</configuration>
</plugin>
</plugins>
</build>
...
</project>
The you will have 2 products in the target/ folder:
The project.war itself
The project-classes.jar which contains all the compiled classes in a jar
Then you will be able to execute a main class using classic method: java -cp target/project-classes.jar 'com.mycompany.MainClass' param1 param2
To execute SomeClass.main(String [] args) from a deployed war file do:
Write class SomeClass.java that has a main method method i.e. (public static void main(String[] args) {...})
Deploy your WAR
cd /usr/local/<yourprojectsname>/tomcat/webapps/projectName/WEB-INF
java -cp "lib/jar1.jar:lib/jar2.jar: ... :lib/jarn.jar" com.mypackage.SomeClass arg1 arg2 ... arg3
Note1: to see if the class SomeOtherClass.class is in /usr/tomcat/webapps/<projectName>/WEB-INF/lib run:
cd /usr/tomcat/webapps/projectName/WEB-INF/lib &&
find . -name '*.jar' | while read jarfile; do if jar tf "$jarfile" | grep SomeOtherClass.class; then echo "$jarfile"; fi; done
Note2: Write to standard out so you can see if your main actually works via print statements to the console. This is called a back door.
Note3: The comment above by Bozhidar Bozhanov seems correct
The rules of locating classes in an archive file is that the location of the file's package declaration and the location of the file within the archive have to match. Since your class is located in WEB-INF/classes, it thinks the class is not valid to run in the current context.
The only way you can do what you're asking is to repackage the war so the .class file resides in the mypackage directory in the root of the archive rather than the WEB-INF/classes directory. However, if you do that you won't be able to access the file from any of your web classes anymore.
If you want to reuse this class in both the war and outside from the java command line, consider building an executable jar you can run from the command line, then putting that jar in the war file's WEB-INF/lib directory.
In Maven project, You can build jar automatically using Maven War plugin by setting archiveClasses to true. Example below.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<archiveClasses>true</archiveClasses>
</configuration>
</plugin>
the best way if you use Spring Boot is :
1/ Create a ServletInitializer extends SpringBootServletInitializer Class
. With method configure which run your Application Class
2/ Generate always a maven install WAR file
3/ With this artefact you can even :
. start application from war file with java -jar file.war
. put your war file in your favorite Web App server (like tomcat, ...)
Well, according to Wikipedia, with a WAR file, the classes that get loaded into the classpath are in the "/WEB-INF/classes" and "/WEB-INF/lib" directory.
You could try simply putting a copy of the classes on the root file system of the zip file (which is what a war/jar is). I'm not sure if that would work though.
You can always just create two separate files.
As an alternative option, include into the war file, the rest service to trigger the application logic via url. Deploy the war file onto any web/application server you want.
Then you can start your application via any command-line based HTTP client like curl on Linux.
The disadvantage: usually these HTTP clients are different on different OSs. Which is not critical for a lot of cases. Also you can install curl on Windows.
It's not possible to run a java class from a WAR file. WAR files have a different structure to Jar files.
To find the related java classes, export (preferred way to use ant) them as Jar put it in your web app lib.
Then you can use the jar file as normal to run java program. The same jar was also referred in web app (if you put this jar in web app lib)

Classpath including JAR within a JAR

Is it possible to specify a Java classpath that includes a JAR file contained within another JAR file?
If you're trying to create a single jar that contains your application and its required libraries, there are two ways (that I know of) to do that. The first is One-Jar, which uses a special classloader to allow the nesting of jars. The second is UberJar, (or Shade), which explodes the included libraries and puts all the classes in the top-level jar.
I should also mention that UberJar and Shade are plugins for Maven1 and Maven2 respectively. As mentioned below, you can also use the assembly plugin (which in reality is much more powerful, but much harder to properly configure).
You do NOT want to use those "explode JAR contents" solutions. They definitely make it harder to see stuff (since everything is exploded at the same level). Furthermore, there could be naming conflicts (should not happen if people use proper packages, but you cannot always control this).
The feature that you want is one of the top 25 Sun RFEs: RFE 4648386, which Sun, in their infinite wisdom, has designated as being of low priority. We can only hope that Sun wakes up...
In the meanwhile, the best solution that I have come across (which I wish that Sun would copy in the JDK) is to use the custom class loader JarClassLoader.
After some research I have found method that doesn't require maven or any 3rd party extension/program.
You can use "Class-Path" in your manifest file.
For example:
Create manifest file MANIFEST.MF
Manifest-Version: 1.0
Created-By: Bundle
Class-Path: ./custom_lib.jar
Main-Class: YourMainClass
Compile all your classes and run jar cfm Testing.jar MANIFEST.MF *.class custom_lib.jar
c stands for create archive
f indicates that you want to specify file
v is for verbose input
m means that we will pass custom manifest file
Be sure that you included lib in jar package. You should be able to run jar in the normal way.
based on: http://www.ibm.com/developerworks/library/j-5things6/
all other information you need about the class-path do you find here
Use the zipgroupfileset tag (uses same attributes as a fileset tag); it will unzip all files in the directory and add to your new archive file.
More information: http://ant.apache.org/manual/Tasks/zip.html
This is a very useful way to get around the jar-in-a-jar problem -- I know because I have googled this exact StackOverflow question while trying to figure out what to do. If you want to package a jar or a folder of jars into your one built jar with Ant, then forget about all this classpath or third-party plugin stuff, all you gotta do is this (in Ant):
<jar destfile="your.jar" basedir="java/dir">
...
<zipgroupfileset dir="dir/of/jars" />
</jar>
If you are building with ant (I am using ant from eclipse), you can just add the extra jar files
by saying to ant to add them...
Not necessarily the best method if you have a project maintained by multiple people but it works for one person project and is easy.
for example my target that was building the .jar file was:
<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
<manifest>
<attribute name="Author" value="ntg"/>
................................
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
</jar>
I just added one line to make it:
<jar ....">
<zipgroupfileset dir="${external-lib-dir}" includes="*.jar"/>
<manifest>
................................
</manifest>
</jar>
where
<property name="external-lib-dir"
value="C:\...\eclipseWorkspace\Filter\external\...\lib" />
was the dir with the external jars.
And that's it...
Not without writing your own class loader. You can add jars to the jar's classpath, but they must be co-located, not contained in the main jar.
You need to build a custom class-loader to do this or a third-party library that supports this. Your best bet is to extract the jar from the runtime and add them to the classpath (or have them already added to the classpath).
I use maven for my java builds which has a plugin called the maven assembly plugin.
It does what your asking, but like some of the other suggestions describe - essentially exploding all the dependent jars and recombining them into a single jar
If you have eclpise IDE, you just need to export your JAR and choose "Package Required libraries into generated JAR". eclipse will automatically add the required dependant JARs into the generated JAR as well as generated some eclipse custom class loader that load these JARs automatically.
I was about to advise to extract all the files at the same level, then to make a jar out of the result, since the package system should keep them neatly separated.
That would be the manual way, I suppose the tools indicated by Steve will do that nicely.
Winstone is pretty good http://blog.jayway.com/2008/11/28/executable-war-with-winstone-maven-plugin/. But not for complex sites. And that's a shame because all it takes is to include the plugin.
Well, there is a very easy way if you're using Eclipse.
Export your project as a "Runnable" Jar file (right-click project folder from within Eclipse, select "Export..."). When you configure the export settings, be sure to select "Extract required libraries into generated Jar." Keep in mind, select "Extract..." and not "Package required libraries...".
Additionally: You must select a run-configuration in your export settings. So, you could always create an empty main( ) in some class and use it for your run configuration.
Anyway, it isn't guaranteed to work 100% of the time - as you will notice a pop-up message telling you to make sure you check the licenses of the Jar files you're including and something about not copying signature files. However, I have been doing this for years and have never encountered a problem.
Extracting into an Uber-dir works for me as we s should all be using root:\java and have outlets code in packages with versioning. Ie ca.tecreations-1.0.0. Signing is okay because the jars are intact from their downloaded location. 3rd party signatures intact, extract to c:\java. There’s my project dir. run from launcher so java -cp c:\java Launcher
In case you are using Spring Boot, you may want to have a look at this documentation: The Executable Jar Format
Java does not provide any standard way to load nested jar files (that
is, jar files that are themselves contained within a jar). This can be
problematic if you need to distribute a self-contained application
that can be run from the command line without unpacking.
To solve this problem, many developers use “shaded” jars. A shaded jar
packages all classes, from all jars, into a single “uber jar”. The
problem with shaded jars is that it becomes hard to see which
libraries are actually in your application. It can also be problematic
if the same filename is used (but with different content) in multiple
jars. Spring Boot takes a different approach and lets you actually
nest jars directly.
The Spring documentation also lists some alternative single Jar solutions:
Apache Maven Shade Plugin
JDotSoft JarClassLoader
One-JAR
Shadow Plugin (Gradle)
I would advise to use one jar and many libraries in separate jars, not in a single jar. Use separate jar from jar libraries.
Suppose you have such a folder structure:
path/yourApp/yourApp.jar
path/yourApp/lib/lib1.jar
path/yourApp/lib/megalib1.jar
path/yourApp/lib/supermegalib1.jar
All you have to do, add in MANIFEST.MF each of used jar.
Manifest-Version: 1.0
Main-Class: com.company.MyProgram
Class-Path: ./lib/lib1.jar ./lib/megalib1.jar ./lib/supermegalib1.jar
From within the manifest, you grant usage to each library.
Single all in one jar file might be easier to share and distribute, but in fact this doesn't give significant advantages over distributing as an archive and unpack it in some folder where you want to deploy. This will not make your program easier to maintain, faster. It will not make significant hdd usage difference.

Categories