Monday, February 25, 2008

Nested Property Placeholders in Spring Configuration

  1. Using Java Properties in Spring Configuration - the PropertyPlaceholderConfigurer
  2. Using Nested Java Properties in Spring Configuration - nested Place Holders
  3. More Nested Java Properties in Spring Configuration
  4. Caveat for using PropertyPlaceholderConfigurer
  5. Data Source vs JNDI
  6. Appendix

Using Java Properties in Spring Configuration - the PropertyPlaceholderConfigurer

In your Spring configuration (Spring 2.5), it is possible to use property place-holders that get replaced with values from a properties file. For example, consider the Spring configuration below in which I define a POJO (plain old Java object) called testBean and a PropertyPlaceholderConfigurer called placeholderConfig.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="placeholderConfig"
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location"
      value="classpath:application.properties" />
  </bean>
  <bean name="testBean" class="my.Test">
    <property name="environmentName">
      <value>${environmentSpecificName}</value>
    </property>
  </bean>
</beans>

Note that the placeholderConfig bean has a location property whose value is the name of a properties file (application.properties) that is expected to appear on the class path. PropertyPlaceholderConfigurer's job is to go through your Spring configuration and replace all property place-holders (a string enclosed by a dollar-curly-brace pair: ${}) with a value from a property file. The property place-holder value is the key to retrieving the value from property file. See below for an important caveat with this behaviour.

Below is the properties file that my Spring configuration references.

# Environment: "local environment", "development environment",
# "test environment", "stress and volume testing environment"
# or "production environment".
environmentSpecificName=local environment

This means that when I retrieve testBean, its environmentName property will be "local environment". Whenever I want to change the value of testBean.environmentName in my code, I change the environmentSpecificName property and re-run my application.

TOP

Using Nested Java Properties in Spring Configuration - nested Place Holders

As you might guess from the comment I placed above the environmentSpecificName property, I want it to vary depending on which environment my code is running within. I.e. I want a different value for each of the environments my code might run within: local, development, testing, SVT and production.

An alternative way I can do this is to set up different properties for each possibility (environment), and use a nested property place-holder in the property value to determine which value should actually be used. My Spring configuration does not change, but my properties file does. Here is my new properties file.

# Environment: local, dev, test, svt or prod.
environmentSpecificName=${local.environmentSpecificName}
local.environmentSpecificName=local environment
dev.environmentSpecificName=development environment
test.environmentSpecificName=test environment
svt.environmentSpecificName=stress and volume testing environment
prod.environmentSpecificName=production environment

Now what happens is that the PropertyPlaceholderConfigurer looks up the value for environmentSpecificName and finds another property place-holder: ${local.environmentSpecificName}. It looks up the value for ${local.environmentSpecificName} and finds local environment.

The advantage here is that I can express each of the possible values as properties and switch between them as needed. It is a bit easier to edit a properties file than a Spring configuration, which tends to get complicated very quickly i.e. arguably, a properties file is more readable than a Spring configuration.

The disadvantage is that I still have to edit a file, which means a re-deploy for a web-app. An Ant build script can help with this, by outputting a different deployable artifact for each environment, using a string replacement (the replace task) to make sure environmentSpecificName is correct.

Why go to all this trouble just to vary one value in the Spring configuration? Forget the place-holders altogether and edit the value directly in the Spring configuration - like you said, an Ant build script can replace the value for us too. This is correct. Everything in this blog entry can be done with an Ant script. The only potential advantage is if you consider it easier to manage options in a properties file than in a build script, or Spring configuration. See the next section for an example of when I prefer to use this approach.

TOP

More Nested Java Properties in Spring Configuration

One situation in which I prefer this approach is when I have to manage multiple values that depend on one condition. Perhaps I have a different database for each environment, or a different set of web service URLs for each environment. I can set up each set of values in a properties file and use a single nested property to switch between sets. This is as close to conditional properties you can get with Spring.

Below is an example Spring configuration in which I define a JdbcTemplate that will come loaded with a data source specific for the environment my code is operating within.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="placeholderConfig"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreUnresolvablePlaceholders" value="false" />
    <property name="location"
      value="classpath:application.properties" />
  </bean>
  <bean name="jdbcTemplate" singleton="true"
    class="org.springframework.jdbc.core.JdbcTemplate">
    <constructor-arg index="1">
      <ref bean="dataSource" />
    </constructor-arg>
  </bean>
  <bean name="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource"
    destroy-method="close">
    <property name="driverClassName">
      <value>${dataSource.driverClassName}</value>
    </property>
    <property name="url">
      <value>${dataSource.url}</value>
    </property>
    <property name="username">
      <value>${dataSource.username}</value>
    </property>
    <property name="password">
      <value>${dataSource.password}</value>
    </property>
  </bean>
