How does one use a Maven profile? - java

I am new to maven and read bit about profiles at this and other resources
What i understood we use profile if we want to do override some default values or do some specific stuff.
In my legacy project i can see below profile where i just see overriding app.mode property. But i do not see using this property
further any where in build. I am sure what special being done here?
<profiles>
<profile>
<id>dev</id>
<properties>
<app.mode>dev</app.mode>
</properties>
</profile>
</profiles>

Maven profiles can be configured however you want them to be. Here are some use cases:
a) The project has several developers. For political and religious reasons, some of the developers are using Windows and some are using Linux. The team develops two profiles, one for Linux and one for Windows, setting project root paths, temporary folder paths, support program paths. These variables are injected into the dependency-calculation process, and subordinate processes that need this information. These profile settings are put into the settings.xml file for each programmer.
b) One of the developers is on loan to the project for a week. She doesn't want to reconfigure her computer for the project standard directory layout, etc., so she creates her own settings.xml that allows her to work in her own root directory.
c) The project uses a continuous integration server, such Hudson or Jenkins. The CI needs to have its own database for integration testing, so it would perhaps have modes like "devtest" and "prodtest" and "fulltest", depending on the requirements.

The profile would be activated if you pass -Pdev on the command line to Maven.
mvn -Pdev
See http://maven.apache.org/guides/introduction/introduction-to-profiles.html.
What this profile does is change the setting of the property app.mode from whatever it was before to dev. You will want to search your pom file (and the poms of any modules) for uses of $[app.mode} to see what's happening -- if anything.
Maven properties do not pass into Spring automatically, but it's not uncommon to find configuration in to re-export them as system properties to make them visible to Spring. This typically happens in the configuration for surefire and failsafe, but it's explicit, you'll still see ${app.mode} in a pom somewhere.

If the property being set there isn't used anywhere else in the build (did you check any sub-modules?) then the answer to your question is "In this case it does nothing."
However, in general, setting a property like that will make it available later in the build in case you needed it for something. If I had to guess, I'd say that this is the result of either an incomplete cleanup of code, or premature optimization.

I think your expectation is correct. Its used to pass on app.mode parameter to spring. See
How can I use maven profile Id value in spring bean files?
http://spring.io/blog/2011/02/11/spring-framework-3-1-m1-released/

Related

Maven global plugin configuration

I'd like to have a plugin configuration that will not exist inside the pom.xml file (and also in any project files).
What I want to do is to have a plugin that will be used only by me on my workstation. Specifically, I want to deploy my project onto the Tomcat 7 container and for this I want to use Apache Tomcat Maven Plugin, but since different developers may want to use different servers or entire ways of deployment I don't want to put this configuration into the pom.xml.
Is it possible in Maven to have such global/user-specific plugin configuration?
There are several workarounds that I can think of.
in the pom.xml a specific set for where you define your way of deployment.
ex: mvn goal -P{PROFILE_NAME}
you can just put the plugin normally in pom.xml and just don't push the changes on the repository, so it won't be available for other developers.
But the assumption seems kind of wrong, if you many developers want to work on the same project they should use the same container for deployment for persistence or if you really want to do it like this, I think the cleanest way would be for every developer to use maven profiles. And in your local settings.xml (USER_HOME/.m2/settings.xml)
just set the profile as default:
(...)
<activeProfiles>
<activeProfile>{YOUR_USER_PROFILE}</activeProfile>
</activeProfiles>
(...)
so you don't need to always specify at command line when running mvn your user profile.

Including per-user configuration in a pom.xml file

