Where to define properties in SpringBoot which are common across all environments? - java

I have few properties common to all environments (Ex. spring.jpa.properties.hibernate.ejb.interceptor) which I have kept in application.properties under resource directory.
I have DB properties defined in environment based properties file which I pass externally through command line while starting the app:
java -jar -Dspring.config.location=<path-to-file> mySpringBootProject.jar
However, spring.jpa.properties.hibernate.ejb.interceptor is not being set when I am passing properties file externally.
Do I need to define common properties even in external file?
Or is there a way I can define them in a single place which is reused when not overridden?

You can use multiple profiles to do this. For example, create property files:
application-dev.properties
application-prod.properties
application.properties
Place your environment-specific properties in the application-${env}.properties file and your common properties in application.properties.
There are multiple ways to tell spring which profiles to use, for example the --spring.profiles.active flag.
See the Spring Boot documentation for more details about the property file search order.

application.properties will normally be overridden by Boot projects. Instead, you can add a properties file in a non-conflicting location (such as src/main/resources/my/package/foo.properties) and use #PropertySource on your autoconfiguration file to add it.

Properties is a file extension for files mainly used in Java related technologies to store the configurable parameters of an application. The advantage of using properties file is, we can configure things which are environment specific (or are prone to change over a period of time) without the need of changing anything in code. Hence inheriting common properties is not a good approach. If a property seems to be static for all e.g. environments, then it shouldn't be a property.
But, it could be we have multiple development environments and production, where we would share same properties in the environments meant for development purposes and different properties for production. In this case we could create a common properties file and inherit it in all our environment specific properties files. Another scenario could be, that at the moment of development the property is same for all environments, but we would like to provide the option of changing it in the future, when required.

Related

Sharing common properties across different property files in Java

I have two properties files in my Java project. Both of them have some common properties, e.g.
PropertyFile1.properties
p1=v1
p2=v2
p3=v3
p4=v4
PropertyFile2.properties
p1=v1
p2=v2
p23=v23
p24=v24
Here, p1 and p2 properties are common to both files.
I need to be able to separate out such common properties in a different file, e.g.
Common.properties
p1=v1
p2=v2
And then refer this common.properties inside PropertyFile1 and PropertyFile2.properties files.
Basically, I need to be able to inherit the common properties and their values across different property files.
Is this possible?
I've come across following link but not sure how to use it for my requirement.
http://commons.apache.org/proper/commons-configuration/
Thanks.
Properties is a file extension for files mainly used in Java related technologies to store the configurable parameters of an application. The advantage of using properties file is, we can configure things which are environment specific (or are prone to change over a period of time) without the need of changing anything in code. Hence inheriting common properties is not a good approach. If a property seems to be static for all e.g. environments, then it shouldn't be a property.
But, it could be we have multiple development environments and production, where we would share same properties in the environments meant for development purposes and different properties for production. In this case we could create a common properties file and inherit it in all our environment specific properties files. Another scenario could be, that at the moment of development the property is same for all environments, but we would like to provide the option of changing it in the future, when required.
There are two ways to inherit properties from a common properties file:
include
includeOptional
The value of these properties is the name of a file on the disk, that file will be included into the configuration. The difference between "include" and "includeOptional" is that if the file value is absent, processing continues with "includeOptional" but stops with "include".

What are Best Practice Recommendations for Java EE 7 Property File Configuration?

