Tuesday, July 22, 2008

Escaping Java Strings for use in Javascript code

What for?


In the context of a Java web application it is often necessary to generate pieces of javascript code dynamically. If you want to inject a Java String into Javascript code, you may need to escape the single-quotes that occur in the Java String, because Javascript can use single-quotes as the String delimiter. The simple way to avoid this issue is to use double quotes as String separators in Javascript, unfortunately this is not possible when the Javascript code is part of an HTLM attribute.
As in:

<a href="viewDocument?id=1234"
onclick="trackLink(this, '<%= ((Metadata) request.getAttribute("metadata")).getPageName4JS() %>')">
Document 1234
</a>

In the example above, a Javascipt error occurs if the method getPageName4JS() returns a String that contains a single quote.

How?


The following method will escape any quotes present in the input String, replacing each single quote with "\'".

public class WebUtil {
 public static String escapeSingleQuotes(String pString) {
  if (pString == null) {
   return null;
  }
  return pString.replaceAll("'", "\\\\'");
 }
}

You can then create a getter method that uses this utility method:

 public String getPageName4JS() {
  return WebUtil.escapeSingleQuotes(this.getPageName());
 }


Not very complicated, but tricky: it took me about 2 hours to write and test the escapeSingleQuotes() method. And I am still not sure I fully understand why it works. Let's look at the statement: pString.replaceAll("'", "\\\\'");
We want to replace every instance of " ' " with " \' ".
But the back-slash character in the regular expression needs to be escaped, so it becomes " \\' ". Each back-slash needs to be escaped in the Java String, so we end up with " \\\\' " in the Java code.

Unit test


Here is the JUnit test method for the escapeSingleQuotes() method:

 public void testEscapeSingleQuotes() {
  Assert.assertEquals(
   "null", null, WebUtil.escapeSingleQuotes(null));
  Assert.assertEquals(
   "empty string", "", WebUtil.escapeSingleQuotes(""));
  Assert.assertEquals(
   "Text with a 'quote'.",
   "This is a text with a \\'quote\\'.",
   WebUtil.escapeSingleQuotes("This is a text with a 'quote'."));
  Assert.assertEquals(
   "one quote", "\\'", WebUtil.escapeSingleQuotes("'"));
  Assert.assertEquals(
   "two quotes", "\\'\\'", WebUtil.escapeSingleQuotes("''"));
  Assert.assertEquals(
   "five quotes", "\\'\\'\\'\\'\\'", WebUtil.escapeSingleQuotes("'''''"));
  Assert.assertEquals(
   "mixed single quotes and back slashes",
   "\\\\\\\\'\\'\\'\\\\\\\\'\\'\\'\\\\'\\\\'\\\\'\\\\'",
   WebUtil.escapeSingleQuotes("\\\\\\'''\\\\\\'''\\'\\'\\'\\'"));
  Assert.assertEquals(
   "mixed single quotes and double quotes",
   "\"\\'\"\\'\"\\'\"\"\\'\\'",
   WebUtil.escapeSingleQuotes("\"'\"'\"'\"\"''"));
 }

Thursday, July 17, 2008

Zen and the Art of ...

When I created the blog a few days ago, I called it "The Art of Java Web App Maintenance".

This title was of course inspired by the title of the famous book "Zen and The Art of Motorcycle Maintenance".

I toyed with the idea of calling my blog "Zen and The Art of Java Web App Maintenance" but I let it go because I thought the Zen part would mislead people into thinking that they would find some kind of philosophical content in this blog.
The purpose of this blog is simply to post some tech articles who will interest only the very few people that encounter the same problems as I do in their work. I have no philosophical ambitions whatsoever.

I have never actually read the famous book, so I thought I might look it up on the Net, and YES, it is actually published on-line.
You can also download the book in PDF form, or subscribe to an audio version here.

Trouble: Spring 2.0 on Tomcat 4.1.37 LE for JDK1.4


What is the problem?


