Set Spring profile in Tomcat web app with no web.xml file - java

I have a webapp that is a RestEASY JAX-RS application and that uses the latest servlet specifications, such as Java EE annotations so that I don't need to create a web.xml file.
The webapp is bundled as foobar.war and dumped into the webapps directory in Tomcat. In fact the same foobar.war is deployed twice in the same Tomcat instance, once as foobar.war and the other as foobar#demo.war (which maps it to foobar/demo as you know).
I configure each mounted webapp by placing conf/Catalina/localhost/foobar.xml and conf/Catalina/localhost/foobar#demo.xml files, that look like this:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Environment name="foo" type="java.lang.String" value="bar"/>
</Context>
In my JAX-RS application I pull in the value of foo from JNDI using java:comp/env/foo.
So now I added a Java-based Spring configuration named FooBarConfiguration. I load it in my JAX-RS application using new AnnotationConfigApplicationContext(FooBarConfiguration.class). That all works fine.
So now I've added two profiles to FooBarConfiguration, one named foo and one named bar. But now... how do I tell the webapp which Spring profile to use? (Remember that I have no web.xml file.) Obviously I have to set spring.profiles.active somewhere. But where?
Because the documentation spoke of "environment" and "JNDI", I crossed my fingers and added an environment variable to conf/Catalina/localhost/foobar.xml:
<Environment name="spring.profiles.active" type="java.lang.String" value="foo"/>
No luck.
I can't set a system property, because that will apply to all the webapps, and the idea here is that each foobar.war instance (foobar.war and foobar#demo.war) could each have a different profile specified.
I suppose I could manually pull it out of the Tomcat environment myself, using java:comp/env/spring.profiles.active, but then where do I set the value? (I thought maybe AnnotationConfigApplicationContext would have a constructor where I could set the profile, or at least have a profile setting, but that also seems to be missing.)
(Plus if I'm manually pulling out the setting from JNDI and setting it myself, I might as well switch to the more lightweight Guice and manually load the modules I want. I'm only using the humongous, clunky Spring because it promised to allow easy selection of profiles.)
How can I indicate, external to my WAR file, on a per-webapp basis, which Spring profile I'm using?

You can set active profiles in many ways. Since you were searching for it via AnnotationConfigApplicationContext constructor, the one described here in spring docs might suit you.
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.refresh();

The solution is to use AnnotationConfigWebApplicationContext instead of StandardServletEnvironment.
The trick is to get Spring to use a StandardServletEnvironment, which looks in several places including JNDI java:comp/env/... for spring.profiles.active. See http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-property-source-abstraction .
My problem is that AnnotationConfigApplicationContext uses a StandardEnvironment, which only looks in a few places for the profile designation. Switching to a AnnotationConfigWebApplicationContext made Spring use a StandardServletEnvironment:
final AnnotationConfigWebApplicationContext webContext =
new AnnotationConfigWebApplicationContext();
webContext.register(FooBarConfiguration.class);
webContext.refresh();
Now my webapp environment configuration in conf/Catalina/localhost/foobar.xml works:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Environment name="spring.profiles.active" type="java.lang.String" value="foo"/>
</Context>

Related

How to run two WAR files with different spring profiles on a tomcat server?

I would like to run my spring application two times, in parallel, on the same tomcat server. One time with a production profile and one time with a dev profile.
I also would like to build one single WAR for the two profiles.
I've successfully integrated profiles in my application with #Profile annotations. I've successfully deployed the two WAR files on my tomcat server.
What I need is a mean to activate a different profile on each of theses two applications, with the constraint that these two applications use a copy of the same WAR file and that the two applications should run in parallel.
So WebApplicationInitializer and web.xml seem not an option.
For the record:
To activate the dev spring profile on the application in application-dev.war
Create a file <CATALINA_BASE>/conf/Catalina/localhost/application-dev.xml
With the following content:
<Context>
<Environment name="spring.profiles.active" value="dev,server" type="java.lang.String" override="false" />
</Context>
This set the spring.profiles.active property to dev,server for the application run by application-dev.war.
Thanks to this answer: https://stackoverflow.com/a/26653238/1807667
P.S.: With autoDeploy=true in server.xml, the configuration files disappear on tomcat restart.
Solution is to add <Context reloadable="true"> in <CATALINA_BASE>/conf/context.xml but beware that according to documentation :
This feature is very useful during application development, but it
requires significant runtime overhead and is not recommended for use
on deployed production applications.
and moreover using <Context reloadable="true"> does not solve fully the issue the configuration files still disappear for some restart.
P.S.2: There is no docBase attribute in the Context element, see this question.

