Oct 31, 2012

JSF 1.2 w/ Facelets on Websphere 6.1: Compiler Initialization Error

I believe this issue isn't specifically related to WebSphere but either way I wanted to list my technology stack for those Googling:
  
WebSphere Portal 6.1 on  
WebSphere Application Server 7.0  
JSF 1.2_15 
Facelets 1.1.14 
Spring Web Flow 2.1.3
Spring 3.1.2  
I had a nice demo working fine on my local Windows machine, but when I went to deploy to our test environment I ran into this error:
Compiler Initialization Error
java.util.zip.ZipException: error in opening zip file
 at java.util.zip.ZipFile.open(Native Method)
 at java.util.zip.ZipFile.<init>(ZipFile.java:137)
 at java.util.jar.JarFile.<init>(JarFile.java:149)
 at java.util.jar.JarFile.<init>(JarFile.java:86)
 at com.sun.facelets.util.Classpath.getAlternativeJarFile(Classpath.java:204)
 at com.sun.facelets.util.Classpath.search(Classpath.java:74)
 at com.sun.facelets.compiler.TagLibraryConfig.loadImplicit(TagLibraryConfig.java:428)
Without getting into the details of the hours I spent debugging, I found out that the system was actually running out of file handles. Now this could be because the differing system is Linux vs Windows or it could be because the Classpath contained many more shared libraries that needed scanning to look for *.taglib.xml files in the META-INF directories. I downloaded the source and found the source of the problem here:
 public static URL[] search(ClassLoader cl, String prefix, String suffix) throws IOException {
  Enumeration[] e = new Enumeration[] {
    cl.getResources(prefix),
    cl.getResources(prefix + "MANIFEST.MF")
   };
  Set all = new LinkedHashSet();
  URL url;
  URLConnection conn;
  JarFile jarFile;
  for (int i = 0, s = e.length; i < s; ++i) {
   while (e[i].hasMoreElements()) {
    url = (URL) e[i].nextElement();
    conn = url.openConnection();
    conn.setUseCaches(false);
    conn.setDefaultUseCaches(false);
    if (conn instanceof JarURLConnection) {
     jarFile = ((JarURLConnection) conn).getJarFile();
    } else {
     jarFile = getAlternativeJarFile(url);
    }
    if (jarFile != null) {
     searchJar(cl, all, jarFile, prefix, suffix);
    } else {
     boolean searchDone = searchDir(all, new File(URLDecoder.decode(url.getFile(), "UTF-8")), suffix);
     if (!searchDone)
     {
      searchFromURL(all, prefix, suffix, url);
     }
    }
   }
  }
  URL[] urlArray = (URL[]) all.toArray(new URL[all.size()]);
  return urlArray;
 }
The error looks like it originates in the getAlternativeJarFile method but that is just because it gets called soooo many times during this for/while loop. The simple fix is to release the jarFile immediately after it has been used like so:
...
if (jarFile != null) {
  searchJar(cl, all, jarFile, prefix, suffix);
  jarFile.close();
  jarFile = null;
} else {
...
}
...
I made the change, deployed to my test server again and everything worked perfect. Now for a long term solution? I'm not sure what I'm going to do. I have read that I can possibly increase the number of allowable open file handles (see here) which may be ok since this only happens on startup and will release them all after it finishes. Problem is the admins of this box are stingy and I doubt they'll want to change this in production too.... hmmmm.....

No comments:

Post a Comment