Managing your environment specific settings in your apps on WebSphere application servers

Working on iMac

In this guide, I’ll show you how to implement a very basic service in your applications leveraging the runtime specific resource environment properties. This will allow you to:

  • get rid of your application contained properties and similar config files
  • dynamic application reconfiguration (just change the settings, restart the app and voilá!)
  • works on WebSphere Liberty (OpenLiberty) and WebSphere Traditional Application Server

So, to have better apps in this manner, just follow my lead! 🙂

Code stuff

Ok, to create the initial setup, first you need to have a Java project (I’m using Eclipse), which contains one single Java class file with the following content:

package hu.burgatshow;

import java.io.Serializable;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;

public class PlaygroundObjectFactory implements ObjectFactory, Serializable {
	private static final long serialVersionUID = 420919292128804505L;

	private static Properties config = null;

	@Override
	public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
		if (config == null) {
			config = new Properties();

			Reference ref = (Reference) obj;
			Enumeration<RefAddr> addrs = ref.getAll();

			RefAddr addr = null;
			String entryName = null;
			String entryValue = null;

			while (addrs.hasMoreElements()) {
				addr = (RefAddr) addrs.nextElement();
				entryName = addr.getType();
				entryValue = (String) addr.getContent();
				config.put(entryName, entryValue);
			}
		}

		return config;
	}

}

The reason I’m using this approach first, is to have the same application running on Liberty and Traditional WAS. If you just using WAS traditional, you can build right into your app this class. Second, Liberty server.xml requires a referenced library to create a jndiObjectFactory configuration element. To see what I mean, here is the sample server configuration XML for my Liberty instance:

<library id="playgroundLib">
	<file name="${server.config.dir}/lib/PlaygroundObjectFactory.jar" />
</library>

<jndiObjectFactory id="objectFactory" libraryRef="playgroundLib" objectClassName="java.util.Properties" className="hu.burgatshow.PlaygroundObjectFactory" />

Liberty requires a JAR file to use in library definition, so that’s why I dedicated it into a standalone Java Project. I referenced it in my EAR file in case of traditional WAS.

The question why WAS traditional can load this reference from the application itself and Liberty why not, you need to go ask IBM. I don’t understand as well.

To demonstrate the usage of this solution, I simply created a servlet with the following code. I wrapped it to an EAR and ready to be deployed.

package hu.burgatshow.web;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.management.ManagementFactory;
import java.util.Properties;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/SayHello")
public class SayHello extends HttpServlet {
	private static final long serialVersionUID = 1L;

	public SayHello() {
		super();
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		Properties p = loadConfig();

		PrintWriter pw = response.getWriter();
		pw.write(p.toString());

		pw.flush();
		pw.close();
	}

	private Properties loadConfig() {
		Properties p = null;

		try {
			MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
			ObjectName libertyInfo = new ObjectName("WebSphere:feature=kernel,name=ServerInfo");

			if (mbs.isRegistered(libertyInfo)) {
				String libertyVersion = (String) mbs.getAttribute(libertyInfo, "LibertyVersion");
				if (libertyVersion != null && !libertyVersion.isEmpty()) {
					System.out.println("Application is running on WebSphere Liberty, version: " + libertyVersion);
				}
			} else {
				System.out.println("Application is running on WebSphere Traditional Application Server.");
			}
		} catch (Exception e) {
			System.out.println("Application is running on WebSphere Traditional Application Server.");
		}

		try {
			p = (Properties) new InitialContext().lookup("config/playground");
		} catch (Exception e) {
			e.printStackTrace();
		}

		return p;
	}
}

Server stuff

So, before we can deploy the application, server side configuration required. Technically we are now going to create the configuration entries we planned to use in our application(s).

WebSphere or Open Liberty

You simply need to add the settings to your server.xml file. The relevant part’s first half already posted before in this post, the configuration settings now comes:

<jndiReferenceEntry id="playgroundRefEntry" jndiName="config/playground" factoryRef="objectFactory">
	<properties
		myProperty1="This is the value of myProperty1" 
		my.property.2="This is the value of my.property.2"
	/>
</jndiReferenceEntry>

And… done. 🙂

WebSphere Application Server (Traditional)

Well in this case, we have a better solution to register the properties we need. Simply follow the steps below:

  1. Sign into WebSphere admin console
  2. Navigate to Resources – Resource Environment – Resource Environment Properties.
  3. Select an appropriate scope (server or cluster for example) and click the New button.
  4. Add a name to it. I will use Playground App Settings and click the Apply button.
  1. On the right, click Referenceables, then New.
    1. For Factory Class Name use the fully qualified class name of your freshly created class.
    2. For Class Name, use the java.util.Properties.
  1. Click the OK button to create the referenceable.
  2. Go back to your Resource Environment Provider, and on the right, click Resource environment entries, then New.
    1. For Name, add a name you want, I’ll pick Playground app entry.
    2. For JNDI name set what you want. This name will be used in the application when do a JNDI lookup to grab the settings. I’ll use config/playground.
    3. Make sure that the Referenceable dropdown list shows your previously created referenceable.
    4. Click Apply.
  1. On the right, click Custom properties and add all your settings here. I create the following two:
    1. myProperty1: This is the value of myProperty1
    2. my.property.2: This is the value of my.property.2
  1. Finally save the configuration.

Deploy stuff

Hurray, now deploy the application to your application server. If you did everything in the right way, here is your reward. 🙂

Liberty

And on WebSphere Application Server:

Download my sample application (it contains the factory JAR inside the lib folder and the servlet too). The servlet context path is /SayHello and the application’s context root is /Playground by default. So if you deploy it as is, the complete address will be: http://<address>:<port>/Playground/SayHello

And here is my exact server.xml. As you can see, the factory JAR file is copied under the <server profile>/lib folder.

<server description="new server">

    <!-- Enable features -->
    <featureManager>
        <feature>webProfile-8.0</feature>
        <feature>localConnector-1.0</feature>
    </featureManager>

    <!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
    <httpEndpoint httpPort="9080" httpsPort="9443" id="defaultHttpEndpoint"/>
                  
    <!-- Automatically expand WAR files and EAR files -->
    <applicationManager autoExpand="true"/>
    
    <!-- Required application files. Unfortunately, Liberty only loads JAR files. -->
	<library id="playgroundLib">
		<file name="${server.config.dir}/lib/PlaygroundObjectFactory.jar"/>
	</library>

	<!-- This object factory will be handled by the runtime -->
	<jndiObjectFactory className="hu.burgatshow.PlaygroundObjectFactory" id="objectFactory" libraryRef="playgroundLib" objectClassName="java.util.Properties"/>

	<!-- Application configuration related to this environment. -->
	<jndiReferenceEntry factoryRef="objectFactory" id="refEntry" jndiName="config/playground">
		<properties my.property.2="This is the value of my.property.2" myProperty1="This is the value of myProperty1"/>
	</jndiReferenceEntry>


    <applicationMonitor updateTrigger="mbean"/>

    <enterpriseApplication id="PlaygroundEAR" location="PlaygroundEAR.ear" name="PlaygroundEAR"/>
</server>