</beans>

Here is what my application.properties file might look like.

# Environment: local, dev, test, svt or prod.
environment=local

###############################################################################
## DATABASE PROPERTIES
###############################################################################

dataSource.driverClassName=${${environment}.dataSource.driverClassName}
dataSource.url=${${environment}.dataSource.url}
dataSource.username=${${environment}.dataSource.username}
dataSource.password=${${environment}.dataSource.password}

local.dataSource.url=jdbc:oracle:thin:@oralocal:1521:oralocal
local.dataSource.username=localuser
local.dataSource.password=localpwd
local.dataSource.driverClassName=oracle.jdbc.driver.OracleDriver

dev.dataSource.url=jdbc:oracle:thin:@oradev:1521:oradev
dev.dataSource.username=devuser
dev.dataSource.password=devpwd
dev.dataSource.driverClassName=oracle.jdbc.driver.OracleDriver

test.dataSource.url=jdbc:oracle:thin:@oratest:1521:oratest
test.dataSource.username=testuser
test.dataSource.password=testpwd
test.dataSource.driverClassName=oracle.jdbc.driver.OracleDriver

svt.dataSource.url=jdbc:oracle:thin:@orasvt:1521:orasvt
svt.dataSource.username=svtuser
svt.dataSource.password=svtpwd
svt.dataSource.driverClassName=oracle.jdbc.driver.OracleDriver

prod.dataSource.url=jdbc:oracle:thin:@oraprod:1521:oraprod
prod.dataSource.username=produser
prod.dataSource.password=prodpwd
prod.dataSource.driverClassName=oracle.jdbc.driver.OracleDriver

In this version, I only need to change one property (environment) to have four other properties change in response. Here is what the PropertyPlaceholderConfigurer is doing for ${dataSource.url} and the other place-holders in the data source Spring configured bean.

  1. The property place holder ${dataSource.url} key resolves to the value ${${environment}.dataSource.url}, which is itself a property place holder with another property place holder nested within it.
  2. The property place holder ${environment} key resolves to the value local.
  3. The property place holder ${${environment}.dataSource.url} key is now ${local.dataSource.url}
  4. The property place holder ${local.dataSource.url} resolves to the value jdbc:oracle:thin:@oralocal:1521:oralocal.

TOP

Caveat for using PropertyPlaceholderConfigurer

If you are using an XmlBeanFactory, you have to explicitly reference the PropertyPlaceholderConfigurer and invoke it upon your bean factory. For example.

XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
configurer.setLocation(new FileSystemResource("application.properties"));
configurer.postProcessBeanFactory(factory);

Or you could retrieve the bean from the context and then invoke it.

XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
PropertyPlaceholderConfigurer configurer = factory.getBean("placeholderConfig");
configurer.postProcessBeanFactory(factory);

Either way, you have explicitly invoke the configurer to do its work upon your Spring configuration. For this reason alone, I prefer to use ClassPathXmlApplicationContext as my factory, because it will automatically invoke any PropertyPlaceholderConfigurer on the context, just by having it in the Spring configuration. Also, you generally need a ApplicationContext in web apps.

TOP

Data Source vs JNDI

If you need to manage data sources across environments, and you have access to JNDI in each environment, use that instead. No need to hard code passwords in property files that way. But you will probably still a properties file approach for local unit testing - that you should be able to run without a server.

TOP

Appendix

Links that helped me with this.

  • From the Spring - Java/J2EE Application Framework Reference Documentation is Chapter 3. Beans, BeanFactory and the ApplicationContext.
  • I posted a question about this on the Spring forum, where I learned how to use this functionality. Further, I learned that Spring V3 might allow nested property place-holders in the property keys, not just values.

Wednesday, February 20, 2008

Date Validation in Spring MVC

I am using Spring MVC (Spring 2.5.1) and needed date validation in my form, with a custom error message. Reflecting my inability to R(or find)TFM, I failed miserably to find a resource that clearly outlined how to set up validation for a date. So here is my public record of a small saga.

First, I set up a date field in my JSP.

<form:errors cssClass="error"
    path="document.publishDate"/>
<form:input id="publishDate"
    path="document.publishDate"
    cssStyle="width:100px;"  />

