Chapter 4 – Logging and Configuration, Part II Common Property File

Often, it is nice to use an external property file (aka, declarative approach) for your web application. It is more flexible over coding start-up parameters inside you application (programmatic approach). I list some of those benefits below. 1. Allowing application reconfiguration without recompilation. 2. Easy to visually inspect what are the initialization parameters for your application. 3. Allowing you to set up test instances of your application. There are several different ways to load a common property files. I list the pros & cons below. Method 1. Portal VarPacks Pros 1. Easy to use 2. No dependency on external libraries 3. Native to the portal 1. 2. 3. 2. Jakarta Commons Configuration Framework (Java) 1. Easy to use 2. Proven technology & well accepted 3. Supports a variety of file formats, e.g. property file, XML file, JNDI, etc. 4. Has a clear documentation 5. Has a defined strategy for loading and setting properties. 1. Moderate effort to write. 2. No need for external library. 3. You know your code 1. Easy to use 2. Support a variety of configuration files, INI, XML, registry, … 3. Automatic reload upon file modification. 1. Cons Tightly coupled with the portal. (Not vendor neutral) Lack of hierarchy, only goes two levels deep. Application cannot live outside of the portal context. Depends on several external libraries (Jakarta Commons Configuration, Jakarta Commons Collection, and Jakarta Commons Lang).

3. Custom Property File Loader 4. AMS Profile Configurator (.Net)

1. Requires custom coding 2. Need post-production support 1. Depends on an external library. 2. No cross platform support, only .Net.

We will discuss 2 – 4 because BEA EDocs provides a pretty good coverage on how to use Portal Varpacks.1

1

BEA EDocs on how to use VarPacks. http://edocs.bea.com/alui/devdoc/docs60/Customizing_the_Portal_UI/Using_Varpacks/PlumtreeDevDoc_Customizi ng_VarPack_SampleUses.htm

1

1 A Custom Property File Loader (Java)
If you read the previous chapter on Logging, you remember that you can add application environment variables in a web application’s configuration files. For Java platform, it is the web.xml file. For .Net platform, it is the web.config file. See my example “logging_and_common_property”.
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <description>InitServlet</description> <servlet-name>InitServlet</servlet-name> <servlet-class>InitServlet</servlet-class> <init-param> <description>globalconfig file</description> <param-name>global_config_file</param-name> <param-value>\\WEB-INF\\globalconfig.properties</param-value> </init-param> </servlet> (other stuff) </web-app>

Web.xml
//Add following lines to our InitServlet class. //reading global configuration file try { System.out.println("Loading global configuration file."); String gcf = config.getInitParameter("global_config_file"); String gcf2 = config.getServletContext().getRealPath(gcf); Konfig.getInstance().initialize(gcf2); } catch (Exception ex) { logger.info("A problem with reading global property file has occured."); logger.info(ex.getStackTrace()); }

Modified InitServlet Class
/* * Konfig.java */ package logging; import import import import import import import java.io.BufferedInputStream; java.io.File; java.io.FileInputStream; java.util.Enumeration; java.util.Properties; org.apache.log4j.Logger; org.apache.log4j.helpers.FileWatchdog;

/** * * @author rgao

2

*/ public class Konfig extends Properties { private static boolean initialized = false; private static Konfig instance; private static Logger logger = Logger.getLogger(Konfig.class); private KonfigWatchdog kdog; /** Creates a new instance of Konfig */ private Konfig() { } public void initialize(String konfigfile) throws Exception { //Dynamic reloading the property file without restarting application. kdog = new KonfigWatchdog(konfigfile); kdog.setDelay(KonfigWatchdog.DEFAULT_DELAY); kdog.start(); } private void doInitialize(String konf) throws Exception { if (initialized == false) { if(konf == null || konf.length() == 0 || !(new File(konf)).isFile()) { logger.info( "ERROR: Invalid configuration file. Please check the init param in web.xml"); throw new Exception("The global config file is not valid."); } //clears existing property index. clear(); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(konf))); load(bis); bis.close(); logger.info("---The list of global properties----"); Enumeration e = propertyNames(); while (e.hasMoreElements()) { String name = e.nextElement().toString(); String val = getProperty(name); logger.info(name + " = " + val); } logger.info("------------------------------------"); //list(System.out); //konfig.loadFromXML(new InputStream) for jdk 1.5 in the future. initialized = true; } //otherwise do nothing. } public static Konfig getInstance() throws Exception { //singleton if (instance == null) { logger.info("Creating a new instance of the global configuraiton properties."); instance = new Konfig(); } return instance; } class KonfigWatchdog extends FileWatchdog { private int i;

3

KonfigWatchdog(String filename) { super(filename); } public void doOnChange() { logger.info("Reloading the global configuration file: " + filename); initialized = false; try { Konfig kk = Konfig.getInstance(); kk.doInitialize(filename); } catch (Exception ex) { logger.warn("Cannot reload the global configuration file. "); ex.printStackTrace(); } } } }