I'm working with a Java/Maven project.
My version of Maven is "Apache Maven 2.2.1 (rdebian-10)".
I need to define some custom configuration when building the project. I'm used to C projects when you configure the project with various configuration flags (configure --this --that) and then the builds proceeds accordingly to the parameters defined/found by the configure script.
I need to emulate this feature with a Java/Maven project, a configuration system would be OK but also isolating the user specific parameters in a given file should be fine as well. For example I would like to be able to define per-user specific configurations in a file included by pom.xml (for example custom.pom.xml). For example in my project I need to specify the path where the program will write temporary files and such.
My question is, is it possible to isolate the per-user specific configuration in a file included by pom.xml, and/or what is the custom way to deal with this problem with Maven projects?
Right now I'm committing a pom.xml.template file, which needs to be renamed to pom.xml and edited with the user-specific parameters, but this is awkward because when the Maven requirement changes (e.g. if a new dependency is added) the developer must update his pom.xml file. This is the relevant snippet from my pom.xml.template file:
<profiles>
<profile>
<id>production</id>
<properties>
<database.url>jdbc:postgresql://localhost:5432/myProject</database.url>
<database.user>myProject</database.user>
<database.password>myProjectPassword</database.password>
<repository_path>my/project/repo/path</repository_path>
<external_tool_bin_path>/absolute/path/to/tool/</external_tool_bin_path>
<external_scripts_path>/path/to/scripts/</external_scripts_path>
<tmpdir>tmp</tmpdir>
</properties>
<build>
<finalName>myProject</finalName>
</build>
</profile>
[...]
External tool and scripts are located in per-user specific paths (depending on system/installation).
Another way would be to use distinct profiles for each developer, which is awkward in a different way (since I don't want to hardcode user profiles in the committed pom.xml file).
Alternatively I could write a configure script but this looks overkill, and not very portable, unless I want to require the user to rely on MinGW or similar.
Thanks in advance.
I suggest you use maven profiles, and the per user configuration, keep as environment variables which are referenced from the pom's profiles as ${env.PARAMETER}.
You will have to instruct the users to set their environment as needed.
This way, you can enjoy both features and not have user specific values in source control.
I hope this helps.

How can I easily switch between environment specific runtime configuration with IntelliJ?

In most Java projects (Maven, Spring) I work on, I have the following requirements:
The packaged jar needs to run in multiple environments e.g. dev, beta, prod
The jar must not be rebuilt for each environment
Each environment requires different configuration properties
In development, I need to be able to easily switch between different environments in order to ensure that each environment is configured correctly, before deployment
So far, I have accomplished this by putting all configuration properties into separate properties files, one per environment. I then have Spring load in these files from the classpath. Then I simply alter the classpath at runtime to load in the needed properties.
This approach has worked well when I was using Eclipse since it is easy to setup multiple run configurations and alter the classpath for each one. Once this is done, switching environments is a matter of choosing the relevant run configuration.
Unfortunately, this is much harder to accomplish in IntelliJ because runtime dependencies can only be added to a module's configuration (as documented in this answer). If I try to set the classpath in the run configuration itself then this overrides all of modules dependencies, preventing the application from running at all (as documented here). Therefore, to switch environment, I must re-edit the modules dependencies every time.
Is there an easier way of achieving this with IntelliJ?
If not, is that because my configuration requirements, or my solutions to them, are unusual? In which case, is there a better way?
Previous Approaches
I have already tried and rejected a number of approaches:
Maven Profiles - These require rebuilding my jar whenever I want to switch environment. Apart from being annoying and dangerous, this seems entirely inappropriate for runtime configuration (but ideal for buildtime configuration). This seems like a great way to risk getting a broken build into production as you aren't testing against the same jar that actually gets deployed. Yuck!
Spring Profiles - These look much more appropriate but appear to require all configuration to be deployed with the jar and distinguished by filename e.g. dev.properties, prod.properties. You can then select which one you want by passing a variable at runtime. This is much better but I don't want my prod.properties to find their way into version control, nor get deployed with the jar itself. Therefore, it still seems necessary to modify the classpath in order to include them which I can't easily do in IntelliJ
When I ran in similar problems, I started loading my properties with a helper class. The helper class will look for a System property that contains either the environment or the absolute/classpath-relative path to load the properties from.
That way, the code is always the same and I don't have to change the project's configuration. I just use different launch configs that set different properties.
The main drawback here of course that test / dev configs leak into prod. With Maven, the simple solution is to move those into a new module and set the scope for it to provided. That way, the module will be visible inside of Maven but it won't be included during WAR packaging, etc.
I do something similar to what #AaronDigulla does but I overlay the properties. That is I first load all the properties from property files in the classpath (using Spring). Then I search for specific variable name and value (in the following order):
in servlet init parameters
then in system properties
finally then environment variables
This variable name and value contains the path of property files that will override/overlay the properties loaded from the classpath. Also each property loaded will perform the same type of resolution (that is system properties, environment variables and servlet init parameters override the properties from the property file).
Now all developers are pushed to use the default configuration that is in the classpath (ie use the same username/password for their database). Those that have different settings can use the above 3 different ways to override.
You can use the same bootstrapping configuration code that I use which will load all property files in classpath*:META-INF/spring/*.properties first then it will load all property files with the file/classpath URL set in the property/environment/init parameter variable called MyConfigParameter (if set).
Then finally after the property files are loaded it will set the Spring profiles by pulling the property/environment/init parameter called MyConfig.profiles (comma separated).
So in production you might set the environment variable MyConfigParameter=file://etc/myapp/*.properties.
Then in one of those property files you might set MyConfig.profiles=production,postgres which will set the Spring profile production and postgres.
Your Maven profiles for releasing/building on your build machine can obviously adjust the MyConfigParameter in the environment using System Properties. However you will have to tell surefire unit testing specifically about this property (especially if forking):
<!-- Surefire setup -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.9</version>
<configuration>
<printSummary>false</printSummary>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
<systemPropertyVariables>
<MyConfigParameter>${MyConfigParameter}</MyConfigParameter>
</systemPropertyVariables>
</configuration>
</plugin>
<!-- Profile to change MyConfigParameter -->
<profiles>
<profile>
<id>release</id>
<activation>
<property>
<name>env</name>
<value>release</value>
</property>
</activation>
<properties>
<MyConfigParameter>file://etc/MyApp/*.properties</MyConfigParameter>
</properties>
</profile>
</profiles>

Maven if-else like behaviour

I have a multi-module project in maven, where other projects need to use the jars according to the environment.
We have like 4 enviroenments
1.Dev
2.UAT
3.QA
4.PROD
There is a project which wants to use projectname-dev.jar for all three environments (i.e dev,uat,QA ) and projectname-prod.jar for PROD.
I did a google search for something like an if-else logic which I can use in maven profiles.
I can still create a new property for this specific project and change it to '-prod' when it goes into produciton, but thats an extra overhead that I do not want to add.
Are there any plugins I can use for this which will help me get the above work done without adding any kind of manual overhead to it.
Thanks
Isn't it what Maven profiles are intended to do? You can define different property values in different profiles and then activate one of them when performing a build.
Not exactly what you're looking for, but you shouldn't be building environment-specific artifacts. Contrary to popular opinion, this is a terrible misuse of Maven profiles. You should build one artifact that is suitable for any environment. The configuration of the artifact, which is external to the artifact, is what's specific to each environment.

How can I disable Maven install and deploy phases using profiles?

My Maven project contains 3 different profiles, dev, stage, and prod, that contain different configuration settings. I would like to make it so that the install and deploy phases cannot be executed (or execute but do nothing) if the active profile is not prod, to keep dev and stage builds out of the repo. Is there a way to do this?
I'm guessing it involves adding the <plugin> to the dev and stage profiles and manually binding it to a "none" phase or something like that.
If that's what you really want to do, then just run the "package" phase on dev and staging, and in your maven settings file the provided user should not have write privileges to the repository.
What I would recommend doing, though, is to keep your configuration files outside of the build artifact, so that you only have one build that gets promoted between environments. As part of a script for deploying a build, you can automatically copy the correct settings, getting a similar effect.
Regardless of whether how you want to do this is the best idea, what you could do is use the Maven Enforcer Plugin to validate that the profile property is set to the value of your 'prod' profile. The plugin binds by default to the validate phase, so you would need to bind it to the package phase, or only the 'prod' profile will be usable.
The specific recipe I would use for this:
There's a built-in rule called requireProperty you can use to make assertions on properties and their values. You could set a property from your prod profile and then (outside any profile) configure the enforcer plugin to check to see that said property is set to the value you expect. This is hokie, however.
I strongly suggest that you externalize environment-specific configuration values into property placeholders and use profiles only to set those values rather than switching out environment-specific config files or affecting the contents of the artifact that you're generating.

Categories

Resources