While installing a new web application stack I ran into an issue apparantly related to an incompatibility between Spring 2.0, JAXP and the Tomcat 4.1.37 LE for JDK1.4 distribution.
Basically I have a web application that uses Spring 2.0. It has been running in production for some time on a Tomcat 4.1.24 and J2SE 1.4.2_15.
I started building a new stack because I wanted to run Tomcat as a 64bit service (see my previous post).
I took the opportunity to upgrade to the latest version of Tomcat (4.1.37) and J2SE 1.4.2_16. I downloaded the so called 'LE for JDK1.4' distribution of Tomcat for Windows (apache-tomcat-4.1.37-LE-jdk14.exe). Seemed like a good deal because it is less bulky and I am running on JDK1.4...
But when I started up my web app I got a beautiful exception:

[gframework .web .servlet .DispatcherServlet] Context initialization failed
org .springframework .beans .factory .BeanDefinitionStoreException: Parser configuration exception parsing XML from ServletContext resource [/WEB-INF/spring-servlet.xml]; nested exception is javax .xml .parsers .ParserConfigurationException: Unable to validate using XSD: Your JAXP provider [org .apache .xerces .jaxp .DocumentBuilderFactoryImpl@22e496ab] does not support XML Schema. Are you running on Java 1.4 or below with Apache Crimson? Upgrade to Apache Xerces (or Java 1.5) for full XSD support.

Very strange indeed, because it complains that the JAXP provider does not support XSD, and suggest to upgrade to Xerces. But the web app already uses Xerces, as is confirmed by the error message ("Your JAXP provider [org .apache .xerces .jaxp .DocumentBuilderFactoryImpl@22e496ab]").


How to fix it?


I did not bother to understand exactly what is causing the error. It looks very much like a classical case of conflicting JARs.
On a hint I re-installed the FULL distribution of Tomcat 4.1.37 (apache-tomcat-4.1.37.exe) instead of the LE for JDK1.4 and guess what? That solved it.

In the hope that this might spare someone else some time,

Humbly yours,
Luca

Wednesday, July 16, 2008

Running Apache Tomcat 4.1 as a 64-bit service on an Itanium server


Why?


My web application consumes a lot of memory, basically because a large portion of the backend database is cached into memory (for performance reasons). The web app consumes around 700MB when no users are logged in, but can climb to 1.5GB when admins are using it. Some memory intensive admin functions can take the system down, i.e. OutOfmemory exceptions. I was never able to allocate more than 1.5GB to the JVM (Tomcat simply refuses to start when I try to go higher, probably because Windows refuses to allocate the requested memory). After reading some Microsoft papers I concluded that this was related to the maximum amount of memory a 32-bit Windows application can access: 4GB total of which 2GB are reserved for the system (to hold network buffers, etc.) and 2GB are accessible to the application. You can tweak the system to move an extra 1GB from the system part to the user part (=> 1GB system, 3GB user) but this is not advisable for a web application because it is network intensive so you do not want to lower your network buffers. The solution to get rid of the 4GB limit is to move to a 64-bit system. I happened to have an spare Itanium server just sitting there, so there I go...

How?



  1. Install a 64-bit version of J2SE 1.4. For example:
    C:\>java -version
    java version "1.4.2_16"
    Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_16-b05)
    Java HotSpot(TM) 64-Bit Server VM (build 1.4.2_16-b05, mixed mode)

  2. Install the latest Tomcat 4.1 release (4.1.37) using the Windows installer (apache-tomcat-4.1.37.exe). Check the option to install the windows service.

  3. After installation, the Tomcat service does not start.
    Some error message like '%1 is not a valid 32bit application'. Probably because we are trying to run Tomcat as a 32-bit service using a 64bit JVM...

  4. Read this very helpful post which explains how to run Tomcat as a 64-bit service.
    The author is using a more modern stack than me (Tomcat 6 and Java6), but he explains he used a tomcat5.exe and just renamed it to tomcat6.exe. I thought I might use the same trick...

  5. Move the files tomcat4.exe and tomcat4w.exe from your Tomcat installation to some other place (just to keep them handy if something goes wrong).

  6. Download the latest version of the Tomcat6 service (tomcat6.exe and tomcat6w.exe) from the Tomcat SVN repository. Copy them to the 'bin' directory of your Tomcat 4.1 installation, and rename the files to tomcat4.exe and tomcat4w.exe respectively.

  7. Start tomcat4w.exe. The Tomcat configuration window should pop-up, but you might have lost part of the configuration (I did). If the configuration is damaged, just re-create it manually. See screenshots.

  8. Start the Tomcat service.


Hope it works for you, it does for me.

Humbly yours,
Luca