Where does application configuration belong in modern Java EE applications? What best practice(s) recommendations do people have?
By application configuration, I mean settings like connectivity settings to services on other boxes, including external ones (e.g. Twitter and our internal Cassandra servers...for things such as hostnames, credentials, retry attempts) as well as those relating business logic (things that one might be tempted to store as constants in classes, e.g. days for something to expire, etc).
Assumptions:
We are deploying to a Java EE 7 server (Wildfly 8.1) using a single EAR file, which contains multiple wars and one ejb-jar.
We will be deploying to a variety of environments: Unit testing, local dev installs, cloud based infrastructure for UAT, Stress testing and Production environments. Many of our properties will vary with each of these environments.
We are not opposed to coupling property configuration to a DI framework if that is the best practice people recommend.
All of this is for new development, so we don't have to comply with legacy requirements or restrictions. We're very focused on the current, modern best practices.
Does configuration belong inside or outside of an EAR?
If outside of an EAR, where and how best to reliably access them?
If inside of an EAR we can store it anywhere in the classpath to ease access during execution. But we'd have to re-assemble (and maybe re-build) with each configuration change. And since we'll have multiple environments, we'd need a means to differentiate the files within the EAR. I see two options here:
Utilize expected file names (e.g. cassandra.properties) and then build multiple environment specific EARs (eg. appxyz-PROD.ear).
Build one EAR (eg. appxyz.ear) and put all of our various environment configuration files inside it, appending an environment variable to each config file name (eg cassandra-PROD.properties). And of course adding an environment variable (to the vm or otherwise), so that the code will know which file to pickup.
What are the best practices people can recommend for solving this common challenge?
Thanks.
I don't know what is best practice, but here is what we do.
(Note however that this only works well for one installation per application per server and will fail when one wants to use multiple deployments per server, say for multitenancy deployments).
CDI injection of properties values
We use a somewhat sophisticated CDI injection approach to inject configuration values from .properties files directly into beans, like this:
#Inject #ConfigurationValue(value="amazonS3FileContentsAccessKey")
private String accessKey;
The corresponding #Producer bean reads configuration files from the class path and from a given "local" location:
global/local .properties files
Each EAR contains a "global" .properties file on the class path for configuration values that change seldom and/or usually remain consistent through environments (such as days for something to expire). Further, the global configuration file contains sane default values (e.g. "localhost" for database server hostname). The global properties files (there are multiple, see below) are maintained in the source tree.
For every development environment/installation/server/deployment, there (possibly) is a "local" properties file that contains the local settings that overwrite the global configuration's settings, e.g., database names, passwords etc.
The expected path to "local" properties files is configured in the global configuration file (e.g., /etc/myapp/local.properties) or C:\myapp\local.properties.
Actually, we even allow substitution of some variables in the filename for the local configuration files, such as "${hostname}". The original idea was that the local properties could also be maintained in some central source control by distinguishing them by hostname (local.machineA.properties, local.machineB.properties), but we don't use that at the moment, because our production settings are the same on all machines (Amazon S3 keys, database password/host etc).
Assembling for dev, testing, production
We assemble different EARs depending on the stage of development using Maven profiles.
On assemply, the desired global.${profile.name}.properties file (where profile.name is, e.g., dev or production) is copied to the expected global.properties file in the classpath.
For example, dev and testing share a common AmazonS3 secret/bucket, which is configured once for all developers in the configuration.dev.properties file, while the configuration.production.properties does not contain our production keys.
Furthermore, our dev and testing environments have debugging enabled and configured in, say web.xml, but of course staging and production have not. Our .properties-based approach cannot change files such as web.xml, but with Maven build profiles it's easy.
There can be many possible solutions to your question depending upon everyone's experience. So, why not let's try some already discussed ideas. Please have a look at
Configure Java EE 6 for dev/QA/prod
How to configure Java EE application to apply different settings
Hope these two will give you some common understanding of how you can build the whole environment by using maven.

Best way to reference the file system in a spring mvc app

In a spring mvc application, what is the best way to reference the filesystem?
Say I want to know the root of my applications path?
Should I create a properties file and hard code this value in the property file, then create different versions for production and development environments?
I might want to reference a file outside of my application also, so I guess a property file is best suited for this correct?
I understood your question as a config/release problem, not coding problem. If you want to access file (say with absolute path) there are different ways to achieve it:
if you use maven to build your app. create maven profile with corresponding property, e.g. file.path and at build-time fill the property to spring bean (e.g. a String)
create different properties files, which containing config parameters for different environments. and let maven fill the placeholder in spring conf, which properties file should be used.
use spring profile. put server-relevant beans in profiles, and your application choose the right profile (the set of beans) at runtime.
well if you have different databases for different environments, you could consider to save some config parameters in a config table. And application loads those data when it starts or when it needs. At least this is another option.
did that answer your question? or I am just talking about something else?...
Get real path and complete your remaining action
String realContextPath = session.getServletContext().context.getRealPath(request.getContextPath());

How to Use Different META-INF/context.xml Files For Development and Production Environments

In Tomcat (and some other servlet containers) I can store information about my JDBC DataSource in META-INF/context.xml. This is very useful.
However, the settings for my JDBC DataSource can be different in my development and production environments. I'd like to know how other people deal with these differences in an elegant way, specifically how can I set up a context.xml for my development environment and one for my production environment in the most hassle-free manner.
You can create different files for specific builds. For example, create:
development.context.xml
production.context.xml
Then, you can control which context file is used in your build.xml file. Basically, setup a prompt for which type of build you would like to use. When you select development, it uses the development context file. When you select production, it uses the production context file.
I would do the same as Kevin mentioned. If you're using Maven you would use "profiles".
If you want to learn more about Maven profiles read this: Introduction to Build Profiles
Personally I wouldn't store configuration information like that in context.xml (perhaps in another properties file or something), but the general way for something like this is to have your build script package different versions of the configuration file into the WAR/EAR/whatever. You could have your build script decide whether to use the "dev" or "production" configuration file based on parameters you pass in, running different targets, etc.
Something I use often is the task in ant to replace certain tokens in files with values from a filters file; and swap which filters file is used depending on which environment I am targeting.

Can I use a single war file in multiple environments? Should I?

