21

I am building a software system to interact with an enterprise software system, using Spring Boot. My system depends on some jars and *.ini files from that enterprise system, so I cannot pack all dependencies in Maven. I would like to be able to run Spring Boot as Executable Jar with embedded Tomcat. I would also like to be able to set the classpath via the command line. So something like:

java -classpath /home/sleeper/thirdparty/lib -jar MyApp.jar

However, -classpath and -jar cannot co-exist. I have tried "-Dloader.path". It was able to load all the jar files under the folder, but not other things, like *.ini files in the folder.

So is there a way we can make -classpath to work with an Spring executable jar with embedded Tomcat?

Dharman
  • 30,962
  • 25
  • 85
  • 135
user1670498
  • 349
  • 1
  • 4
  • 12
  • 2
    Have a look at this question and accepted answer: http://stackoverflow.com/questions/15930782/call-java-jar-myfile-jar-with-additional-classpath-option – Strelok Sep 27 '16 at 12:00

7 Answers7

15

On Linux:

java -cp MyApp.jar:/home/sleeper/thirdparty/lib -Dloader.main=myMainApplicationClass org.springframework.boot.loader.PropertiesLauncher

On Windows:

java -cp MyApp.jar;/home/sleeper/thirdparty/lib -Dloader.main=myMainApplicationClass org.springframework.boot.loader.PropertiesLauncher

This will avoid messing with the manifest or the Spring Boot Maven plugin configuration as in the other answers. It will launch your app with the PropertiesLauncher, which allows you to specify the main class in loader.main. As mentioned earlier, for some reason if you use PropertiesLauncher with loader.path, it will not add resource files to the classpath. This works around the issue by using -cp instead of -jar.

EDIT As mentioned by Pianosaurus in the comment, use ":" instead of ";" as separator in the classpath on Linux

Mihail Kostira
  • 510
  • 7
  • 10
  • 2
    Thank you, this worked for us. Remember to use ":" as separator in the classpath if you are on un*x. – Pianosaurus Dec 11 '18 at 10:45
  • 1
    This solution supports reading config files from the classpath at startup time, so this is a win for my little spring-boot app. This should be the accepted answer. – chrisinmtown Jul 19 '19 at 18:33
  • 1
    Remember to use the fully qualified name of the class for -Dloader.main – Aurasphere Jan 21 '20 at 16:32
13

If you just want add external libraries you can use the loader.path property.

java -Dloader.path="your-lib/" -jar your-app.jar

UPDATE

If you also need to read additional files from the classpath you have to create/change the manifest file of your application.

Lets assume that your are initializing your Spring Boot context from the class de.app.Application. Your MANIFEST.MF should looks as follows:

Manifest-Version: 1.0
Main-Class: de.app.Application
Class-Path: your-lib/

And the you can simply start your app with java -Dloader.path="your-lib/" -jar MyApp.jar.

For more information about the MANIFEST.MF please see Working with Manifest Files: The Basics.

Paul Wasilewski
  • 9,762
  • 5
  • 45
  • 49
  • 1
    I have actually tried that. See my original question. It is not working exactly as -classpath. It loads the jar files, but not the ini files. – user1670498 Sep 27 '16 at 07:09
  • @user1670498, sorry I overlooked that. Please see my answer for new details. – Paul Wasilewski Sep 27 '16 at 15:35
  • Hmm, it seems to be a solution, but it is not working for me. The classpath is still not loaded in the runtime. Not sure if it conflicts with Spring-Boot-Lib in my manifest... – user1670498 Sep 27 '16 at 20:54
  • 1
    Actually, it works for non jar files, but not jar files. So my app can read any ini files in /home/sleeper/thirdparty/lib/ from the classpath, but it cannot load any jar files from /home/sleeper/thirdparty/lib/. – user1670498 Sep 28 '16 at 00:33
  • 3
    After a lot of researching, seems "Class-Path: /home/sleeper/thirdparty/lib/" will not load any jar files under the folder. To reference jar file, we would need to explicitily reference each individual jar, something like "Class-Path: /home/sleeper/thirdparty/lib/abc.jar /home/sleeper/thirdparty/lib/def.jar". – user1670498 Sep 28 '16 at 02:52
  • @user1670498, referencing a directory in the class-path should work. It seems that you have to pay attention on the path format you use. See http://todayguesswhat.blogspot.de/2011/03/jar-manifestmf-class-path-referencing.html?m=1 – Paul Wasilewski Sep 28 '16 at 06:02
  • 2
    I actually read that blog, and I have the class-path ends in "/". I think what the blog says is, it won't load the jar files in the directory, but it will load .class files in the directory. – user1670498 Sep 28 '16 at 07:28
  • my spring context xml required to read a.properties file from a folder e.g. /XYZ/conf/. I modified the manifest.mf and introduced Class-Path: /XYZ/conf/ and it worked. my property file was references inside spring context xml file like below : – nirmalsingh May 31 '19 at 13:16
