I am writing a Java application which is almost ready for release, but I don't know how to create different .properties files for Debug and Release.
Let me clarify this for you.
I am storing the database host, username, password and other properties in the .properties files.
When I am writing and debugging the application these properties are configured to work with my development machine and database, but when the application is released they need to point to the release database and contain the release properties.
Is there any way to achieve this with Java and Maven?
I once did something similar, I wanted to have several resources packs in a Java webapp: one for IDE development, one for local (but outside IDE) development for graphic designers, and finally one for release, with all the packing controlled by Maven.
My solution is to declare several extra resources folders in the <build> node, and tell Maven which one to pick up using profiles (like #biziclop already suggested you); those folders are controlled through properties.
This is the POM I've used:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<packaging>war</packaging>
<name>...</name>
<!-- My prerequisite was that when working in Eclipse no extra steps
should be required to make the IDE use the right configuration than
Configure -> Convert to Maven Project, so I didn't like having
default settings in a profile that must be enabled in Eclipse project
configuration -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<war-name>/</war-name>
<!-- These solve the problem: AFAICT, each <resource /> is added to the final POM,
so declaring a resources folder in a profile didn't exclude other resources
folders declared in the default (i.e. without profiles active) configuration.
So, the solution is to change what Maven brings in from each folder depending
on the profile currently active. What follows is the default, no-profile
active configuration. -->
<res.devel.includes>**/*</res.devel.includes>
<res.devel.excludes></res.devel.excludes>
<res.local.includes></res.local.includes>
<res.local.excludes>*</res.local.excludes>
<res.release.includes></res.release.includes>
<res.release.excludes>*</res.release.excludes>
</properties>
<build>
<resources><!-- Here I declare all the resources folders, so that they will all be shown in Eclipse. Property values drive what is included and excluded. -->
<resource><!-- This is the default Maven main resource directory -->
<directory>${basedir}/src/main/resources-local</directory>
<filtering>true</filtering>
<includes>
<include>${res.devel.includes}</include>
</includes>
<excludes>
<exclude>${res.devel.excludes}</exclude>
</excludes>
</resource>
<resource><!-- This is the resources directory for when the WAR is deployed on a local standalone Tomcan installation (useful for web pages editing) -->
<directory>${basedir}/src/main/resources-local</directory>
<filtering>true</filtering>
<includes>
<include>${res.local.includes}</include>
</includes>
<excludes>
<exclude>${res.local.excludes}</exclude>
</excludes>
</resource>
<resource><!-- This is the resource directory for when the WAR will be deployed -->
<directory>${basedir}/src/main/resources-release</directory>
<filtering>true</filtering>
<includes>
<include>${res.release.includes}</include>
</includes>
<excludes>
<exclude>${res.release.excludes}</exclude>
</excludes>
</resource>
</resources>
<plugins>
<!-- Plugins configurations -->
</plugins>
</build>
<dependencies>
<!-- Dependencies declarations -->
</dependencies>
<profiles><!-- Here are the profiles. When working in Eclipse no profile is active, so the resources will be taken only from src/main/resources (as per default properties values). -->
<profile>
<id>local</id><!-- This is for when the WAR is deployed on a local standalone Tomcat instance (i.e. outside of Eclipse) -->
<properties>
<war-name>ROOT</war-name>
<!-- The resources will be taken only from src/main/resources-local -->
<res.devel.includes></res.devel.includes>
<res.devel.excludes>*</res.devel.excludes>
<res.local.includes>*</res.local.includes>
<res.local.excludes></res.local.excludes>
<res.release.includes></res.release.includes>
<res.release.excludes>*</res.release.excludes>
</properties>
</profile>
<profile>
<id>release</id><!-- This is for when the WAR is deployed on the production server -->
<properties>
<war-name>ROOT</war-name>
<!-- The resources will be taken only from src/main/resources-release -->
<res.devel.includes></res.devel.includes>
<res.devel.excludes>*</res.devel.excludes>
<res.local.includes></res.local.includes>
<res.local.excludes>*</res.local.excludes>
<res.release.includes>*</res.release.includes>
<res.release.excludes></res.release.excludes>
</properties>
</profile>
</profiles>
</project>
You may get further details in my answer here.
Related
It was a little bit hard to come up with a meaningful title, hope it will become clear enough after the explanation. I have searched through a number of Qs and As on SO, and they were all very close to the problem I am experiencing, but still not close enough.
In general, what I want to accomplish is to store project version in DB by accessing the maven property #project.version# from a .csv file which is loaded by a Liquibase script.
My maven project structure looks like this:
parentModule
pom.xml
|
---moduleA
|__pom.xml
---moduleB
|__pom.xml
---moduleC
|__pom.xml
...
Pom.xml are defined as:
**PARENT POM**
<project ...>
<groupId>com.parent</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<name>parent</name>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.2.1.RELEASE</version>
<relativePath />
</parent>
<properties>
<java.version>8</java.version>
</properties>
<modules>
<module>moduleA</module>
<module>moduleB</module>
<module>moduleC</module>
...
</modules>
<build>
<defaultGoal>package</defaultGoal>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
----------------------------------------------------------------------
**CHILD POM**
<project ...>
<artifactId>moduleC</artifactId>
<name>moduleC</name>
<parent>
<groupId>com.parent</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
</parent>
<dependencies>
<dependency>
...
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>moduleC/src/main/resources/db/changelog/</directory>
<filtering>true</filtering>
<includes>
<include>**/app_version.csv/</include>
</includes>
</resource>
</resources>
</build>
</project>
Liquibase scripts are defined in moduleC/src/main/resources/db/changelog/changelog-master.xml etc., while the .csv files with initial values are located in moduleC/src/main/resources/db/users.csv etc. In one of those csv files, I want to push #project.version# value, like this:
id;app_key;app_value;created_by;last_modified_by
1;app-version;#project.version#;system;system
Since that file is located in moduleC, I used maven resource filtering even inparentModule <build/> to filter that file so it can resolve #project.version# property, but with no luck:
<build>
<resources>
<resource>
<directory>moduleC/src/main/resources/db/changelog/</directory>
<filtering>true</filtering>
<includes>
<include>**/app_version.csv/</include>
</includes>
</resource>
</resources>
<defaultGoal>package</defaultGoal>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
There are errors, one that says that master changelog cannot be found, while in other cases just string value #project.version# is stored. Seems to me I should include app_version.csv and its location (moduleC) as resource inside <build> tag withing parentModule pom.xml, but every combination of referencing it fails. Is there a solution to reference it properly (either from parentModule or moduleC pom.xml) or there might be an easier way to store #project.version# with liquibase?
I am extremely sorry for not replying on time, was temporarily removed from the project after posting the question and was not able to access the git repository due to change of location. So far, I have tried all of the proposed actions, but with no result. In the end, what I found to work was an accepted answer posted here. I have added the build block inside mavenC module pom.xml and it worked. Nevertheless, thank you all immensely for posting and helping.
I think you need to use the maven-replacer-plugin in your build cycle. It will be configured to process the 'app_version.csv' file and output the substituted file content to the 'target/classes' folder. The subsequent packaging phase will ensure the csv file with the current pom version will be bundled into the artifact that the liquidbase tool then handles.
Looks like you're using the wrong syntax for filtering in the CSV-file. Instead of using #project.version#, try ${project.version} instead:
Check the <directory>moduleC/src/main/resources/...</directory>, if the resources plugin lives in the child POM of moduleC then there is no need for the prefix.
Try replacing with <directory>src/main/resources/..</directory>
Resources filtering of a module should be in the build of the module itself.
The project.version property will be inherited from the parent.
Be careful so the filtering does not mess with your xml files.
I think the syntax should be ${project.version}
Can you share your liquibase maven plugin configuration ?
The markup should point to your master changelog.
Here is two other solutions:
Another solution would be to create a liquibase changeset every time you create a new version. You can do it programmatically using liquibase java SDK in a spring component which run on startup of your project or create the changeset yourself.
Another solution would be to use Spring Boot Actuator to retrieve project version.
For this you need to add <goal>build-info</goal> in the goals of spring-boot-maven-plugin
I know that it is possible to configure Decorators in the "beans.xml" file that is embedded in the EAR to be deployed.
The problem is that I use the same EAR for all the environments, and the set of properties or specific configurations are stored in some folder outside the package.
I need to determine if a Decorator will be used or not "external beans.xml" or some similar mechanism (something that is outside the EAR).
Any ideas?
Thank you very much.
Normally, this won't work - standard means of enablement are beans.xml for per-archive approach and #Priority for global enablement. There is nothing like "external beans.xml".
Although there is a way to enable it with extension. You need to set up an extension and observe AfterTypeDiscovery event. From there you can
make use of public List<Class<?>> getDecorators(); which returns MUTABLE list of decorators - so you can add your own into the list (in a form of a Class). That should enable it.
Another scenario you can use, is to utilize build-time inclusion and processing.
If you know before hand, what properties activate specific decorators at build-time, then you can use maven resources, together with system properties, to define additional resources to be filtered, thus:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompant</groupId>
<artifactId>my-project-id</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<some.kind.of.selector.properties>$basedir}/src/main/resources/development</some.kind.of.selector.properties>
</properties>
<build>
<resources>
<resource>
<directory>${some.kind.of.selector.properties}</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
<profiles>
<profile>
<id>test</id>
<properties>
<some.kind.of.selector.properties>$basedir}/src/main/resources/test</some.kind.of.selector.properties>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<some.kind.of.selector.properties>$basedir}/src/main/resources/prod</some.kind.of.selector.properties>
</properties>
</profile>
</profiles>
</project>
The at build time, you can specify different beans.xml for every environment:
mvn clean install -Pprod
or even specify the property directly
mvn clean install -Dsome.kind.of.selector.properties=/path/to/additional/resources
I'm a Maven beginner, and after some trial and error, I managed to specify different properties files for the release WAR with respect to the development WAR (I tried to do it in the simplest way I could think of, but feel free to suggest any simpler solution).
So, during development, my database.properties and log4j.properties come from src/main/resources, while producing the release WAR they come from src/main/resources/release.
So far, so good.
The question is: since I'm working with Eclipse, is there a way to say, inside the POM, that the src/main/resources/release is a source folder too, so that Eclipse will list it under the other source folders in the Project Explorer, even when another developer imports the project inside his IDE (i.e. without adding that folder as a source folder manually)?
This is the relevant part of my POM:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
...
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<war-name>/</war-name>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
...
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
...
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
...
</plugin>
</plugins>
</build>
<dependencies> ... </dependencies>
<profiles>
<profile>
<id>release</id>
<properties>
<war-name>ROOT</war-name>
</properties>
<build>
<resources><!-- Replace Maven default resources directory (this could probably be achieved with a property)-->
<resource>
<filtering>true</filtering>
<directory>${basedir}/src/main/resources/release</directory>
<includes>
<include>*</include>
</includes>
</resource>
</resources>
</build>
</profile>
</profiles>
<build>
...
< sourceDirectory > src/main/java </ sourceDirectory >
< testSourceDirectory > src/test/java </ testSourceDirectory >
< resources >
< resource >
< directory > src/main/resources </ directory >
</ resource >
</ resources >
< testResources >
< testResource >
< directory > src/test/resources </ directory >
</ testResource >
</ testResources >
...
</build>
You can specify the profile that eclipse should run maven in...
Right click on project, select Maven, and include the profiles you can eclipse to work under.
Your config should be picked up by eclipse when you do a maven update project (i.e. eclipse should recognise the new source folders) once you've changed the profile.
Here's how I did it.
Basically, I declare all the resources folders I want to see in Eclipse in the default configuration (adding them in each profile didn't work since when a profile is active, the <resources /> node is appended to the final POM instead of (as I thought) replacing the existing one); then I tell Maven from which folder to copy the resources using properties, whose values are driver by the active profile.
Any comment is, of course, very much appreciated!
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<packaging>war</packaging>
<name>...</name>
<!-- My prerequisite was that when working in Eclipse no extra steps
should be required to make the IDE use the right configuration than
Configure -> Convert to Maven Project, so I didn't like having
default settings in a profile that must be enabled in Eclipse project
configuration -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<war-name>/</war-name>
<!-- These solve the problem: AFAICT, each <resource /> is added to the final POM,
so declaring a resources folder in a profile didn't exclude other resources
folders declared in the default (i.e. without profiles active) configuration.
So, the solution is to change what Maven brings in from each folder depending
on the profile currently active. What follows is the default, no-profile
active configuration. -->
<res.devel.includes>**/*</res.devel.includes>
<res.devel.excludes></res.devel.excludes>
<res.local.includes></res.local.includes>
<res.local.excludes>*</res.local.excludes>
<res.release.includes></res.release.includes>
<res.release.excludes>*</res.release.excludes>
</properties>
<build>
<resources><!-- Here I declare all the resources folders, so that they will all be shown in Eclipse. Property values drive what is included and excluded. -->
<resource><!-- This is the default Maven main resource directory -->
<directory>${basedir}/src/main/resources-local</directory>
<filtering>true</filtering>
<includes>
<include>${res.devel.includes}</include>
</includes>
<excludes>
<exclude>${res.devel.excludes}</exclude>
</excludes>
</resource>
<resource><!-- This is the resources directory for when the WAR is deployed on a local standalone Tomcan installation (useful for web pages editing) -->
<directory>${basedir}/src/main/resources-local</directory>
<filtering>true</filtering>
<includes>
<include>${res.local.includes}</include>
</includes>
<excludes>
<exclude>${res.local.excludes}</exclude>
</excludes>
</resource>
<resource><!-- This is the resource directory for when the WAR will be deployed -->
<directory>${basedir}/src/main/resources-release</directory>
<filtering>true</filtering>
<includes>
<include>${res.release.includes}</include>
</includes>
<excludes>
<exclude>${res.release.excludes}</exclude>
</excludes>
</resource>
</resources>
<plugins>
<!-- Plugins configurations -->
</plugins>
</build>
<dependencies>
<!-- Dependencies declarations -->
</dependencies>
<profiles><!-- Here are the profiles. When working in Eclipse no profile is active, so the resources will be taken only from src/main/resources (as per default properties values). -->
<profile>
<id>local</id><!-- This is for when the WAR is deployed on a local standalone Tomcat instance (i.e. outside of Eclipse) -->
<properties>
<war-name>ROOT</war-name>
<!-- The resources will be taken only from src/main/resources-local -->
<res.devel.includes></res.devel.includes>
<res.devel.excludes>*</res.devel.excludes>
<res.local.includes>*</res.local.includes>
<res.local.excludes></res.local.excludes>
<res.release.includes></res.release.includes>
<res.release.excludes>*</res.release.excludes>
</properties>
</profile>
<profile>
<id>release</id><!-- This is for when the WAR is deployed on the production server -->
<properties>
<war-name>ROOT</war-name>
<!-- The resources will be taken only from src/main/resources-release -->
<res.devel.includes></res.devel.includes>
<res.devel.excludes>*</res.devel.excludes>
<res.local.includes></res.local.includes>
<res.local.excludes>*</res.local.excludes>
<res.release.includes>*</res.release.includes>
<res.release.excludes></res.release.excludes>
</properties>
</profile>
</profiles>
</project>
I got maven project with pom.xml in which I replace placeholder in one of configuration files according to profile as follows:
<properties>
<mq-server.host>127.0.0.1</mq-server.host>
</properties>
<build>
<resources>
<resource>
<directory>src/main/resources/config</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<profiles>
<profile>
<id>staging</id>
<properties>
<mq-server.host>local.staging.com</mq-server.host>
</properties>
</profile>
</profiles>
When I build project using maven all doing fine on the other hand my project is Web project that is synchronizing with local tomcat via eclipse plugin and when I'm running project on tomcat using "play" button in eclipse, placeholder is not being changed. I wonder how I can fix it without adding special file just for developer.
I am using JNDI to read database configuration from my application's context.xml. The way I currently have this setup is to have [appname].xml in conf/Catalina/localhost. However, when I redeploy the app, this file gets overridden with an empty context file, and I have to copy the custom one back to the conf/Catalina/localhost directory. I have different database settings, etc. for my test and production servers, and so don't want to put the context file in META-INF in the WAR file, but would like to just keep it in the conf/Catalina/localhost directory. Is this possible?
Is there somewhere better to put the database configuration?
I'd also like to avoid putting the configuration in the server.xml file, although I know this is possible.
Thanks!
I would say look into using maven profiles (one for prod, one for test), and having different resource definitions for each profile. You can keep your common files in src/main/resources and then have a folder for each profile type to keep specific config files in:
src/test/resources
src/prod/resources
Then you can amend your pom to define each profile and its associated resources:
<project>
<profiles>
<profile>
<id>prod</id>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<targetPath>${project.build.outputDirectory}</targetPath>
</resource>
<resource>
<directory>src/prod/resources</directory>
<targetPath>${project.build.outputDirectory}</targetPath>
</resource>
</resources>
</build>
</profile>
<profile>
<id>test</id>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<targetPath>${project.build.outputDirectory}</targetPath>
</resource>
<resource>
<directory>src/test/resources</directory>
<targetPath>${project.build.outputDirectory}</targetPath>
</resource>
</resources>
</build>
</profile>
</profiles>
</project>
finally you can build the war using the -Pprod or -Ptest profile argument to mvn
mvn -Pprod package
Problem is that undeploy removed the webapp specific context.xml file that was installed in Catalina/localhost/.xml
If you don't want to have the file removed, you'll have to just redeploy it, not undeploy/deploy