Spring Framework: How to use properties files for configuration instead of the JNDI in a Wildfly server environment
In a previous post, I gave the arguments for and against using the JNDI for managing application configuration and attempted to contrast those arguments with using spring framework to pull in configurations from flat files.
In this post, I would like to show how I managed to take an application that was heavily dependent upon the JNDI and managed to free it of said dependency, making it dependent instead upon flat configuration files.
Before continuing, I should preface what remains with the following disclaimer: This is an example for an application in a Wildfly server environment. Although I am certain Glassfish and/or Tomcat server environments are similar, I don’t typically work with those server environments, so if you do, you’re out of luck. (Sorry)
So, you’ve got a java web application, and you’re tired of using the JNDI. In that jboss-web.xml
file of yours, you probably see many entries that look like this:
<resource-ref>
<res-ref-name>service/mail.from</res-ref-name>
<jndi-name>java:/my-service/mail.from</jndi-name>
</resource-ref>
These resource-ref entries may or may not be accompanied by other resource-ref entries in your application’s web.xml file:
<resource-ref>
<res-ref-name>service/mail.from</res-ref-name>
<res-type>java.lang.String</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Between these two entries, you’ve pulled a configurable value out of the JNDI (provided by your server environment), declared a type and provided scope. By the time this is done, all of these entries will be gone.
Step 1: Include your dependencies
Include spring framework’s web package into your application by inserting this XML into your project’s POM.xml file (where $VERSION$
indicates the version you want to include … anything above version 3 should work, but you should use the latest release, which is 4.3.11 as of this writing… See Spring Framework):
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>$VERSION$</version>
<type>jar</type>
</dependency>
Step 2: Plug in spring to your application
In order spring to do anything after you’ve included it, you need to “plug it in,” so to speak. The key construct that will enable you to free yourself from the JNDI is called the “Web Application Context.” In not so many words, the context allows the application to become somewhat self-aware in that it can look around its environment and create objects for you from the get go that you can reference virtually anywhere in your application either through Autowiring or through reference to the context itself.
Include this listener in your web.xml. (Note: If you have some sort of logging listener, make sure this listener comes AFTER that one.) This listener will load the Web Application Context and make it available for use wherever you can get access to a Java servlet object.
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Step 3: Place a properties file
Before you go further, you might want to start thinking about actually creating the configuration file you intend to reference. The syntax is fairly simple: property=value
. In my applications, I typically include these files in my source code, then copy them to Wildfly’s configuration directory to be referenced later. Continuing with the example from earlier, in which the service/mail.from
property was pulled from the JNDI, one such file might look something like this:
service/mail.from=me@mydomain.com
service/mail.to=customer@one.com,customer@2.com,customer@3.com,customer@4.com
service/mail.subject=I know how to do this property files thing
The properties in this file are delimited by carriage returns; no need to fret about the white space between words breaking something.
Step 4: Tell the app where to “cook up the java.”
Programmers are bad at naming things, so it should come as no surprise that spring singleton object recipes (and other recipes for objects with non-singleton scopes) are called “beans,” which is quite possibly the most worthless name for a contextually referenced object in the history of ever. (It took me months to figure out what was meant by this term given its ubiquity … but that’s another blog post). In any case, now that you’ve got a web application context and a flat file containing your configurable values, it’s time to actually pull these properties into objects that can be referenced in your web application code.
You have two choices on how to proceed: You can either use Java-based configuration to pull your properties or XML-based configuration. In building applications from scratch, I actually prefer using Java-based configuration, but if you’re using any sort of custom Servlet or any non-Spring request dispatching mechanism, it’s probably easier to use XML. Why? Spring Framework an application initialization object that will allow you to set up your servlets, context loader listener and configuration location using Java code, but if you’re adapting an existing application and already are using a web.xml file, you’ve probably got your servlets and routing all set up anyway, and you probably don’t want to rewrite the whole configuration in Java.
Since we’re going to continue with XML-based configuration, you need to tell the application where the XML configuration file is going to be located. This is a different file than you’re properties file … this is an XML file where your “beans,” or configurable objects that will be referenced throughout your application later, are defined.
In your web.xml file, you have to define the configuration location as a context parameter. Include this snippet somewhere in your web.xml BEFORE you include the Context Loader Listener, and modify it as appropriate. (You’ll want to modify the classpath to suit your app, and you can name the file whatever you want.)
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:my/web/app/properties.xml
</param-value>
</context-param>
Once you’ve defined this value, create the file in the place you’ve specified so that it can actually be found and parsed. In this example, we reference the properties file, and use the values within in the definition of other objects. Here’s a sample xml file that takes the three properties from above and does something with them.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
<context:property-placeholder location="file:${jboss.server.config.dir}/myapp.properties" />
<bean class="java.lang.String" id="service/mail.from">
<constructor-arg value="${service/mail.from}" />
</bean>
<bean class="java.lang.String" id="service/mail.to">
<constructor-arg value="${service/mail.to}" />
</bean>
<bean class="java.lang.String" id="service/mail.subject">
<constructor-arg value="${service/mail.subject}" />
</bean>
</beans>
Let’s go through this line by line…
First, the root element’s attributes. You have to include the xsi name space in order to pull in other relevant schemas that you’ll need to use in order to make this work; without the context
namespace being available, you can’t reference your flat file you’ve created and its configurable values. As you get better, you can use other schemas to do what you want … there are a lot of them. Google can help you learn about them.
Now, the context:property-placeholder
element. This is where you declare the actual location of your properties file. In Wildfly, server level variables are available for use here; I’m declaring that my properties are located in a file called myapp.properties
in Wildfly’s configuration folder. To reference the properties later in the xml file, use the ${...}
syntax.
Next, the elements themselves. Here, I’ve declared three string objects. As an argument to the default constructor of the string object, I’ve included my properties. After spring gets done parsing this, these Strings will be singletons, procurable throughout my application by tapping the web application context.
You can use these properties to make objects far more complex than Strings. If you have some sort of web service manager, for example, you can define it in this XML and pass properties out of your flat file and into the object. There’s a lot you can do with this. Read up on it here.
Step 5: Reference your beans
In any place in your code where you want to reference these objects, is obtain the web application context and use the getBean
method to call out your bean based on the id you gave it when it was declared. Note: You will need access to the servlet context in order to obtain the web application context using spring’s Web Application Utilities.
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class MainServlet extends BaseServlet {
public void init() throws ServletException {
ServletContext ctx = getServletConfig().getServletContext();
//other servlet code goes here
//whatever you need to initialize...
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(ctx);
String from = wac.getBean('service/mail.from');
String toList = wac.getBean('service/mail.to');
String subject = wac.getBean('service/mail.subject');
//use these values in mail objects or whatever you want.
//other init code as you see fit
}
}
There you go! You’ve gone from a flat properties file through spring’s XML-based configuration and into your web application. You’re no longer dependent on the JNDI. Feel free to remove all those resource-ref
s from before.
Thanks for reading! I hope it was helpful.