Konfig.java In my example, the InitServlet class loads a specified global configuration file from the web.xml. A singleton Konfig (subclass of java.util.Properties) is then constructed. The Konfig class takes advantage of the FileWatchDog from the Log4J packages and automatically refreshes itself when the property file is updated. To access values of the configure file, use the following code snippets.
Konfig k = Konfig.getInstance(); Enumeration e = k.propertyNames(); while (e.hasMoreElements()) { String name = e.nextElement().toString(); String val = k.getProperty(name); out.println("<p>" + name + " = " + val + "</p>"); } //or you can access indvidual properties by. String prop1 = k.getProperty("Mykey");

Code Snippet2 and
#The global config file for MyKey=cde zzz=zzz DDDD=DDDD xyz=xyz ttt=msprule bbbbbb=blah

globalconfig.properties

2 AMS Profile Loader (.Net)
The AMS Profile loader is written by Alvaro Mendez. You can download it from http://www.codeproject.com/csharp/readwritexmlini.asp . That download includes a demo
2

If you feel that Konfig class should not directly throw a generic Exception, you can easy subclass it and override the getInstance() method and wrap around your specific exception. 4

application showing you how to read/write a variety of property files using that library. The GUI clipping is displayed below.

In a DotNet web application, it quite simple. You specify environment variables in the web.config file within the <appSettings> node. When, your page-behind C# code retrieves the name of the property file, you can create a wrapper class to represent your property file. That way you can manipulate an object instead of individual name-value pairs of strings. In my example, I uses a DataSet and a Xml configuration file.
<configuration> <appSettings> <add key="MyConfigFile" value="myconfig.xml" /> </appSettings> <system.web> … </system.web> </configuration>

Web.config
//Reading the global configue file. String configFileName = ConfigurationSettings.AppSettings["MyConfigFile"]; //using relative path. MyGlobalConfig gs = new MyGlobalConfig(Page.MapPath(configFileName)); Response.Write("<br>Below is the content of my global config file.<br><hr>"); Response.Write(gs.getMyConf().GetXml());

ASP C# Code Behind Page
using using using using using System; System.Collections; AMS.Profile; System.Data; System.IO;

namespace logging

5

{

/// <summary> /// MyGlobalConfig contains a DataSet for the configuration file. /// </summary> public class MyGlobalConfig { DataSet myconf; public MyGlobalConfig(String PropertyFileName) { if (!File.Exists(PropertyFileName)) { Console.WriteLine(PropertyFileName + " is not a valid file."); throw new Exception(PropertyFileName + " is not a valid file error."); } Console.WriteLine("PropertyFileName is: " + PropertyFileName); Xml profile = new Xml (PropertyFileName); this.myconf = profile.GetDataSet(); } public DataSet getMyConf() { return myconf; } } }

MyGlobalConfig.cs file

3 Jakarta Commons Configurations (Java)
Jakarta Commons Configurati3on framework depends on: 1. Jakarta Commons Lang4 library. 2. Jakarta Commons Collection5 library. See my example “config_with_jakarta_commons”.

3 4

Jakarta Commons Configuration - http://jakarta.apache.org/commons/configuration/index.html Jakarta Commons Lang - http://jakarta.apache.org/commons/lang/ 5 Jakarta Commons Collection - http://jakarta.apache.org/commons/collections/

6