Override the classpath in the MANIFEST.MF of a jar [duplicate] - java

I have a JAR file that contains an application as well as configuration files for that application. The application loads configuration files from the classpath (using ClassLoader.getResource()), and has its dependencies completely satisfied using the configuration files baked into the JAR file.
On occasion I want the application to be run with a slightly different configuration (specifically I want to override the JDBC URL to point to a different database) so I create a new configuration file, store it in the correct directory structure (which means in a directory /config of a classpath entry), and I want to do something like this:
java -cp new-config:. -jar application.jar
But I can't get the classpath to have the new-config path entry before the application JAR's contents. Is it hard-coded that the JAR's content is always the first thing on the classpath?

Why not just invoke the application without specifying -jar and instead name the application main class explicitly? This will allow you to put both your new-config and the application.jar on the classpath in the required order:
e.g. (assuming "new-config" is a directory containing the overridden properties file)
java -cp new-config:application.jar Application.Main.Class
I believe the name of main class can be found in the MANIFEST.MF file inside the jar ....

When you use the -jar option to launch your application:
... the JAR file is the source of all
user classes, and other user class
path settings are ignored.
as described here. A workaround would be to specify the classpath in the jar file's manifest to include the additional path (described here).
However, given that you are only talking about amending configuration you may want to take a different approach that is not reliant on the classpath. For example, I typically configure my applications through Spring using property files to determine the location of databases, etc. My Spring configuration is consistent across test, QA and live environments but I pass a different property file as a command line argument when launching the app.
Spring Configuration Snippet
<bean id="MyDataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource">
<property name="url" value="jdbc:microsoft:sqlserver://${dbServer}:${dbPort};DatabaseName=${dbName}"/>
<property name="username" value="${dbUserName}"/>
<property name="password" value="${dbPassword}"/>
<property name="suppressClose" value="false"/>
</bean>
Property File Snippet
dbServer=MyServer
dbPort=1433
dbName=MyDb
dbUserName=Me
dbPassword=foobar

The JAR archive specified by the -jar option, overrides all other values.
You would have to generally do it with an outside config file or build your own solution withod ClassLoader.getResource().
We use a custom solution to solve this - we load the internal properties like so:
final Properties p = new Properties();
p.load(DefaultConfiguration.class.getResourceAsStream("config.properties"));
We then load the external file in the same way and overwrite the internal values with the external ones.
For info on how class loading works see:
http://java.sun.com/javase/6/docs/technotes/tools/findingclasses.html

It may not be possible using just the CLASSPATH. There are ways to get make the call to ClassLoader.getResource() use a static path to find the resource. If it is doing that, it is bypassing the CLASSPATH.

Related

Load resource file like (sql, txt etc) with absolute path to Spring ApplicationContext classpath

