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());
Related
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".
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.
Our system is split up into different environments, each one as a separate Tomcat instance.
Development (Windows)
QA: Accessed by our QA department (Linux)
Production: Live site, accessible to customers. (Linux)
Each of these environments rely on separate databases, and several other web services. This means we need to keep track of the various URLs, usernames, and passwords that are all different. Many of these settings are shared across several apps, so it would be ideal to have them all in one place to remove duplication.
Right now we have config files in the application itself. We use Maven profiles to fill in the different config settings when we build the app. But this is clumsy because we have to build a different WAR for each environment.
Where is a good place to store the config files so that we can deploy the same WAR file to each server?
I've done a fair amount of research on this already. But I haven't found an solution that completely makes sense to me yet.
Separate Config Directory
Define a directory to hold config files. Such as /opt/config on linux.
I like this idea, but how do I tell Tomcat where this directory is? I see references to context.xml, but every example I've seen puts the context.xml in the META-INF folder inside the WAR. Is there a way to configure this outside the WAR?
System Property to define environment
This involves setting a system property, and then using some sort of if/else or switching logic to load the appropriate config file. This seems workable, but a bit messy. But where/how do you set this property? I typically start tomcat with ./startup.sh. Do I add arguments to that command or is there another configuration somewhere?
JNDI
I don't think this is an option for us. Every tutorial I've looked at for this seems to be dependent on LDAP or something similar. To my knowledge we don't have that available to us, and it seems like too much overhead to get set up for only a half-dozen config files.
Use system property that refers to the location where your configuration file or directory is located. In this case you can manage different environment easily and no if/else logic is needed.
You application can have hard coded value of config file path, that will allow running application without any additional system property. The application however should fail to start if mandatory data is not found.
Concerning to partial sharing of data among environments.
You can split your data into several files by categories. Some files will be shared, some other different for different environments. You can even develop your own mechanism of references between data files.
However better approach is using some ready-to-use packages. For example Spring framework supports very flexible configuration mechanism. However if you already have Spring-less application introducing this framework for configuration only seems like an overkill. In this case take a look on Apache Commons Configuration package.
Worked with my team on this and we came up with what we feel is a cleaner approach. While every tutorial I found put the context.xml inside the WAR, it can also be placed in the conf folder of the Tomcat directory.
This works for us as all our servers are Tomcat based. So each server can have it's own context.xml which has a property pointing to the config folder on that particular server.
I want to store configuration for a web project outside of the web project (ear/war file).
The application shouldn't know in which container it's running (WebSphere/JBoss etc.).
What is the best way to handle this?
Is JNDI a clean way? If JNDI can solve my problems, how should I configure it? (Custom Objects?)
In my case are there only simple Key=>Value pairs (String,String) for SOAP/WS endpoints.
See this question for reading properties file outside of the WAR file.
See this question for reading variable values from JNDI. I believe that this is the best solution. You can read a String variable with this code:
Context initialContext = new InitialContext();
String myvar = (String) initialContext.lookup("java:comp/env/myvar");
The above code will work on all containers. In Tomcat you declare the following in conf/server.xml:
<GlobalNamingResources ...>
<Environment name="myvar" value="..."
type="java.lang.String" override="false"/>
</GlobalNamingResources>
The above will create a global resource. It is also possible to define a resource in the context of application. In most containers the JNDI resources are available through a MBeans Management Console. Some of them offer a graphical interface to edit them. At most an application restart is needed, when a change is made.
How JNDI resources are defined and edited is container specific. It is the job of the configurator/administrator to apply the appropriate settings.
These are the benefits offered by JNDI:
You can define default values of the parameters in the WAR/EAR file.
Parameters are easily configurable at the container.
You don't need to restart the container when you modify the value of a parameter.
We had a similar configuration requirement when deploying a webapp for different developers, and on Amazon's EC2: how do we separate configuration from the binary code? In my experience, JNDI is too complex, and varies too much between containers to be used. Also, hand-editing XML is very susceptible to syntax errors, so was the idea was thrown out. We resolved this with a design based on a few rules:
1) only simple name=value entries should be used
2) new configurations should be loadable by changing only one parameter
3) our WAR binary must be reconfigurable w/o repackaging it
4) sensitive parameters (passwords) will never be packaged in the binary
Using .properties files for all configuration, and using System.getProperty("domain"); to load the appropriate properties files, we were able to meet the requirements. However, the system property does not point to a file URL, instead we created a concept we call "domain" to specify the configuration to use. The location of the configuration is always:
$HOME/appName/config/$DOMAIN.properties.
So if I want to run my app using my own configuration, I start the app by setting the domain to my name:
-Ddomain=jason
on startup, and the app loads the file:
/home/jason/appName/config/jason.properties
This lets developers share configurations so we can recreate the same state of the app for testing and deployment without recompiling or repackaging. The domain value is then used to load .properties from a standard location, outside of the bundled WAR.
I can completely recreate the production environment on my workstation by using the production configuration like:
-Ddomain=ec2
which would load:
/home/jason/appName/config/ec2.properties
This setup allows us to do have dev/QA/release cycles with exactly -one- set of compiled binaries, using different configurations in each environment. There's no risk of having passwords/etc bundled in the binaries, and people can share their configurations to recreate issues that we're seeing.
I use an environment variable to point to a URL (which probably is a file:// URL) that has my configuration in it. This is very simple to setup and doesn't require the JNDI infrastructure.
Here's some sample code (typed from memory - I haven't compiled/tested this):
public void loadConfiguration() {
String configUrlStr = System.getenv("CONFIG_URL"); // You'd want to use a more
// Specific variable name.
if(configUrlStr == null || configUrlStr.equals("") {
// You would probably want better exception handling, too.
throw new RuntimeException("CONFIG_URL is not set in the environment.");
}
try {
URI uri = new URI(configUrlStr);
File configFile = new File(uri);
if(!configFile.exists()) {
throw new RuntimeException("CONFIG_URL points to non-existant file");
}
if(!configFile.canRead()) {
throw new RuntimeException("CONFIG_URL points to a file that cannot be read.");
}
this.readConfiguration(configFile);
} catch (URISyntaxException e) {
throw new RuntimeException("Malformed URL/URI in CONFIG_URL");
}
}
you can just store then is a normal java properties file that is on the class path and just load the properties?
it is straightforward and pretty simple.. unless I am missing something
My favorite places are : Environment Variables and Properties files (as suggested by Jared and kgiannakakis above.)
Database Table storing environment properties
However one other simpler solutions is to have Database table storing environment properties.
If your application uses database
this is relatively easy to setup
Gives really easy way to control/change values
It can be integrated in the process well by making it part of DB scripts
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.