How to implement environment-specific init parameters in my tomcat application

I'd like to be able to implement a configuration-less deployment for my java application (tomcat7, spring-mvc). For example, right now we are considering creating one context.xml file for each environment (prod, stage, dev) we deploy to:
context_prod.xml
context_stage.xml
context_dev.xml
context.xml (for localhost)
On each of our servers we would have a symlink context.xml which would point to the appropriate context file (e.g. context_prod.xml). So when we deploy, we don't have to worry about changing database references, keys, etc.
I feel like there's probably a better way to do this; perhaps one that is built into spring?
Spring has recently added the functionality to handle environment configuration:
http://blog.springsource.com/2011/02/11/spring-framework-3-1-m1-released/
This still seems a little bit complicated for me and I have done exactly what you are asking in my own Spring MVC applications for our logging. In my DispatcherServlet configuation I have this line:
<context:property-placeholder location="classpath*:/system.properties"/>
<util:properties id="mySystemProperties" location="classpath:/logging/log4j-${system.runMode}.properties" />
system.runMode is just an env variable that we created and is set in CATALINA.SH at startup like this: Setting environment variable TESSDATA_PREFIX in Tomcat
I then use Spring EL to reference any values I want and it works per environment. As you can see I did this for our logging and have a different logging configuration file per environment.
You could use Spring #Profile introduced in Spring 3.1. In your case you could use profiles like dev, stage, prod etc.
This profile value could be initialized run time. So when your application started, Spring could fetch appropriate profile based on configuration.
You could setup profile from environment variable, via deployment descriptor (web.xml) etc. This Spring source tutorial could be interesting for you.
I personally using Maven build to replace the profile value during build time in the web.xml. In the build time I passed profile value as build argument.

Optional JNDI Binding For Websphere Web Application

I would like to have optionally supplied information available to my webapp when deployed to Websphere (we are using 8.5) via the application's JNDI context.
I know that I can put a resource-ref or resource-env-ref in my web.xml but when I do that WAS will require me to supply a binding for it and deployment time.
My application will look in JNDI for certain values and adjust its behavior if found, but will function fine with default behaviour if it does not find values in JNDI. How, in WAS, do I supply a binding (just for a string or a URL) for my webapp without declaring a dependency on it in a resource-ref or resource-env-ref in my web.xml.
I know how to do this in Tomcat, I just put a Environment entry in the context.xml, like this:
<Environment
name="com.myorg.config"
value="http://localhost:8081/suff"
type="java.lang.String"/>
You can use an #Resource String lookupName; to accomplish the same in a portable manner. You will be prompted for a value at deployment time, but you can specify nothing, which will cause no value to be injected.

How to read external properties files in WebSphere?

