Access maven project version in Spring config files - java

I would like to display the currently running version of my web application in the page. The project is based on Maven, Spring, and Wicket.
I'd like to somehow get the value of maven's ${project.version} and use it in my spring XML files, similarly to the way I use the Spring PropertyPlaceholderConfigurer to read a property file for settings that I use in my application.
If I had the maven project.version available as a variable in my Spring config, I could do something like this:
<bean id="applicationBean" class="com.mysite.web.WicketApplication">
<property name="version"><value>${project.version}</value></property>
</bean>
How can I do this?

You can use Maven filtering as already suggested.
Or you could just read the pom.properties file created by Maven under META-INF directory directly with Spring:
<util:properties id="pom"
location="classpath:META-INF/groupId/artifactId/pom.properties" />
and use the bean.
The only drawback of the later approach is that the pom.properties is created at package phase time (and won't be there during, say, test).

One technique would be to use mavens filtering. You can insert placeholders in resource files like which then get replaced with values from the build during the resource phase.
Look up "How do I filter resource files?" in the Maven getting started guide

Use the #PropertySource annotation to add the pom.properties file created by Maven in the META-INF directory. Note that the file doesn't exist until the package phase. To avoid errors during testing set the ignoreResourceNotFound=true and add a default value on the property being read (e.g. none below).
#Service
#PropertySource(value = "classpath:META-INF/maven/io.pivotal.poc.tzolov/hawq-rest-server/pom.properties", ignoreResourceNotFound=true)
public class MyClass {
private String applicationPomVersion;
#Autowired
public MyClass(#Value("${version:none}") String applicationVersion ) {
this.applicationPomVersion = applicationVersion;
}
public String getApplicationPomVersion() {
return this.applicationPomVersion;
}
}

We deploy property files outside of the web app. The files can then be filtered at deployment time.
If using Jetty one can put the file under $JETTY_HOME/resources or use the extraClassPath feature to load the property file at runtime.

I think the right way of gather application version is the one explained in this thread: https://stackoverflow.com/a/2713013/840635 through getClass().getPackage().getImplementationVersion().

Related

Read active application.properties from jar - injected by maven

I want to read Spring's boot active application.properties file a jar file that I add as Maven dependency.
We mange environment params via 3-4 files under publish folder, for example:
-publish
--some.project-application.properties.test
--some.project-application.properties.dev
in the project root so I cannot use PropertySources(different file name per env).
So is there is a way to read the active(in use) application.properties file?
Is using #Value annotation will work without any configuration?
I think you should use spring naming conventions(application.properties, application-dev.properties), so that spring will automatically take the correct property file from the class path.
Also, refer : How to resolve placeholder in properties file with values from another properties file in spring boot application

loading of property file outside of war file in spring boot

I need to load the application.properties file from outside the spring boot war which going to be deployed in tomcat.
I tried various solution missing something
Tried setting environmental variable as below in windows
name : SPRING_CONFIG_NAME
value:D:/test/application.properties
i tried multiple values for above value like file:/// in prefix and only file: as perfix .Nothing worked
Tried having context parameter is tomcat like mentioned in below SO answer
https://stackoverflow.com/a/44697239/2751962
Tried loading like this in main file which extends SpringBootServletIntializer
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class)
.properties(getProperties());
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
SpringApplicationBuilder springApplicationBuilder = (SpringApplicationBuilder) (new SpringApplicationBuilder(Application.class))
.sources(Application.class)
.properties(getProperties())
.run(args);
}
static Properties getProperties() {
Properties props = new Properties();
props.put("spring.config.location", "file:///D:/test/application.properties​");
return props;
}
I not sure what i missed , Kindly help.
External Configuration in Spring Boot
When using Spring Boot, there are documented naming conventions and directory structure. A Spring Boot app searches for properties to load from a prioritized list, so there are to suggestions for you to consider:
Use command-line flag spring.config.location to target specific file or directory from which to load properties sources. You can use this to specify directories to search or individual files to load. Be cautious loading individual files though, if you intend to use profile-based properties. (add flag in command like this: java -jar MyJar.jar --spring.config.location=D:\test\)
By default, Spring Boot will look for a ./config/ directory where the WAR is and the directory of the WAR itself, so you may place "application.properties" in either position and it will be loaded.
Pivotal provides a super great reference for Spring Boot. Section 24 covers properties more extensively than I can in a post.
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html (*links to most recent release's reference)
Note: I am not a Windows user, so be careful pasting in that filepath above. Edit Me.
Extending Configuration to Deployable Packages
Normally Spring Boot packages into an executable WAR or JAR that has an embedded servlet container engine that is used for the runtime. In your case, however, you are packaging a conventional WAR and deploying that to an external instance of Tomcat, so the configuration parameters must be propagated through Tomcat, using the JAVA_OPTS variable.
For a Apache Tomcat, the convention is to place your properties in ${catalina_base}/conf where catalina.base points to the location of the Tomcat instance. I created a working demo just now following these steps:
First, follow section 88.1 of the reference to setup a base WAR app
mvn package
Place application.properties in conf directory within Tomcat
set JAVA_OPTS=-Dspring.config.location=${catalina.base}/conf/
"%CATALINA_HOME%"\bin\startup
Deploy
It's not the cleanest deployment pipeline, but if you must use an external Tomcat instance, then this will work. However, to run multiple apps with separate property files on the same Tomcat instance would complicate things. In that case, using Spring Framework (not Boot) would be easier to configure.
You can try setting properties via XML and or Java configuration and #PropertySource.
#Configuration
#PropertySource("classpath:foo.properties")
public class PropertiesWithJavaConfig {
//...
}
source :- https://www.baeldung.com/properties-with-spring

Spring boot external configuration of property file

I have a spring boot application that I can package in a war that I want to deploy to different environments. To automate this deployment it'd be easier to have the configuration file externalized.
Currently everything works fine with a application.properties file in src/main/resources. Then I use ´mvn install´ to build a war deployable to tomcat.
But I would like to use a .yml file that does not need to be present on mvn install but that would be read from during deployment of the war and is in the same or a directory relative to my war.
24. externalized configuration shows where spring boot will look for files and 72.3 Change the location of external properties of an application gives more detail on how to configure this but I just do not understand how to translate this to my code.
My application class looks like this:
package be.ugent.lca;
Updated below
Do I need to add a #PropertySource to this file? How would I refer to a certain relative path?
I feel like it's probably documented in there as most spring boot documentation but I just don't understand how they mean me to do this.
EDIT
Not sure if this should be a separate issue but I think it's still related.
Upon setting the os variable the error of yaml file not found went away. Yet I still get the same error again as when I had no application .properties or .yml file.
Application now looks like this:
#Configuration
**#PropertySource("file:${application_home}/application.yml")**
#ComponentScan({"be.ugent.lca","be.ugent.sherpa.configuration"})
#EnableAutoConfiguration
#EnableSpringDataWebSupport
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
The application_home OS variable
$ echo $application_home
C:\Masterproef\clones\la15-lca-web\rest-service\target
My application.yml file(part it complains about):
sherpa:
package:
base: be.ugent.lca
Error upon java -jar *.war
All variations upon:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'sherpa.package.base' in string value "${sherpa.package.base}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:204)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:178)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:172)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:808)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1027)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
... 142 more
Using external properties files
The answer lies in the Spring Boot Docs, I'll try to break it down for you.
First of all, no you should not use #PropertySource when working with Yaml configuration, as mentioned here under the Yaml shortcomings :
YAML files can’t be loaded via the #PropertySource annotation. So in the case that you need to load values that way, you need to use a properties file.
So, how to load propery files? That is explained here Application Property Files
One is loaded for you: application.yml , place it in one of the directories as mentioned in the link above. This is great for your general configuration.
Now for your environment specific configuration (and stuff like passwords) you want to use external property files, how to do that is also explained in that section :
If you don’t like application.properties as the configuration file name you can switch to another by specifying a spring.config.name environment property. You can also refer to an explicit location using the spring.config.location environment property (comma-separated list of directory locations, or file paths).
So you use the spring.config.location environment property.
Imagine you have an external config file: application-external.yml in the conf/ dir under your home directory, just add it like this:
-Dspring.config.location=file:${home}/conf/application-external.yml as a startup parameter of your JVM.
If you have multiple files, just seperate them with a comma. Note that you can easily use external properties like this to overwrite properties, not just add them.
I would advice to test this by getting your application to work with just your internal application.yml file , and then overwrite a (test) property in your external properties file and log the value of it somewhere.
Bind Yaml properties to objects
When working with Yaml properties I usually load them with #ConfigurationProperties, which is great when working with for example lists or a more complex property structure. (Which is why you should use Yaml properties, for straightforward properties you are maybe better of using regular property files). Read this for more information: Type-Safe Configuration properties
Extra: loading these properties in IntelliJ, Maven and JUnit tests
Sometimes you want to load these properties in your maven builds or when performing tests. Or just for local development with your IDE
If you use IntelliJ for development you can easily add this by adding it to your Tomcat Run Configuration : "Run" -> "Edit Configurations" , select your run configuration under "Tomcat Server" , check the Server tab and add it under "VM Options".
To use external configuration files in your Maven build : configure the maven surefire plugin like this in your pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dspring.config.location=file:${home}/conf/application-external.yml</argLine>
</configuration>
</plugin>
When running JUnit tests in IntelliJ:
Run → Edit Configurations
Defaults → JUnit
add VM Options -> -ea -Dspring.config.location=file:${home}/conf/application-external.yml
Yes, you need to use #PropertySource as shown below.
The important point here is that you need to provide the application_home property (or choose any other name) as OS environment variable or System property or you can pass as a command line argument while launching Spring boot. This property tells where the configuration file (.properties or .yaml) is exactly located (example: /usr/local/my_project/ etc..)
#Configuration
#PropertySource("file:${application_home}config.properties")//or specify yaml file
#ComponentScan({"be.ugent.lca","be.ugent.sherpa.configuration"})
#EnableAutoConfiguration
#EnableSpringDataWebSupport
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
There is a very simple way to achieve this.
Inside your original application.properties file you can just specify the following line:
spring.config.import=file:Directory_To_The_File/Property_Name.properties
It will automatically sync all the properties from the external property file.
Now lets say that you have a situation where you need to get properties from multiple property files. In that case, you can mention the same line in the external property file which in turn will take the remaining properties from the second property file and so on.
Consider the following example.
application.properties:
spring.config.import=file:Resources/Custom1.properties
Custom1.properties:
server.port=8090
.
.
.
spring.config.import=file:Resources/Custom2.properties
One of the easiest way to use externalized property file using system environment variable is, in application.properties file you can use following syntax:
spring.datasource.url = ${OPENSHIFT_MYSQL_DB_HOST}:${OPENSHIFT_MYSQL_DB_PORT}/"nameofDB"
spring.datasource.username = ${OPENSHIFT_MYSQL_DB_USERNAME}
spring.datasource.password = ${OPENSHIFT_MYSQL_DB_PORT}
Now, declare above used environment variables,
export OPENSHIFT_MYSQL_DB_HOST="jdbc:mysql://localhost"
export OPENSHIFT_MYSQL_DB_PORT="3306"
export OPENSHIFT_MYSQL_DB_USERNAME="root"
export OPENSHIFT_MYSQL_DB_PASSWORD="123asd"
This way you can use different value for same variable in different environments.
Use below code in your boot class:
#PropertySource({"classpath:omnicell-health.properties"})
use below code in your controller:
#Autowired
private Environment env;

How to access file in the static resources(WEB-INF) folder from the referencing java project?

I have a web application, that contains a configuration xml file for one of my application services that is exposed as spring bean.
Also I have a standalone java application(which references my web app project from its pom.xml) in the same workspace, that runs tests using Spring TestContext framework and one of the tests checks the configuration of that XML file.
However I have a problem with accessing this xml file from the standalone app:
Before setting-up the test, in my previous configuration, the file was accessed through ServletContext and was located in WEB-INF/ folder. However, to make it accessable from the test project I had to move it to source/ folder and load it with getClassLoader().getResourceAsStream() method instead that of ServletContext. But it makes editing the file cumbersome because every time the app has to be redeployed.
Is it possible to keep the file in WEB-INF/ folder but load it from the referencing project during the test-runs?
P.S. Currently it's an STS project with Tomcat server.
Definitely keep the file under WEB-INF/ folder if that's where it is supposed to live.
For your test classes that are being executed from the command line. Your can use getClassLoader().getResource() on a file that you know is in the root of your classpath (e.g. application.properties file). From there you know the structure of your project and where to find WEB-INF/ relative to the properties file. Since it returns a URL you can use it to figure out a path to the XML files you're looking for.
URL url = this.getClass().getClassLoader().getResource("application.properties");
System.out.println(url.getPath());
File file = new File(url.getFile());
System.out.println(file);
// now use the Files' path to obtain references to your WEB-INF folder
Hopefully you find this useful. I have had to make assumptions about how your test classes are runing etc.
Take a look at the File Class and it's getPath(), getAbsolutePath(), and getParent() methods that could be of use to you.
I ended up using Spring MockServletContext class and injecting it directly to my service bean, before the test runs, as my service implemented ServletContextAware :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/test-ctx.xml" } )
public class SomeServiceTest {
#Autowired
private MyServletContextAwareService myService;
#Before
public void before(){
//notice that I had to use relative path because the file is not available in the test project
MockServletContext mockServletContext = new MockServletContext("file:../<my web project name>/src/main/webapp");
myService.setServletContext(mockServletContext);
}
If I had several classes using Servlet Context, then the better solution would be to use WebApplicationContext instead the default one (currently provided by DelegatingSmartContextLoader), but it would require implementing custom ContextLoader class and passing its class name to #ContextConfiguration annotation.
alternative and somewhat cleaner solution which later came to my mind is to refactor the service and inject ServletContext via #Autowired instead of messing with ServletContextAware, and provide the bean of corresponding type(effectively a MockServletContext instance).
Possibly, in future the direct support of MockServletContext from test classes will be added to Spring see SPR-5399 and SPR-5243.
UPDATE FOR SPRING 3.2
In Spring 3.2 initialization of the servlet context became as simple as adding one #WebAppConfiguration annotation:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration("file:../<my web project name>/src/main/webapp")
#ContextConfiguration(locations = { "/test-ctx.xml" } )
public class SomeServiceTest {
see details in the article
In a Maven project I had same problem. I had no servletContext and couldn't access static file in
WEB-INF directory.
I came across to a solution by adding entry to pom.xml which gave me access to the directory. It actually includes this path to the classpath.
PS: I was using Tomcat container
<project>
<build>
<resources>
<resource>
<directory>src/main/webapp/WEB-INF</directory>
</resource>
</resources>
</project>
It's a classpath resource, so put it on the classpath: $webapp/WEB-INF/classes
Maven projects will copy things in $module/src/main/resources to this location when packaging the webapp.
(the former is a sourcepath, the latter - WEB-INF/classes - is always put on the classpath by the servlet container, per spec.)

How can I set hibernate database config from outside config file?

I need to have hibernate database config set from outside text file, how can I do it? Is there some kind of method for this, or do I have to make my own?
You can programaticaly configure hibernate within your app: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/session-configuration.html#configuration-programmatic
HTH
Hibernate configuration file (*.cfg.xml) have to be in the class path of your application's war.
But, you can maintain file like 'install.properties' in deployment folder of your application.
Inside it you can maintain database related properties.For eg.
database.name =
database.hostname=
database.username=
databse.password=
Use some predefined constant string inside 'hibernate.cfg.xml' that you can replace during time of application deployment task. For eg.
<property name="hibernate.connection.url">jdbc:mysql://$database.hostname/$database.name</property>
you can write an 'ant' task which 'unwar' the war then replace constant strings with values as mentioned inside 'install.properties' and then make 'war' again.
In this way you can separate out configuration settings from application code structure.
Hibernate configuration files have to be on the class path but can be outside your war. The exact way to add a directory or files to the class path will depend on your app server so you'll have to mention which one you're using (for Tomcat, see this previous question for example).

Categories

Resources