Where is the relative path to a packaged Spring Boot application - java

How/Where can I set the relative path location for a packaged (jar) Spring Boot jar application?
The following is what works in my IDE (IntelliJ).
I have in my application.properties file the following properties.
converter.output=upload-dir/output/
converter.input=upload-dir
I have a Java class that controls the properties for me.
#Component
#ConfigurationProperties("converter")
public class ConverterProperties {
//getters
//setters
}
I have the following directory structure within the IDE.
src/
target/
upload-dir/
upload-dir/output/
pom.xml
README.txt
However, I am wanting to know where my upload-dir and upload-dir/output folders would be when I generate a jar and run it from a folder? I have tried putting the folder in the same location as the jar
C:\app\app.jar
C:\app\upload-dir\
C:\app\upload-dir\output\
But no dice. I setup the #ConfigurationProperties based on this documentation. https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html, but I can't seem to find anywhere in there were it talks about packaged jar relative paths.

A typical spring boot application displays some of the information you are looking for in the first line at info level (Starting Application {name} on {host} with PID 1234 ({jarpath} started by {user} in {workdir})
Looking at the source code in StartupInfoLogger, it looks like you need to use the ApplicationHome helper class (in package org.springframework.boot) in order to get that absolute path of the JAR file of your running spring boot application.
Here is an example of Java code to retrieve the location of the jar file and the directory containing the jar file. This is then used to create the uploadDir file (assuming it is a subdirectory of the jar directory)
ApplicationHome home = new ApplicationHome(this.getClass());
File jarFile = home.getSource();
File jarDir = home.getDir();
File uploadDir = new File(jarDir, "upload-dir");
You would want to run this from within one of your application classes running in the spring boot app. Looks like it uses the class passed to the constructor of ApplicationHome in order to find the jar which contains that class.

Related

Read external .properties file from the install directory (not the current directory)

I am distributing a Spring Boot application as a zipped "bootJar" using the Gradle Application plugin and the "distZip" task. The end-user will get the zip file, unzip it, and run it by just typing "myApp" (a shell script nicely created by the plugin).
I would like the end-user to create a "myapp.properties" file (a name I chose) and put it in the installation directory, or a "config" directory under the installation directory.
Suppose I set up my embedded (in the jar) application.properties file as follows:
spring.config.import = file:./myapp.properties will only read from the current directory
spring.config.import = file:/etc/myapp.properties will read from the specified directory -- but I don't know what this is at build time (the end-user determines it at installation time)
How can I set up my application so that Spring Boot can read properties from an external file whose location is specified later?
NOTE: I know I can play around with the generated scripts to pass in environment variables or Spring Boot properties, but I was hoping to do this completely within Spring Boot so I don't need to modify the nicely generated shell scripts.
spring.config.import = file:./myapp.properties will only read from the
current directory spring.config.import = file:/etc/myapp.properties
will read from the specified directory -- but I don't know what this
is at build time (the end-user determines it at installation time)
Why overcomplicate this.
Place inside the jar all the properties that you want to be statically configured as default values when you build the application.
Embedded application.properties
server.port = 8080
prop1.element = something
Then the client can create another file application.properties and place it in the same directory with the jar and define more properties which are not already defined.
prop2.element = something2
prop3.element = something3
By default Spring Boot will load properties both from the embedded file as well from the file in the current directory where the jar is placed during startup.
In the external application.properties you can also overwrite properties existing in the embedded application.properties. So if the external file in the current directory same as the jar is the following
prop2.element = something2
prop3.element = something3
prop1.element = something4 <--- this value here will overwrite the value 'something' from embedded file
According to spring doc
SpringApplication will load properties from application.properties
files in the following locations and add them to the Spring
Environment:
/config subdirectory of the current directory.
The current directory
classpath /config package
The classpath root The list is ordered by
Precedence (properties defined in locations higher in the list
override those defined in lower locations).
After having more input from the comments, it seems that you face another issue as well. You start the application from command line from another directory so that is counted as the directory where spring will look for the external configuration instead of where the jar is placed.
So for example let's say that the jar is placed inside the target folder that exists in current directory. You start the application using the following command:
java -jar target/demo-0.0.1-SNAPSHOT.jar
But then the external application.properties existing inside target folder is not loaded from spring because you executed the command from another directory. This can be solved if you start the application in the following way
java -jar -Dspring.config.additional-location=./target/ target/demo-0.0.1-SNAPSHOT.jar
This should not be difficult as you already provide the path where the jar exists in the command line.

which application.properties file will be picked in spring boot

In my spring boot project, I have application-default.properties file in resource folder and another application.properties file in a folder named "config". This folder I have manually created inside the project folder.
Now when I run the spring boot application via eclipse it picks up properties file inside config directory and when I create jar of the same project and run via java -jar, then it picks up the application.properties file in resource folder.
Can someone explain this behavior as why while running form eclipse, the properties file in config folder overrides the one in resource folder ?
By default , in springboot the precedence order for the application.properties is as follows :
A /config subdirectory of the current directory
The current directory
A classpath /config package
The classpath root
Since the application.properties that you created in the config folder is not under the resource directory it might not be included in the jar(check the jar to confirm if it is present). When maven creates the jar anything under the resource dir will be copied over to the jar, so if you want the config folder to be present inside the jar then move it to under the resource dir. Or if you want to keep the conf external then, Create the config folder in same dir as the jar or set a classpath to the config to take it up while deploying using java -jar. You could specify external location for the property file like :
java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
Read official doc.

File relative to jar using Spring

I work on a Java console application. There is a property in my application.properties file, which contains another file name as a value of a property, like
my.file.location=file:myDir/myFileName
In the code I try to get the file like this:
#Value("${my.file.location}")
private File myfileLocation;
If I start the application from the directory, which contains jar file, the file is resolved, but when I run my application from a different location, the file location is not valid.
I can't have this file on classpath, it must be external to the jar file.
How can I make the file path to be relative to my jar file and not to the current working directory?
I believe this has nothing to do with Spring right? You just want to load configuration file, that is inside your application, unpacked, so the user can modify it, ok?
First, you may try to always setup the working directory, which I believe is more "standard" solution. In windows you can make a link, that specifies the Start in section and contains the path to your jar file (or bat or cmd, whatever).
If you insist on using the jar path, you could use How to get the path of a running JAR file solution. Note, that the jar must be loaded from filesystem:
URI path = MySpringBean.class.getProtectionDomain().getCodeSource().getLocation().toURI();
File myfileLocation = new File(new File(path).getParent(), "/myDir/jdbc.properties");

How to retrieve .properties?

Im developing desktop java application using maven.
I got a *.properties file that I need to retrive during execution (src/resources/application.properties).
The only thing comes to my mind is to use:
private Properties applicationProperties;
applicationProperties.load(new BufferedInputStream(new FileInputStream("src/resources/application.properties")));
This would work if I run my application directly from IDE.
I want to to keep outpout hierarchy clear, so I set maven to copy resources folder dircetly to target folder (which is a basedir for the output application). This way application.properties file won't load (since I have target/resources/application.properties but not target/src/resources/application.properties).
What is the best way to manage resources so they work both when I debug from IDE and run builded jar file directly?
Don't expect files to be in the src folder - it doesn't exist at runtime. The properties files go to /bin. But don't rely on that either. Because FileInputStream takes absolute paths only.
When you need a classpath-relative path, use:
InputStream is = YourClass.class.getResourceAsStream("/a.properties")`
(maven sends files from /src/main/resources to the root of the classpath)
You should load the property file from the classpath rather than from an explicit file system location:
applicationProperties.load(new BufferedInputStream(this.getClass().getResourceAsStream( "/application.properties" );
As long as your IDE is configured to include the resources directory on your classpath (this should be the default with Maven), then this will work whether you're running within the IDE or not since maven will copy the resources to the right place when packaging your archive.

Error with the .class location when building a WAR for a Play! framework application

I'm working on a 1.2 Play! framework application, and I have a problem when deploying it as a WAR on a Tomcat 6.
One page of my application displays a list of information.
These information are retrieved from a .yml file.
So I have a controller that generate a Iterable<Object> from this .yml file, like that:
public static void myFunction() {
Constructor constructor = new Constructor(MyClass.class); // org.yaml.snakeyaml.constructor.Constructor
constructor.addTypeDescription(new TypeDescription(MyClass.class));
Yaml yaml = new Yaml(constructor);
Iterable<Object> listOfInfo = yaml.loadAll(Application.class.getResourceAsStream("/my-file.yml"));
render("Application/my-page.html", listOfInfo);
}
The important point is that MyClass is located in the app/my/company/my-app/ package (my.company.my-app.MyClass).
When I run my application using play run, there is no problem.
Now, I build the WAR package (using play war -o some/dir --zip), and I install this generated WAR on a Tomcat (6.0).
Once the server is started, and try to access the corresponding page, I get the following error:
#683lh76fn
Internal Server Error (500)
Template execution error (In /app/views/Application/my-page.html around line 9)
Execution error occured in template /app/views/Application/my-page.html. Exception raised was ConstructorException : null; Can't construct a java object f
or tag:yaml.org,2002:my.company.my-app.MyClass; exception=Class not found: my.company.my-app.MyClass.
play.exceptions.TemplateExecutionException: null; Can't construct a java object for tag:yaml.org,2002:my.company.my-app.MyClass; exception
=Class not found: my.company.my-app.MyClass
at play.templates.BaseTemplate.throwException(BaseTemplate.java:84)
at play.templates.GroovyTemplate.internalRender(GroovyTemplate.java:252)
at play.templates.Template.render(Template.java:26)
at play.templates.GroovyTemplate.render(GroovyTemplate.java:184)
at play.mvc.results.RenderTemplate.<init>(RenderTemplate.java:24)
at play.mvc.Controller.renderTemplate(Controller.java:659)
at play.mvc.Controller.renderTemplate(Controller.java:639)
at play.mvc.Controller.render(Controller.java:694)
at controllers.Application.myFunction(Application.java:311)
If I have a look in the exploded war, I see that my.company.my-app.MyClass is located in the directory WEB-INF/application/precompiled/java/my/company/my-app/ directory.
If I move this directory into WEB-INF/classes, then I don't get this error anymore.
Why does this error occurs? What are my options to make it work (without modifying manually the WAR)?
By the default, Tomcat only looks at files in /WEB-INF/classes and /WEB-INF/lib (for each war file) as they are included in the classpath. There are a number of things you could do..
Put your classes in the Tomcat lib folder and they'll be handled by the common loader rather than the webapp loader. (not recommended as they will be available to other apps)
You can enable the shared loader in conf/catalina.properties and use whatever directory you want.
add the path to the CLASSPATH variable in the '/bin/setclasspath.sh' script (or setclasspath.bat for
Windows).
Personally i would fix/modify how i build the war file and make sure that the classes are copied to WEB-INF/classes instead of WEB-INF/application folder.
I finally found the answer of this problem!
The Yaml parser (snakeyaml) creates its own ClassLoader in order to parse a .yml file. The structure of the Play! framework uses its own ClassLoader, and the compiled classes are located in the WEB-INF/application/precompiled/java directory, which is not the WAR standard.
Due to that, Yaml was not able to retrieve the classes, in particular MyClass.class.
The solution to solve this issue (except by modifying the WAR using Ant for example), is to give the Play! ClassLoader to the Yaml parser. Instead of writing that:
Constructor constructor = new Constructor(MyClass.class);
constructor.addTypeDescription(new TypeDescription(MyClass.class));
I write:
CustomClassLoaderConstructor constructor = new CustomClassLoaderConstructor(MyClass.class, MyController.class.getClassLoader());
constructor.addTypeDescription(new TypeDescription(MyClass.class));
using this, I can use the WAR created by Play! directly without getting the Yaml error anymore!

Categories