January 2009


We included two key enhancements in the 1.0.11.5 release of our Java framework to improve support for environment specific configurations. The first enables you to specify a path relative to the classpath on which the log4j.xml file can be found. The second enables you to group all of the configuration files for all environments in your jar and use directory structures or file names to specify which ones are to be chosen for any environment. Both these features are driven by newly provided support for substituting the value of a system property e in any value reference within the app_config.xml file. The system property can be defined either at the WAS console or using the -D parameter on the java command line.

Armed with this knowledge, let’s look at one possible way to organize our framework configuration files to account for environment differences. My preferred approach (at the moment) is to use a directory structure – with a different directory representing each environment. I’ll throw all of my config files for a given environment in the corresponding directory. This approach works for any component configuration that can be included from app_config.xml. Not all components will honor this approach, but many will (see list below). For all the “non-compliant” configurations we’ll group the environment settings together and use a naming convention coupled with the system property to pick out desired settings at runtime.

Configurations That Honor “Include”

Codes

CustomProfile

Exceptions

Search

WebServices

Let’s start with the directory-driven stuff. Set up a directory structure in the /src directory of your WAR (or whatever you are using as your source directory) as follows:

/src/config/

  app_config.xml

/src/config/Development/

  dataaccess.xml
  codes.xml
  log4j.xml

/src/config/Test

  dataaccess.xml
  codes.xml
  log4j.xml

/src/config/Prod

  dataaccess.xml
  codes.xml
  log4j.xml

/src/config/Directories

  directory-services.xml

/src/config/content

  content-services.xml

In this example I place the configuration files for Codes and Data Access in the Development, Test, Prod directories, with environment specific settings in each copy of the file. We will drive which directory and file is selected using the include file mechanism in app_config. Let’s cover that in a moment, first I want to address those pesky non-compliant config files. The non-compliant configuration files (all those not in the above list) must be placed in a subdirectory named after the component itself – as is reflected in the prior example. To enable environment-specific settings, I recommend lumping all of the environment settings for a given component in a single file, then use a naming convention to differentiate them. Here is an example for the Directories component.

<directories>

  <directory name="TestAD">
  <providerUrl>ldap://gldc-gc.ad.allstate.com:3268/&lt;/providerUrl>
  <initialContextFactory>com.sun.jndi.ldap.LdapCtxFactory</initialContextFactory>
  <userName>aptbinduser</userName>
  <password>5_Z-.903</password>
  <useConnectionPooling>true</useConnectionPooling>
  <connectionPoolTimeout>10000</connectionPoolTimeout>
  <initSize>10</initSize>
  <maxSize>50</maxSize>
  <preferredSize>10</preferredSize>
  </directory>
  <directory name="ProdAD">
  <providerUrl>ldap://gldc-gc.ad.allstate.com:3268/&lt;/providerUrl>
  <initialContextFactory>com.sun.jndi.ldap.LdapCtxFactory</initialContextFactory>
  <userName>aptbinduser</userName>
  <password>5_Z-.903</password>
  <useConnectionPooling>true</useConnectionPooling>
  <connectionPoolTimeout>10000</connectionPoolTimeout>
  <initSize>10</initSize>
  <maxSize>50</maxSize>
  <preferredSize>10</preferredSize>
  </directory>

</directories>

You can follow the same pattern for content-services.xml and any other configuration file that must be in a fixed location. The key is to define the lookup name in such a way that they reflect a specific environment. We’ll use custom configuration properties to determine the appropriate lookup name at runtime using the app_config.xml file, which follows:

<category name="AIC.CustomLogConfig">

<property name="AIC.LogConfigName" value="/config/${EDMS_Environment}/log4j.xml" />

</category>

<category name="EDMS.Properties">

  <property name="EDMS.AD_Directory" value="${EDMS_Environment}AD" />

</category>

<include name="/config/"$[Macro error: Can't evaluate the expression because the name "EDMS_Environment" hasn't been defined.]
/dataaccess.xml" />

<include name="/config/"$[Macro error: Can't evaluate the expression because the name "EDMS_Environment" hasn't been defined.]
/codes.xml" />

Notice that we’ve used a reference to a system property to customize the location of the log4j.xml file, placing a custom copy in each of our environment directories. We use the include file mechanism for environment specific configurations for codes and dataaccess – using the system property substitution to provide a specific path at runtime. For the non-include-compliant configuration files you can use a lookup mechanism driven by custom properties. In your application, when doing a lookup (i.e. using the services factory to get an instance of a component, use the custom property to do the lookup rather than the specific literal name given in the configuration file. In this example, use the property “EDMS.AD_Directory” to do your lookup for an environment specific reference to the directory instance you want to work with. With this approach you can change the system property EDMS_Environment to reflect the name of the environment you are running in. Your application uses the property to do lookups for those components that are not yet capable of supporting the “include file” mechanism for app_config.

You could, of course, use the environment variable/lookup mechanism for ALL configuration files. That provides consistency, but requires a little more effort. On the other hand, it also lets you put all of your environment configurations for any component in a single file. Also, you might elect to using a file naming convention rather than a directory structure. In this case, you could prepend or append the env variable reference to the file name to create a reference to the desired config file. Use whichever approach works best for you.

Advertisements

Since we’ve temporarily lost our internal blog and I have a lot of backlogged topics, I’m going to start posting to this site. These will eventually get moved back to our internal site. In the meantime, I’d like to start with this fascinating topic.

Olga called me about a week ago with a problem. Any time she re-published her WAR file to Websphere and re-ran her application she was getting ClassCastExceptions. This did not happen when she stopped and restarted the Websphere server. It didn’t take too much investigation to realize that her app (actually, our framework) was putting custom objects in the cache. On republishing her app those objects were still present in Dynacache. However, the new app used a new instance of the Java class loader and so could not trust any objects previously loaded by another class loader. This is one of those places where the .Net concept of namespace (which includes version number) is really nice. I told Olga she either needed to stop and restart the server or clear the cache. She didn’t like the former idea and had no way of doing the latter. As an experiment, I decided to look into whether or not I could use JMX to complete such a task. It turns out you can. In fact, using JMX you can get a list of every Dynacache instance on your cluster and get at all of the metadata for each entry in the cache. On top of that, you can clear any cache instance with a single API invocation. More on the where-for’s and how-to’s coming up in my next post.