There is a matching java.util.Date instance variable called publishDate in my Document class.

Next step is to define an object that can validate my dates. In servletName-servlet.xml, I have defined a custom date editor.

<bean id="customDateEditor"
    class="org.springframework.beans.propertyeditors.CustomDateEditor">
    <constructor-arg >
      <bean class="java.text.SimpleDateFormat">
        <constructor-arg><value>dd/MM/yyyy</value></constructor-arg>
      </bean>
    </constructor-arg>
    <constructor-arg ><value>true</value></constructor-arg>
  </bean>

The same config file defines my controller, and I inject the custom data editor into the controller through my Spring config.

<!-- Controller for ... -->
  <bean name="doJob.htm" id="jobController" class="..."
    ...
    <property name="customDateEditor" ref="customDateEditor"/>
    ...
  </bean>

This means that my controller will have an instance of CustomDateEditor ready to use, injected by Spring. I could have had my controller create its own CustomDateEditor, however. Either way, the next step is to register the data editor in the initBinder(HttpServletRequest, ServletRequestDataBinder) method of my controller.

  /**
   * {@inheritDoc}
   * @see org.springframework.web.servlet.mvc.
   *    BaseCommandController#initBinder(javax.servlet.http.HttpServletRequest, 
   *    org.springframework.web.bind.ServletRequestDataBinder)
   */
  protected void initBinder(HttpServletRequest request, 
      ServletRequestDataBinder binder) throws Exception {
    binder.registerCustomEditor(Date.class, getCustomDateEditor());
  }

The controller needs to be a subclass of org.springframework.web.servlet.mvc.BaseCommandController to have an initBinder() method. In this instance, my controller is a org.springframework.web.servlet.mvc.SimpleFormController.

This means that I have automatically defined the date format required for all dates that get validated by this controller.

Next step is to set up the error message I want. Personally, I found this the hardest part. I had to look at my logging closely to work out what was going on. In my controller's showForm(HttpServletRequest, HttpServletResponse, BindException, Map) method, I was outputting the result of a toString() call on the BindException object and I saw the following. The key things are in red.

Field error in object 'viewDocCommand' on field 'document.publishDate':
  rejected value [noWayIsThisAGoodDateValue!];
  codes [typeMismatch.viewDocCommand.document.publishDate,
  typeMismatch.document.publishDate,typeMismatch.publishDate,
  typeMismatch.java.util.Date,typeMismatch];
  arguments [org.springframework.context.support.DefaultMessageSourceResolvable:
  codes [viewDocCommand.document.publishDate,document.publishDate];
  arguments []; default message [document.publishDate]];
  default message [Failed to convert property value of type
  [java.lang.String] to required type [java.util.Date]
  for property 'document.publishDate';
  nested exception is java.lang.IllegalArgumentException:
  Could not parse date: Unparseable date: "noWayIsThisAGoodDateValue!"]

After a lot of searching in forums and javadocs I finally accumulated enough hints to get a hunch! Those dot separated values are message keys. Spring looks in the messages.properties file to find values to replace them with. I put entries into messages.properties to cover the most generic error type (typeMismatch.java.util.Date) and the field name.

typeMismatch.java.util.Date={0} is an invalid date. Use format DD/MM/YYYY.
document.publishDate=Publish Date

Now when I have a bad date value, I see the error message: Publish Date is an invalid date. Use format DD/MM/YYYY.

Links that helped me with this.

Saturday, February 16, 2008

Hard to Soft Formatting

Sometimes I have a document that uses manual line breaks or hard formatting to limit line lengths to x characters and I want to change it to a more word processor friendly format that uses paragraph breaks to delimit paragraphs, not lines.

This recent example is from a very decent walk through for the PS2 game "Hulk". It has text like this, where each line is no more than 80 characters long.

***********************
Section I: INTRODUCTION
***********************


The Hulk is one of my most favorite comic book characters ever.  I remember 
watching the 80's series when I was younger and collecting a few of his 
comics.  But enough about that, lets get to the game.  The Hulk (GC, PS2, X-
box) has got to be the best Hulk game ever made!  Finally we have a worthy 
successor to The Incredible Hulk (Genesis, SNES).  The Hulk is the first game 
that gives you the impression that you actually are the Hulk.  From smashing 
down doorways to making your own door, The Hulk involves a path of endless 
destruction from the beginning to the end.


