Purpose of storing variables in web.xml? - java

A lot of the advice on the web on storing variables which may change depending on the env/other conditions is to put them in web.xml, but isn't the web.xml within the war file? even if you find the exploded war and change it, wouldn't it get overriden if you update the war file? Or does the webcontainer provide any method to configure the web.xml without tinkering with the war file?

The web.xml variables are of very limited use, in my experience - the only advantage is that it's a standard location to look for hard-coded "configuration".
There are several common solutions to get a more sensible way to configure web apps, none of which is standard:
Use system properties (which usually involves fiddling around with startup scripts, and it can be hard to get a good overview of your entire config)
Use environment variables (same drawbacks as system properties)
Read a config file from a predefined location; often from the classpath by using getResourceAsStream (IIRC that usually means putting the config files in Tomcat's lib directory)
You can also use JNDI, which has the disadvantage of being rather heavy-weight both to set up and read (if you're using vanilla Java, anyways - Spring for example has rather good support for reading from JNDI). However, JNDI is rather good because it's per-application, and not a process-global setting. If you need to run several instances of the same app on the same server, JNDI is pretty much the only option (although you can use it to just point out a config file somewhere, which makes things easier to work with).
This may be relevant to your interests: How can I store Java EE configuration parameters outside of an EAR or WAR?

Advantages of specifying Parameter Values in web.xml
Using your own settings file requires additional coding and management.
Hard-coding parameter values directly into your application code makes them more difficult to change in the future, and more difficult to use different settings for different deployments (eg: JDBC settings, mail server address).
Other developers using your code will be able to find any relevant parameters more easily, as this is a standard location for such parameters to be set.
See also:
Advantages of specifying Parameter Values in web.xml
Web.xml.EnvEntry
Referencing Environment Variables in web.xml

As far as I know web.xml does not provide ability to store custom variables. Typical way to configure your web application is to store configuration in database, separate properties/xml/json/other file, get configuration from separate web service or provide it through environment variables.
Often a mixture of all these is used. For example you can add system variable using -D switch when running your container. This variable will contain path to file or URL where your configuration can be found.
You can supply parameters using OS environment.
You choice should depend on how many parameters do you have, what kind of application are you developing and how can you configure application server or computer OS. For example if you a hosting application on server you cannot configure these ways are not for you, so DB or web service are your only ways.

The folks that work on the Tomcat container recognize the irony that you have identified and have implemented a way to work-around the issue.
The solution that they implemented for the issues that you have alluded to is to create another xml file... the context.xml file, which is read by the server.
It appears that you can edit this file and have the new values read by the Tomcat without a restart... as long as you keep the elements out of the server.xml.
I do not use Tomcat so I might be mis-interpreting the docs
The GlassFish web container supports a similar feature, but does it via a couple admin cli command (asadmin):
set-web-env-entry
set-web-context-param
There is probably web admin console support and you can set them up by editing the domain.xml. It seems like it isn't as flexible as the Tomcat implementation... but it does make it really easy to use.
You need to disable and then enable your application for the changed values to 'take'. Do not redeploy you app, since that will delete the value that you just set.

Related

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.

Is there a typical way to handle separate configurations for different tomcat instances in a Java app?

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.

Making an open source Java webapp easy to install and deploy on Tomcat, Jetty etc

I'm distributing a simple Java webapp as open source. The webapp needs to be configured before it can be run – a configuration file needs to be created, and the location of that configuration file needs to be made known to the webapp as a parameter in web.xml.
Now my question is how to best package and distribute the webapp in order to make it easy to install, and how to describe that installation process in the documentation. The options I can think of are:
Distribute the webapp as a WAR archive. Recommend that users deploy the WAR into their Tomcat/Jetty/whatever, then drop their configuration file into /webapps/myapp/WEB-INF, and modify /webapps/myapp/WEB-INF/web.xml accordingly
Distribute the webapp as source. Recommend that users should drop their configuration file into the /src/main/webapp/WEB-INF folder, then modify their /src/main/webapp/WEB-INF/web.xml accordingly, then build a WAR using Ant or Maven, and deploy that into their servlet container.
There are probably other options that I can't think of.
What setup is likely to be most convenient to users that need to install, configure and deploy the webapp?
Edit: I should add that the configuration file isn't just a few lines – it's a large file that contains a database schema and other stuff and is potentially generated using an external editor; so just providing good defaults isn't an option.
Externalize this configuration and maybe provide some default values. If you make a new version of your app, everybody will have to remember to back-up that configuration file, then redeploy and then copy back that file--> this is a nightmare.
There are many ways to put that configuration somewhere else. You can use Java Preferences for example.
I would say the WAR, although not requiring the configuration would likely be more convenient :)
What is it, loosely, that must be configured such that there isn't a sensible default value for everyone? URL string?
Providing an answer of my own, after more reading on the issue: JNDI seems to be the “official”, although somewhat heavyweight, way of solving this. With JNDI, a configuration option (like the location of the full config file I need) can be declared in the web.xml, and its actual value can be set in a per-webapp context.xml that lives in the /webapps directory of Tomcat (or the /contexts directory of Jetty). This setup has a bunch of advantages:
The big configuration file can live outside of the servlet container and webapp
The webapp can be updated without danger of losing the configuration
The distributed war doesn't need to be modified or rebuilt
Downside: It's sort of complicated, requires messing around with XML, and configuring JNDI on Tomcat works differently from Jetty (requiring twice as much documentation).
Maybe use a system property for the config file location. Can easily be passed on the command line as -Dorg.example.config.file=/foo/bar, in startup scripts or in Java code. I think I've seen some tools, e.g. logging frameworks, use system properties for similar things in webapps.

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.

How do you maintain java webapps in different staging environments?

You might have a set of properties that is used on the developer machine, which varies from developer to developer, another set for a staging environment, and yet another for the production environment.
In a Spring application you may also have beans that you want to load in a local environment but not in a production environment, and vice versa.
How do you handle this? Do you use separate files, ant/maven resource filtering or other approaches?
I just put the various properties in JNDI. This way each of the servers can be configured and I can have ONE war file.
If the list of properties is large, then I'll host the properties (or XML) files on another server. I'll use JNDI to specify the URL of the file to use.
If you are creating different app files (war/ear) for each environment, then you aren't deploying the same war/ear that you are testing.
In one of my apps, we use several REST services. I just put the root url in JNDI. Then in each environment, the server can be configured to communicate with the proper REST service for that environment.
I just use different Spring XML configuration files for each machine, and make sure that all the bits of configuration data that vary between machines is referenced by beans that load from those Spring configuration files.
For example, I have a webapp that connects to a Java RMI interface of another app. My app gets the address of this other app's RMI interface via a bean that's configured in the Spring XML config file. Both my app and the other app have dev, test, and production instances, so I have three configuration files for my app -- one that corresponds to the configuration appropriate for the production instance, one for the test instance, and one for the dev instance.
Then, the only thing that I need to keep straight is which configuration file gets deployed to which machine. So far, I haven't had any problems with the strategy of creating Ant tasks that handle copying the correct configuration file into place before generating my WAR file; thus, in the above example, I have three Ant tasks, one that generates the production WAR, one that generates the dev WAR, and one that generates the test WAR. All three tasks handle copying the right config file into the right place, and then call the same next step, which is compiling the app and creating the WAR.
Hope this makes some sense...
We use properties files specific to the environments and have the ant build select the correct set when building the jars/wars.
Environment specific things can also be handled through the directory service (JNDI), depending on your app server. We use tomcat and our DataSource is defined in Tomcat's read only JNDI implementation. Spring makes the lookup very easy.
We also use the ant strategy for building different sites (differeing content, security roles, etc) from the same source project as well.
There is one thing that causes us a little trouble with this build strategy, and that is that often files and directories don't exist until the build is run, so it can make it difficult to write true integration tests (using the same spring set up as when deployed) that are runnable from within the IDE. You also miss out on some of the IDE's ability to check for the existence of files, etc.
I use Maven to filter out the resources under src/main/resources in my project. I use this in combination with property files to pull in customized attributes in my Spring-based projects.
For default builds, I have a properties file in my home directory that Maven then uses as overrides (so things like my local Tomcat install are found correctly). Test server and production server are my other profiles. A simple -Pproduction is all it then takes to build an application for my production server.
Use different properties files and use ant replace filters which will do the replacement based on environment for which the build is done.
See http://www.devrecipes.com/2009/08/14/environment-specific-configuration-for-java-applications/
Separate configuration files, stored in the source control repository and updated by hand. Typically configuration does not change radically between one version and the next so synchronization (even by hand) isn't really a major issue.
For highly scalable systems in production environments I would seriously recommend a scheme in which configuration files are kept in templates, and as part of the build script these templates are used to render "final" configuration files (all environments should use the same process).
I recently also used Maven for alternative configurations for live or staging environments. Production configuration using Maven Profiles. Hope it helps.
I use Ant's copy with a filter file.
In the directory with the config file with variables I have a directory with a file for each environment. The build script know the env and uses the correct variable file.
I have different configuration folders holding the configurations for the target deployment, and I use ANT to select the one to use during the file copy stage.
We use different ant targets for different environments. The way we do it may be a bit inelegant but it works. We will just tell certain ant targets to filter out different resource files (which is how you could exclude certain beans from being loaded), load different database properties, and load different seed data into the database. We don't really have an ant 'expert' running around but we're able to run our builds with different configurations from a single command.
One solution I have seen used is to configure the staging environment so that it is identical to the production environment. This means each environment has a VLAN with the same IP range, and machine roles on the same IP addresses (e.g. the db cluster IP is always 192.168.1.101 in each environment). The firewalls mapped external facing addresses to the web servers, so by swapping host files on your PC the same URL could be used - http://www.myapp.com/webapp/file.jsp would go to either staging or production, depending on which hosts file you had swapped in.
I'm not sure this is an ideal solution, it's quite fiddly to maintain, but it's an interesting one to note.
Caleb P and JeeBee probably have your fastest solution. Plus you don't have to setup different services or point to files on different machines. You can specify your environment either by using a ${user.name} variable or by specifying the profile in a -D argument for Ant or Maven.
Additionally in this setup, you can have a generic properties file, and overriding properties files for the specific environments. Both Ant and Maven support these capabilities.
Don't forget to investigate PropertyPlaceholderConfigurer - this is especially useful in environments where JNDI is not available

Categories

Resources