8

You mentioned that you needed to load *.ini files from an external folder. I had to do something similar, load CSV files from an external folder.

My file structure looked like this

./myapp.jar  
./config/file.csv

I was using the ResouceLoader to load the files as:

Resource res = resourceLoader.getResource("classpath:file.csv");
File csvFile = res.getFile();

Start script:

java -Dloader.path="config" -jar your-app.jar

The resource was not loading from the "config" folder as expected. After some research I found out that I had to change my Maven plugin configuration to use ZIP layout.

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <layout>ZIP</layout>
    </configuration>
</plugin>

This will direct Spring Boot to use PropertiesLauncher, which allows loading external resources from "loader.path".

See this excellent article for more detail.

Peter Tarlos
  • 731
  • 10
  • 13
6
  java -cp  C:\jar-path\your-jar-1.2.0.jar -Dloader.main=package-and-main class  -Dloader.path=external dependency jar path  org.springframework.boot.loader.PropertiesLauncher -Dspring.profiles.active=profile etc -default,test --spring.config.location=external properties file name

If want to define external memory use

        java -ms8g -mx8g -cp

java -cp

-Dloader.main

Spring Boot’s org.springframework.boot.loader.PropertiesLauncher comes with a JVM argument to let you override the logical main-class called loader.main:

-Dloader.path

Tell the PropertiesLauncher that it should pick up any libraries found in the “lib”

org.springframework.boot.loader.PropertiesLauncher

Spring Boot’s org.springframework.boot.loader.PropertiesLauncher comes with a JVM argument to let you override the logical main-class called loader.main:

          java -cp bootApp.jar -Dloader.main=org.khan.DemoApplication  org.springframework.boot.loader.PropertiesLauncher

-Dspring.profiles.active

If you are using Spring profile then you need to set profile first

     set SPRING_PROFILES_ACTIVE=default,test

or window run type envi and add

      spring_profiles_active
       default,test

--spring.config.location

Directory is specified then that is where the application.properties is searched for

vaquar khan
  • 10,864
  • 5
  • 72
  • 96
3

Just to add a simple solution without PropertiesLauncher or too much arguments.

1 - Build your standard executable springboot jar (my-spring-boot-app.jar)

2 - then run it without using the -jar option and using the JarLauncher class as the main class

java -cp "/path/to/jars/*:/path/to/app/my-spring-boot-app.jar" org.springframework.boot.loader.JarLauncher

(relative pathes are also perfectly valid)

that's all

Mumrah81
  • 2,034
  • 2
  • 16
  • 23
  • This worked also with `org.springframework.batch.core.launch.support.CommandLineJobRunner` as the declared main class in the manifest. – PJ_Finnegan Jan 10 '22 at 15:47
1

The standard way to add dependencies in Spring Boot project is placing those Jar files into BOOT-INF/lib. This will result in copy that dependencies in the jar or war file generated and with the classpath.idx updated as well.

You can see the official documentation here

The accuracy literature says:

Application classes should be placed in a nested BOOT-INF/classes directory. Dependencies should be placed in a nested BOOT-INF/lib directory

I already do that with external Jar files and everything gone Ok.

Dharman
  • 30,962
  • 25
  • 85
  • 135
pazfernando
  • 577
  • 6
  • 15
1

One solution that worked for me was to insert the jar with the external classes into the MANIFEST.MF's Class-Path. That's because the -jar switch ignores the -classpath option and the CLASSPATH environment variable.
Procedure:

  1. install the maven-jar-plugin into the POM;
  2. add the lines:
    <configuration>
        <archive>
            <manifestEntries>
                <Class-Path>/my/external/jar/absolute/path.jar</Class-Path>
            </manifestEntries>
        </archive>
    </configuration>

Build and run with java -jar myapp.jar. Its manifest will contain the line:

Class-Path: /my/external/jar/absolute/path.jar

This way the external jar will be searched at runtime and not at compile-time (it won't be copied in BOOT_INF/lib).

Sources:

post 1

post 2

PJ_Finnegan
  • 1,981
  • 1
  • 20
  • 17