I hope this FAQ will answer any questions that you might have about The Hulk.  
I have tried to make the FAQ as detailed as possible.  I have stayed away from 
anything dealing with the storyline or cutscenes to avoid spoilers, but I 
cannot guarantee that this FAQ is totally spoiler-free.  So be sure to use the 
FAQ as a last resort if you are playing through for the first time. 

When I paste this into MS Word, it means every line ends with a paragraph break. I want to format the document so that paragraph breaks are at the end of logical paragraphs of text only, not at the end of each line. This way, I can turn a 40 page document down to 8 pages of monospaced text.

  1. Copy from the web page and paste into the Word document as unformatted text.
  2. Apply wicked formatting: 8 pt Courier New, 66% character spacing on an A4 page with two columns 0.5 cm apart, with top, bottom, left and right margins of 1cm.
  3. Get rid of unnecessary spaces. Find ("  " - two spaces) and replace with (" " - one space). Note: don't use this if the document has useful tables or ASCII art that uses space for layout.
  4. Get rid of more unnecessary spaces before paragraph marks i.e. at the end of lines. Find using wildcards (" [^13]" - space then a paragraph mark) and replace with ("^p") - a paragraph mark.
  5. Get rid of unnecessary paragraph marks. Find ("^p^p" - two paragraph marks) and replace with ("^p" - one paragraph mark).
  6. Get rid of paragraph marks that are used only to delimit lines. The assumption is that any line ending with a letter where the next line begins with letter can be safely replaced with "letter letter" i.e. replace the paragraph mark with a space. Find using wildcards ("([A-Za-z])[^13]([A-Za-z])") and replace with ("\1 \2"). Since I can't really tell if a period at the end of a line indicates the end of a sentence or the end of a paragraph, I leave those alone.

The above text will now take up substantially less column centimetres.

This demonstrates the following.

  • Word's Find and Replace can be used with regular expressions - when you use Wildcards in the Find and Replace dialog .
  • To find a paragraph mark when you use wildcards, use the control code " [^13]" in the "Find" field. In the "Replace" field, you will still use "^p".
  • Character ranges can be defined between square brackets, such as "[A-Za-z]".
  • Round braces - "()" - can be used to mark wild card sequences in the "Find" field and back references - "\x" - can be used address those wild card sequence in the "Replace" field. For example, find using wildcards ("(Robert) (Bram)") and replace with ("\2 \1") would result in "Bram Robert".

Some pages I found useful when working this out.

Wednesday, February 13, 2008

Firefox loading an XSLT file - MIME type required

Posted this as a question on comp.text.xml: http://groups.google.com/group/comp.text.xml/browse_frm/thread/b3e34e9bc37c0057/80bd928ac0e6c94f#80bd928ac0e6c94f.

I am doing some XSLT work with my Javascript loading the XML to be converted and the XSLT stylesheet to do the converting using the following method.

function loadXMLDoc(fileName) {
  var xmlDoc;
  xmlDoc = document.implementation.createDocument("", "", null);
  xmlDoc.async = false;
  xmlDoc.load(fileName);
  return(xmlDoc);
} 
loadXMLDoc("stylesheet.xslt"); // XSLT Stylesheet
loadXMLDoc("data.xml");        // XML data to be rendered in HTML via XSLT

This worked in IE, but wasn't working in Firefox (2.0.0.12). The problem turned out to be MIME types in Firefox. My server wasn't set up to have a MIME type ("application/xml" or "text/xml") for my XSLT file and Firefox was failing because of this! Worse, it didn't output any sort of error message to let me know.

My workaround was to re-name the XSLT file to have an XML extension.

Monday, February 11, 2008

AJAX boiler-plate - asynchronous/synchronous, GET/POST, allows multiple concurrent requests

Update. 16/12/2008 5:58:37 PM. Updated formatting.

Here is my template AJAX code. It allows for the following:

  • Asynchronous or synchronous calls.
  • GET or POST request - ensuring that if it is a GET request, the URL is unique.
  • POST data sent as part of the POST request.
  • Multiple concurrent requests.

When I looked around for boiler-plate AJAX code, I didn't find any decent examples of code that allowed multiple concurrent requests. At the time I was writing a search page that would search files in mutliple locations. For a single search, I wanted to launch a different request to the server for each location. This way, I could update the page separately each time a location specific search finished and make the page look much more responsive.

One issue I came accross is that by default, IE only lets two concurrent requests occur at a time. Firefox was not so restrictive.

Examples of usage.

// url, callBackFunction, async flag, GET flag, post data
// Asynchronous GET request.
sendAjaxRequest("myScript?arg1=22&arg2=4", ajaxReqReturned, true, true, null);