I developed a sample web application which will read the data from an external properties file. The properties file is in the source folder in my system and is not included inside the WAR file.
The property file is accessed like this:
Properties prop = new Properties();
//File f1 = new File("Property.properties");
prop.load(getClass().getClassLoader().getResourceAsStream("Property.properties"));
How do I access this property file externally inside the WAR file?
What changes have to be made in the code to read it in the WAR file?
I think the most versatile approach is to define a simple environment entry as described in the section EE.5.4 Simple Environment Entries of Java™ Platform, Enterprise Edition (Java EE) Specification, v5.
From the section (page 68):
A simple environment entry is a configuration parameter used to
customize an application component’s business logic. The environment
entry values may be one of the following Java types: String,
Character, Byte, Short, Integer, Long, Boolean, Double, and Float.
You may also use URL connection factory as described in the section EE.5.6.1.4 Standard Resource Manager Connection Factory Types of the specification.
The Application Component Provider must use the java.net.URL resource
manager connection factory type for obtaining URL connections.
Both require a definition of a resource reference in the deployment descriptor WEB-INF/web.xml of your web application so you can inject the value using #Resource or use JNDI API with java:comp/env as the entry point.
The benefit is that you can change the configuration of your web application without having to recompile the code as well as let you change it using an application server's administrative tools your admins are accustomed with.
In web.xml you define the resource reference.
<resource-ref>
<res-ref-name>propertiesURL</res-ref-name>
<res-type>java.net.URL</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
<resource-ref>
<res-ref-name>propertiesPath</res-ref-name>
<res-type>java.lang.String</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
Then in your code you use the following to access the values:
#Resource
String propertiesPath;
#Resource
URL propertiesURL;
With this you met the requirements of Java EE and you can use propertiesPath or propertiesURL as if they were passed as input parameters to your methods.
Now, it's time to meet expectations of WebSphere Application Server.
What you defined are logical names that need to be mapped to their administered names (an application server knows about and can provide to the application).
In WebSphere Application Server you use WebSphere Binding descriptor WEB-INF/ibm-web-bnd.xml with the following configuration:
<?xml version="1.0" encoding="UTF-8"?>
<web-bnd xmlns="http://websphere.ibm.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-web-bnd_1_1.xsd"
version="1.1">
<virtual-host name="default_host" />
<resource-ref name="propertyURL" binding-name="propertyURL" />
<resource-ref name="propertyURL" binding-name="propertyURL" />
</web-bnd>
When the application gets deployed WAS allows you to map these mappings to its administered resources. Use the ISC console to define values of the environment entries and map them to the application.
It has became easier with WebSphere Liberty Profile. I described the mechanism as offered by WLP in my article Using #Resource to access JNDI in WebSphere AS 8.5 Liberty Profile.
You have three options:
configure the Websphere to include the directory which contains the property file in the classpath. Don't know how to do it, but I'm sure it is possible, since our application does the same thing
include the property file in the war archive. You probably don't want to do that.
instead using the classloader to load the property file use the file api with an absolute path. I'm not completely sure WAS does allow that, but it is a bad idea anyway, because it makes your application very dependent on things that it really shouldn't care about, such as the installation path of your application.
WebSphere has two folders on the classpath, properties can be loaded from there:
Enterprise Applications > myear > Manage Modules > myjar.jar > Class loader viewer
4 - Extension - com.ibm.ws.bootstrap.ExtClassLoader
file:/projekte/IBM/WebSphere/AppServer-8.5/classes/
file:/projekte/IBM/WebSphere/AppServer-8.5/lib/

Working with properties files outside war with Spring

I am working on a Spring 3.0.5 web application that accesses LDAP and two databases. I have a properties with configuration information for the LDAP server and that databases, in applicationContext-security.xml and dispatcher-servlet.xml, but I would like to make it so each server can have different data properties without changing a file in the WAR. Can I somehow put a file somewhere else on the server and still access it from within my application?
Add this to your context
<context:property-placeholder location="${envfile}"/>
This will load the properties file located at ${envfile}, a variable you can set with Java's startup paramater like this
-Denvfile="file:/var/server/environment.properties"
Or maybe in Tomcat's startup script
CATALINA_OPTS=" -Denvfile=file:/var/server/environment.properties"
Values can be retrieved in your controllers using Springs Value annotation like this:
#Values("${myvalue}")
private String myValue;
Please note that these features require Spring 3.1, more information here
Good luck!
Try
<util:properties id="props" location="file:///path/to/server.properties"/>

Categories

Resources