I have a Java web application at my work and I'd like simplify how we deploy to our DEV, QA, and PROD environments.
The application reads in a series of properties at startup, and the properties files are different for dev, qa, and prod. Whenever I want to deploy to a certain environment I drop the environment-specific properties file into my app folder, build the war, and then deploy it to one of the three tomcat 5.5 servers.
What I would like to do is have to have a single .war that has the properties for all environments, and have the app interrogate the webserver during the init process to figure out which environment the app is in, and hence which properties to load. Is there an easy way (or, failing that, a standard way) to do that?
This really depends on what you are using those properties for.
Some (like data source, for example) can be configured in the container itself (Tomcat 5.5. JNDI Resources, see JDBC sources section as well).
Others (application-specific) may indeed need to be properties. In which case your choices are:
Bundle properties within WAR file and load the appropriate subset based on some external switch (either environment variable or JVM property)
Setup a deployment process on each of your servers where war is unpacked and a property file (located in a predefined location on that server and specific to that server) is copied over to WEB-INF/classes (or other appropriate place).
As far as "is this a desirable goal" goes - yes, I think so. Having a single WAR to test in QA / staging and then deploy to production cuts out an intermediate step and thus leaves less chances for mistakes.
Update (based on comment):
Item #1 above refers to an actual environment variable (e.g. something that you set via SET ENV_NAME=QA in Windows or ENV_NAME=QA; export ENV_NAME in Linux). You can the read its value from your code using System.getenv() and load the appropriate properties file:
String targetEnvironment = System.getenv("TARGET_ENV");
String resourceFileName = "/WEB-INF/configuration-" + targetEnvironment + ".properties";
InputStream is = getServletContext().getResourceAsStream(resourceFileName);
Properties configuration = new Properties();
configuration.load(is);
But yes, you can instead define a scalar value via JNDI (see Environment Entries in Tomcat doc) instead:
<Context ...>
<Environment name="TARGET_ENV" value="DEV" type="java.lang.String" override="false"/>
</Context>
and read it within your app via
Context context = (Context) InitialContext().lookup("java:comp/env");
String targetEnvironment = (String) context.lookup("TARGET_ENV");
// the rest is the same as above
The thing is, if you will be using JNDI anyway, you might as well forgo your property files and configure everything via JNDI. Your data sources will be available to you as actual resources and basic properties will remain scalars (though they will be type safe).
Ultimately it's up to you to decide which way is better for your specific needs; both have pros and cons.
What you do is an accident waiting to happen... One day a DEV war will end up in de PROD server, and by some law superior to all laws of nature that problem will be detected at 2AM. Can't explain why this is the case, but some day that will happen. So one war is in my opinion definitely a good idea.
You can set a system property in the respective JVM's (-Dcom.yourdomain.configpath=/where/you/store/configfiles) and fetch this property with
String value = System.getProperty("com.yourdomain.configpath", "defaultvalue_if_any");
The default value could point somewhere inside the war (WEB-INF/...), or if there's no default, be used to make some logging noise during load to warn for misconfiguration). Also note that this technique is not platform dependent, so you dev machine can be a Windows box and the server a Linux machine, it can cope with both. We normally create a subdir per application in this configpath, as several applications use this system on a server, and we want to keep things tidy.
As an added bonus, you don't risk to trash manually tweaked property files on a PROD server this way. Just don't forget to include the path where the files are stored in a backup scenario.
I think a single war file is a good way to go, because its nice to have confidence that the binary you tested in DEV is exactly the same as in Production. The way we do it, we keep the configurations in a separate properties file, outside the war, but in the app server's class path.
If you want to keep all the properties inside the war (which does make deployment easier, because then you don't have to also deploy a properties file), you could keep a single properties file in the classpath that identifies the server environment type, and use that to key values in the properties file within your .war file. The external properties file may also be a good way to go for maybe some high-level configurations that don't change much and are used across a number of war files.
Probably the simplest way would be to set up an environment variable that differs between the application services and use this to determine which property file to read.
Another possibility would be to store the properties in a database, and use a datasource that exists under a standard JNDI name, but points to a different place in the various environments.
I prefer the one EAR or one WAR approach. There is something re-assuring and often required from a security standpoint about taking the exact same EAR that was just tested and moving it directly into the next state (test > Production).
There are also many options besides properties files provided by the container. Often the container has a nice UI to maintain those values and resources when you are dead and gone.
There are countless examples of using a database backed ResourceBundle.
For example, the Spring framework has several mechanisms to make that happen without much effort. Starting with PropertyPlaceholderConfigurer
Set a Systemproperty at startup that points to the location of your properties file, and then in your application pull in this property and load your settings. Another thing I do is have two properties file, something like default.properties, and external.properties. They contain the same properties, but the default.properties contains the default(works most of the time settings), this file goes in the war. Then if you deploy to an env. you look for the external.properties, if found that is used, if not then you rollback to the default.properties. This provides a nice way to override properties if needed, but also have a default setup. This works in a lot of my deployments, but may not in your scenario.
Absolutely a single WAR is the best way to go. Set the resources using the same JNDI names in each environment, and if you need to detect which environment you're in for business logic purposes, use a System property on Tomcat startup.
A single build (war) is certainly the right approach.
However, when it comes to environment specific configuration, the best way to go is to ensure that all configuration .properties files should not be pushed to all the environments. e.g. PROD properties files should be copied to DEV or UAT.
Spring profiles also should be avoided as they lead to convoluted configuration management.

Categories

Resources