// url, callBackFunction, async flag, GET flag, post data
// Asynchronous POST request.
var postData = "field1=value1&field2=value2";
sendAjaxRequest("myPage.jsp", ajaxReqReturned, true, false, postData);

/**
 * Call back function for when the AJAX call to the server returns.
 *
 * Param request - request object we are responding to
 */
function ajaxReqReturned(request) {
  var response = request.responseText;
  debug("updatePageForEnvironments with response [" + response + "].");
  // Take appropriate actions...
}

Here is the boiler-plate code.

/**
 * Writes debug to firebug console
 *
 * Param message - message to write out
 */
function debug(message) {
  if (console) {
    console.debug(message);
  } else {
    alert(message);
  }
}

/**
 * Send an AJAX request.
 *
 * Inspiration: http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=20&t=005211
 *
 * Param url - URL to request
 * Param callBackFunction - function to call when the request has returned from
 *     the server
 * Param async - true if the call is asynchronous, false otherwise
 * Param get - true if this is a GET request, false if it is a POST
 * Param postPayload - post data. Ignore if get is true.
 */
function sendAjaxRequest(url, callBackFunction, async, get, postPayload) {
  var method = "GET";
  if (get) {
    // Prevent browser caching by sending always sending a unique url.
    url += "&randomnumber=" + new Date().getTime();
    postPayload = null;
  } else {
    method = "POST"
  }
  console.info("Sending [" + method + "] request "
      + (async ? "a" : "")  + "synchronously to url [" + url + "].");
  // Obtain an XMLHttpRequest instance
  var req = newXMLHttpRequest();
  // Set handler function to receive callback notifications from request.
  var handlerFunction = getReadyStateHandler(req, callBackFunction);
  req.onreadystatechange = handlerFunction;
  req.open(method, url, async);
  if (!get) {
    req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    req.setRequestHeader("Content-length", postPayload.length);
    req.setRequestHeader("Connection", "close");
  }
  req.send(postPayload);
}

/**
 *  Return: a new XMLHttpRequest object, or false if this browser doesn't
 *    support it
 */
function newXMLHttpRequest() {
  var xmlreq = false;
  if (window.XMLHttpRequest) {
    // Create XMLHttpRequest object in non-Microsoft browsers
    xmlreq = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    var avers = [
      "Microsoft.XmlHttp",
      "MSXML2.XmlHttp",
      "MSXML2.XmlHttp.3.0",
      "MSXML2.XmlHttp.4.0",
      "MSXML2.XmlHttp.5.0"
    ];
    for (var i = avers.length -1; i >= 0; i--) {
      try {
        xmlreq = new ActiveXObject(avers[i]);
      } catch(e) {}
    }
    if (!xmlreq) {
      alert("Unable to create AJAX request object.");
    }
  }
  return xmlreq;
}

/**
 * Create and return a call back function that checks for server
 * success/failure.
 *
 * Param req - The XMLHttpRequest whose state is changing
 * Param responseHandler - Function to pass the response to
 * Return: a function that waits for the specified XMLHttpRequest
 *     to complete, then passes its XML response to the given handler function.
 */
function getReadyStateHandler(req, responseHandler) {
  // Return an anonymous function that listens to the XMLHttpRequest instance
  return function () {
    // If the request's status is "complete"
    if (req.readyState == 4) {
      var message = req.getResponseHeader("status");
      // Check that a successful server response was received
      if (req.status == 200) {          //todo: catch 404 error.
        // Pass the payload of the response to the handler function
        responseHandler(req);
      } else if (message != undefined && message != null
            && message.length > 0) {
          debug("Error status  [" + req.status + "] with message ["
              + message + "].");
      }
    }
  }
}

Wednesday, February 06, 2008

Java: set error header in servlet

When something goes wrong in the servlet, here is how to set the error header.

protected void doPost(HttpServletRequest request, 
    HttpServletResponse response) throws ServletException,
    IOException {
  PrintWriter out = response.getWriter();
  try {
    // Try something that can go wrong..
  } catch (Exception exception) {
    // Make sure to LOG the exception ... 

    // Then report back something the client can use..
    String message = "Bad error: reason.....";
    response.setHeader("status", message);
    response.setStatus(500);
    out.print(message);
  }
}

In the client code, Javascript can be used to access the error status itself and the error message on the request object as request.getResponseHeader("status"); and request.status; respectively. I don't cover here how the Javascript gets the request object.