Usually in a maven based spring application, if I need to make use of any file resource I would usually place it in a folder src/main/resources and then it would magically be available for spring app config file as shown below with a file reference such as "classpath:create-table.sql".
spring_config.xml
<jdbc:embedded-database id="dataSource" type="DERBY">
<jdbc:script location="classpath:create-table.sql" />
</jdbc:embedded-database>
It is even more magical if the application context is being loaded from web.xml with WebApplicationContext in which case it won't even need the following style of initialization for application context, but if I were to write a standalone java based spring application, which needs the following app context initialization and not maven or web based, how do I make any resource file available in it's classpath to refer such a file(create-table.sql) as shown above from with in config file?
ApplicationContext context = new ClassPathXmlApplicationContext("/spring_config.xml");
I know how to load with an absolute path such as shown below but what I need is to be able to make resource file available in classpath for the spring config file to refer from classpath itself.
<jdbc:embedded-database id="dataSource" type="DERBY">
<jdbc:script location="file:C:\junit_framework\springmodulecontext\create-table.sql" />
</jdbc:embedded-database>
All the more better if there is a way to refer the file with a relative path, so that I could just place the resource file right next to config file and refer it with a current directory notation such as "file:./create-table.sql"? I tried this absolute notation but it doesn't seem to work.
if I were to write a standalone java based spring application and not maven or web based, I make any resource file available in classpath by including its directory path (absolute or relative) to java -classpath list.
That is to say, if I want a file reference such as "classpath:create-table.sql" to actually read ./create-table.sql, I need to put a . (which is directory path of a desired file) to classpath like:
java -classpath .:lib/*.jar:...

How to externalize application.properties in Tomcat webserver for Spring?

SpringApplication will load properties from application.properties
files in the following locations and add them to the Spring
Environment:
- A /config subdirectory of the current directory.
- The current directory
- A 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).
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-application-property-files
Question: when running a war file on a tomcat server: how can I add an additional location for the application.properties outside the classpath or the tomcat container, like d:\application.properties?
The custom location should get highest precedence regarding the locations above.
Problem is: I could of course add a /config folder inside my exploded war in the tomcat webapps folder, but then I'd lose any custom configuration if the webapps folder is cleaned and war is redeployed.
Thus I'd like to add an additional location outside.
For me the easiest way to do it, was to place a context file inside Tomcat's config folder. For example if your application is running under root path (eg. http://your_domain.com/) you need to create a file [path_to_your_tomcat]/conf/Catalina/localhost/ROOT.xml. If your application runs in a different path, for example http://your_domain.com/example_path the file should be named like this [path_to_your_tomcat]/conf/Catalina/localhost/example_path.xml. Inside this file you can specify a path to the external application.properties file that can be placed anywhere on your hard drive.
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Environment name="spring.config.location" value="file:/path/to/your/application/properties/file/" type="java.lang.String"/>
</Context>
You can set a spring_config_location environment variable pointing to the folder that contains your application.properties file.
In the case of Tomcat you can do this by adding the following line to your <TOMCAT_HOME>/bin/setenv.sh file (create the file if missing):
export spring_config_location=/usr/local/tomcat/conf/
Place the properties file in that folder. In case you have multiple apps you can set the name of the properties file of each app to be unique. For a Spring Boot App I have done it like this:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
System.setProperty("spring.config.name", "my-app");
SpringApplication.run(MyApplication.class, args);
}
}
This will pick the new name when run with BOOT. To have the name configured when deployed on Tomcat too, overwrite configure of SpringBootServletInitializer like so:
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyApplication.class).properties("spring.config.name: my-app");
}
}
Then name your properties file like: my-app.properties. Instead of the default name Spring will look for that. You can put all your apps properties files in the specified folder, /usr/local/tomcat/conf/ in our sample. Your external properties will get precedence. See here for priorities: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
UPDATE
Since Spring Boot 2 the behavior of spring_config_location has changed (from the migration guide):
it previously added a location to the list of default ones, now it
replaces the default locations. If you were relying on the way it was
handled previously, you should now use
spring.config.additional-location instead.
So based on your use case you should consider which of the properties to set as an environment variable. The new one should look like spring_config_additional-location in setenv.sh.
Where files are looked up from is described in the reference documentation too.
In tomcat/bin create setenv.sh file and in file you need to give below line in file for default properties for all the wars, And save it and restart tomcat.
export SPRING_PROFILES_ACTIVE=dev
I had to do it several times and the best approach I found is to configure an external directory as classpath resource in the container:
Then, in the directory place the resources you want to externalize and everything will run fine. To load the resource in spring, you could do this way:
<beans:bean id="externalProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<beans:property name="location" value="classpath:[my-application-name]/applicationProperties.properties" />
<beans:property name="placeholderPrefix" value="!applicationProperties{" />
<beans:property name="placeholderSuffix" value="}" />
</beans:bean>
You can see that, as you said that you might want to have multiple applications deployed in each tomcat, you could simply create a directory structure in the folder you set in classpath, to maintain different application.properties for each of your war applications
In case you want to maintain dinamically the application name section in your Spring configuration, you could do it in several ways, during packaging stage in maven or even using the application context path
I finally added the following property to externalize eg secure properties:
spring.config.additional-location=/etc/tomcat/<appname>/application-production.properties
if anyone is looking for a linux solution, This is working for us:
edit tomcat startup.sh
add:
export spring_config_location=/<YOUR_PATH>/application.properties
example:
export spring_config_location=/app/conf/application.properties
for tomcat 9 on Ubuntu 18.04 and spring boot 2, create setenv.sh file under $CATALINA_HOME/bin/ working for me:
#!/bin/bash
export spring_config_additional_location="/opt/tomcat/latest/conf/application.properties"
don't forget to set file permission if needed
Using Tomcat 9.0.27
Running Spring boot 2.2.1
I did setup setenv.sh and added the line
export spring_config_location=/<PATH_TO_CONF_DIR>/application.properties
and got it to work..

spring boot: add new yml files to application config

i want developers to be able to locally override some configuration properties. (let's say we work on google drive and everyone should test it on its own account).
i don't want to override properties using command line (because it has to be set inside every IDE configuration and on every CLI run).
what i want is: application should use all the standard spring boot config files (application.yml etc) and also look for e.g. local.yml (on the classpath) or some file inside user.home. and those additional files should override other settings.
how to add new yml resources and order them correctly?
edit: i know spring's default orders and locations. question is about adding new ones
If you look in the Spring Boot documentation about the locations for configuration files (http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config), you can see, that they are loaded from the following places (amongst others):
Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants)
Application properties outside of your packaged jar (application.properties and YAML variants).
There are two default locations where they are loaded from ( see http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-application-property-files):
A /config subdirectory of the current directory.
The current directory
Current directory in this case means the working directory for the Java process (Usually the directory where the JAR is located, or in case of running with in the IDE, usually the project root folder). So the developers just can place their own configuration files in that places and they are automatically loaded (and will override properties within the JARs). Add that files to .gitignore (or .svnignore or ...) and they won't accidentally committed into your repository.
There's a new way to do this, after Spring Boot v2.4, by using spring.config.import: https://spring.io/blog/2020/08/14/config-file-processing-in-spring-boot-2-4#importing-additional-configuration
By adding this part to your application.yml file, you should be able to import the additional configuration:
spring:
config:
import: local.yml
The article also has this section:
Imports can be considered as additional documents inserted just below the document that declares them. They follow the same top-down ordering as regular multi-document files: An import will only be imported once, no matter how many times it is declared.
So the contents of local.yml should be handled as if they were appended to the end of application.yml, thereby allowing you to override any property in application.yml.
From Spring Boot Documentation : Application property files:
SpringApplication will load properties from application.properties files in the following locations and add them to the Spring Environment:
A /config subdirectory of the current directory.
The current directory
A 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).
This also goes for yaml, so you everyone can add application.yml under config directory, under the directory you run the spring boot jar from.
You can also customize the extra configuration file to be local.yml if you'd like by using spring.config.location:
--spring.config.location=classpath:/application.yml,classpath:/local.yml
Note however:
spring.config.name and spring.config.location are used very early to determine which files have to be loaded so they have to be defined as an environment property (typically OS env, system property or command line argument).
To provide the configuration from external config file in spring-boot application -
-Dspring.config.location=file:/home/vfroot/Workspace/project/MODULE_HOME/application.yaml
this command can be run with terminal:
mvn clean install -Dspring.config.location
= file:/home/vfroot/Workspace/MODULE_HOME/application.yaml
or need to set in Eclipse VM argument.
Also to set the active profiles :
-Dspring.profiles.active=dev
Well, since i am new in Spring Boot & Restfull Web Services. However, i managed to add a new .yml file to mange database and server port.
Instructions that i followed:
Project File.
Other Sources
src/main/resources
default package
right click on "default package"
add new YAML FILE
Or of YAML File option not available
5. right click on "default package"
6. then in categories: other --> File Types: YAML File

spring boot external config

I am trying to load an external properties file into my spring boot app.
initially I used #PropertySource in the config class.
but now I want to remove this annotation so the class is not dependent on the location.
so I tried to use:
java -jar my-boot-ws.war --SPRING_CONFIG_NAME=file:///Users/TMP/resources/
based on this http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html documentation but I get the following error:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder
using the annotation works fine but I would really like to move away from that.
any help on this would be great
Thanks
****** CORRECTION *******
Sorry copy paste error the above command was supposed to be:
java -jar my-boot-ws.war --spring.config.location=file:///Users/TMP/resources/
I'm not trying to change the name of the config file just add an additional location.
As explained here:
If spring.config.location contains directories (as opposed to files)
they should end in / (and will be appended with the names generated
from spring.config.name before being loaded).
I interpreted this as saying that the file ${spring.application.name}.properties would be loaded from the --spring.config.location passed in from the command line
After some more googeling I found this Spring Boot and multiple external configuration files indicating that the following is the correct usage:
java -jar my-boot-ws.war --spring.config.location=file:///Users/TMP/resources/myFile.properties
I was under the impression that the --spring.config.location would load other properties files in the directory specified. according to the post at the link I mentioned this is not the case. based on the link if the directory is specified then that is where the application.properties is searched for. but again the documentation here http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html seems to insinuate that the spring boot app will look on the class path first and if available grab the app name to get additional properties files based on that name.
however once I specified a file name everything worked fine so I guess I was mistaken.
In command line you should use below property to mention an additional boot configuration file:
--spring.config.location="file:/path/to/application.properties"
An alternative would be:
-Dspring.config.location="file:/path/to/application.properties"
Note that characters are lower case and the word separator is a period ..
Otherwise you can use an environment variable with key you used already:
In a *nix system:
export SPRING_CONFIG_NAME=file:/path/to/application.properties
In Windows OS:
set SPRING_CONFIG_NAME=file:/path/to/application.properties
It might not be a common issue, but I faced it. You also must have an application.properties inside your classpath even when you replace it with --spring.config.name (I had mine in gitignore due to sensitive information).
1) Makesure args pass inside of run method
public class GemFireTestLoaderApplication {
public static void main(String[] args) {
SpringApplication.run(GemFireTestLoaderApplication.class, args);
}
}
2) If you have configureed in xml comment or remove first
<!-- <context:property-placeholder location="classpath:config.properties" /> -->
<!-- <context:property-placeholder location="file:/data/xxx/vaquarkhan/dataLoader/config.properties" /> -->
Following command you can use to pass properties name
3.1)
java -jar GemfireTest-0.0.1-SNAPSHOT.jar --spring.config.location=file:///C:/data/xxx/vaquarkhan/dataLoader/test/config.properties
3.2)
java -jar GemfireTest-0.0.1-SNAPSHOT.jar --spring.config.location=file:///C:/data/xxx/vaquarkhan/dataLoader/test/config.properties
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
spring.config.name=spring
spring.config.location=classpath:/config/
in side the config folder spring.properties file is available, while running the server the this properties file is not loading

What's the purpose of META-INF?

In Java, you often see a META-INF folder containing some meta files. What is the purpose of this folder and what can I put there?
From the official JAR File Specification (link goes to the Java 7 version, but the text hasn't changed since at least v1.3):
The META-INF directory
The following files/directories in the META-INF directory are recognized and interpreted by the Java 2 Platform to configure applications, extensions, class loaders and services:
MANIFEST.MF
The manifest file that is used to define extension and package related data.
INDEX.LIST
This file is generated by the new "-i" option of the jar tool, which contains location information for packages defined in an application or extension. It is part of the JarIndex implementation and used by class loaders to speed up their class loading process.
x.SF
The signature file for the JAR file. 'x' stands for the base file name.
x.DSA
The signature block file associated with the signature file with the same base file name. This file stores the digital signature of the corresponding signature file.
services/
This directory stores all the service provider configuration files.
New since Java 9 implementing JEP 238 are multi-release JARs. One will see a sub folder versions. This is a feature which allows to package classes which are meant for different Java version in one jar.
Generally speaking, you should not put anything into META-INF yourself. Instead, you should rely upon whatever you use to package up your JAR. This is one of the areas where I think Ant really excels: specifying JAR file manifest attributes. It's very easy to say something like:
<jar ...>
<manifest>
<attribute name="Main-Class" value="MyApplication"/>
</manifest>
</jar>
At least, I think that's easy... :-)
The point is that META-INF should be considered an internal Java meta directory. Don't mess with it! Any files you want to include with your JAR should be placed in some other sub-directory or at the root of the JAR itself.
I've noticed that some Java libraries have started using META-INF as a directory in which to include configuration files that should be packaged and included in the CLASSPATH along with JARs. For example, Spring allows you to import XML Files that are on the classpath using:
<import resource="classpath:/META-INF/cxf/cxf.xml" />
<import resource="classpath:/META-INF/cxf/cxf-extensions-*.xml" />
In this example, I'm quoting straight out of the Apache CXF User Guide. On a project I worked on in which we had to allow multiple levels of configuration via Spring, we followed this convention and put our configuration files in META-INF.
When I reflect on this decision, I don't know what exactly would be wrong with simply including the configuration files in a specific Java package, rather than in META-INF. But it seems to be an emerging de facto standard; either that, or an emerging anti-pattern :-)
The META-INF folder is the home for the MANIFEST.MF file. This file contains meta data about the contents of the JAR. For example, there is an entry called Main-Class that specifies the name of the Java class with the static main() for executable JAR files.
META-INF in Maven
In Maven the META-INF folder is understood because of the Standard Directory Layout, which by name convention package your project resources within JARs: any directories or files placed within the ${basedir}/src/main/resources directory are packaged into your JAR with the exact same structure starting at the base of the JAR.
The Folder ${basedir}/src/main/resources/META-INF usually contains .properties files while in the jar contains a generated MANIFEST.MF, pom.properties, the pom.xml, among other files. Also frameworks like Spring use classpath:/META-INF/resources/ to serve web resources.
For more information see How do I add resources to my Maven Project.
You can also place static resources in there.
In example:
META-INF/resources/button.jpg
and get them in web3.0-container via
http://localhost/myapp/button.jpg
> Read more
The /META-INF/MANIFEST.MF has a special meaning:
If you run a jar using java -jar myjar.jar org.myserver.MyMainClass you can move the main class definition into the jar so you can shrink the call into java -jar myjar.jar.
You can define Metainformations to packages if you use java.lang.Package.getPackage("org.myserver").getImplementationTitle().
You can reference digital certificates you like to use in Applet/Webstart mode.
Adding to the information here, the META-INF is a special folder which the ClassLoader treats differently from other folders in the jar.
Elements nested inside the META-INF folder are not mixed with the elements outside of it.
Think of it like another root. From the Enumerator<URL> ClassLoader#getSystemResources(String path) method et al perspective:
When the given path starts with "META-INF", the method searches for resources that are nested inside the META-INF folders of all the jars in the class path.
When the given path doesn't start with "META-INF", the method searches for resources in all the other folders (outside the META-INF) of all the jars and directories in the class path.
If you know about another folder name that the getSystemResources method treats specially, please comment about it.
Just to add to the information here, in case of a WAR file, the META-INF/MANIFEST.MF file provides the developer a facility to initiate a deploy time check by the container which ensures that the container can find all the classes your application depends on. This ensures that in case you missed a JAR, you don't have to wait till your application blows at runtime to realize that it's missing.
I have been thinking about this issue recently. There really doesn't seem to be any restriction on use of META-INF. There are certain strictures, of course, about the necessity of putting the manifest there, but there don't appear to be any prohibitions about putting other stuff there.
Why is this the case?
The cxf case may be legit. Here's another place where this non-standard is recommended to get around a nasty bug in JBoss-ws that prevents server-side validation against the schema of a wsdl.
http://community.jboss.org/message/570377#570377
But there really don't seem to be any standards, any thou-shalt-nots. Usually these things are very rigorously defined, but for some reason, it seems there are no standards here. Odd. It seems like META-INF has become a catchall place for any needed configuration that can't easily be handled some other way.
If you're using JPA1, you might have to drop a persistence.xml file in there which specifies the name of a persistence-unit you might want to use. A persistence-unit provides a convenient way of specifying a set of metadata files, and classes, and jars that contain all classes to be persisted in a grouping.
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
// ...
EntityManagerFactory emf =
Persistence.createEntityManagerFactory(persistenceUnitName);
See more here:
http://www.datanucleus.org/products/datanucleus/jpa/emf.html
All answers are correct. Meta-inf has many purposes. In addition, here is an example about using tomcat container.
Go to
Tomcat Doc and check
" Standard Implementation > copyXML " attribute.
Description is below.
Set to true if you want a context XML descriptor embedded inside the application (located at /META-INF/context.xml) to be copied to the owning Host's xmlBase when the application is deployed. On subsequent starts, the copied context XML descriptor will be used in preference to any context XML descriptor embedded inside the application even if the descriptor embedded inside the application is more recent. The flag's value defaults to false. Note if the deployXML attribute of the owning Host is false or if the copyXML attribute of the owning Host is true, this attribute will have no effect.
You have MANIFEST.MF file inside your META-INF folder. You can define optional or external dependencies that you must have access to.
Example:
Consider you have deployed your app and your container(at run time) found out that your app requires a newer version of a library which is not inside lib folder, in that case if you have defined the optional newer version in MANIFEST.MF then your app will refer to dependency from there (and will not crash).
Source: Head First Jsp & Servlet
As an addition the META-INF folder is now also used for multi-release jars. This is a feature which allows to package classes which are meant for different Java version in one jar, e.g. include a class for Java 11 with new features offered by Java 11 in a jar also working for Java 8, where a different class for Java 8 with less features in contained. E.g this can be useful if a newer Java version is offering enhanced, different or new API methods which would not work in earlier version due to API violations. One will see a sub folder versions then.

Categories

Resources