Webapp Configuration
Jeff Duska has a post tonight on how he handles app-specific configuration settings. His method sounds pretty neat, and hopefully we can combine our methods to come up with a better solution. Here's how I've been doing it.
- I have a StartupServlet that reads in an XML file and, using Castor, populates a JavaBean with the XML file's values. Sidenote - I recommend having a StartupServlet in webapps to populate drop-down choices from the database and stuff ArrayLists (of beans) into the ServletContext - works real slick in Struts using the LabelValueBean.
- Stuff the JavaBean into the servlet context and get values as needed while running the app.
Jeff's method seems make it easier to retrieve the values, but not to set them. I have a getConfiguration() method in my BaseAction class that I use to get my configuration data:
/** * Get the Configuration object from the servlet context */ public Configuration getConfiguration(){ return (Configuration) servlet.getServletContext().getAttribute( Constants.CONFIGURATION); }
The obvious problem with this is that only subclasses of BaseAction can get the configuration information - therefore, I like Jeff's idea better. However, I'm curious to know how he populates his Registry class. Castor makes it pretty damn easy. Here's the method I use in StartupServlet, where obj
is my JavaBean, and xmlFilePath
is configured as "/WEB-INF/app-config.xml" in one of StartupServlet's init-parameters.
/** * Load our application configuration XML file. * * @exception Exception if any problem occurs while loading */ private synchronized Object loadConfig ( Object obj, String xmlFilePath ) throws Exception { // attempt to extract the filename, using system file separator int index = xmlFilePath.lastIndexOf(Constants.FILE_SEP); // no system file separator found in configuration setting if (index == -1) { // check traditional file separator as used in web app URI's index = xmlFilePath.lastIndexOf("/"); // still no separator, maybe they're just specifying the filename if (index == -1) { index = 0; } } String xmlFileName = xmlFilePath.substring(index + 1, xmlFilePath.length()); if (logger.isDebugEnabled()) { logger.debug("Looking for '" + xmlFileName + "' in " + Constants.USER_HOME); } // Acquire an input stream to our configuration file InputStream is = null; try { is = new FileInputStream(Constants.USER_HOME + xmlFileName); } catch (FileNotFoundException fnf) { // No file found in user.home if (logger.isDebugEnabled()) { logger.debug("File not found at " + Constants.USER_HOME + xmlFileName + " - looking at specified path in web.xml."); } // Look for config.xml in WEB-INF is = getServletContext().getResourceAsStream(xmlFilePath); if (is == null) { throw new Exception("Configuration file '" + xmlFileName + "' not found in '" + Constants.USER_HOME + "', nor at '" + xmlFilePath + "'"); } } InputStreamReader reader = new InputStreamReader(is); // Marshal the configuration object obj = Unmarshaller.unmarshal(obj.getClass(), reader); // close the InputStream, and the reader is.close(); reader.close(); return obj; }
I like this method because it can load multiple configuration files, and I already have it written - so the hard part is over ;-) It also makes it easy to configure your app outside of WEB-INF, and changes won't be overwritten when a user upgrades their WAR file or whatnot. I suppose I could use Jeff's method and make my JavaBean into a Singleton and not store it in ServletContext - that'll probably work better than my current scenario.