Mantis - Resin
Viewing Issue Advanced Details
951 major always 02-20-06 01:51 03-08-06 09:43
mate  
ferg  
normal  
closed 3.0.17  
fixed  
none    
none 3.0.19  
0000951: XSL uses wrong ClassLoader (-> memory leak)
Resin XSL uses the wrong classloader for XSL templates, so that Java methods called from XSL will reference an old version of classes if the application is reloaded. Please see attached classes for a demonstration of the problem.

This causes trouble for us since all our XSL:s use static methods for i18n. If we re-deploy a WAR all text will fallback to the default locale until we restart Resin.

This probably also explains why we have been seeing memory leaks when redeploying, and finally running out of PermGen memory if we do not restart Resin.
Try the attached classes/application and then just touch the WAR and reload the page.

Notes
(0000885)
mate   
02-20-06 01:55   
(Where do I attach files???)

-------------------------------------------------------------------------------
package resin3Test;

import org.jdom.transform.JDOMSource;
import org.jdom.transform.JDOMResult;
import org.jdom.Element;
import org.jdom.Document;
import org.jdom.output.XMLOutputter;
import org.jdom.output.Format;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
import java.io.StringReader;

/**
 * Created: 2006-feb-20 10:19:47
 *
 * @author Mattias Jiderhamn
 */
public class XslClassLoaderServlet extends HttpServlet {
  protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    try {
      XslUtils.setString("bar");
      
      Element elem = new Element("manual");
      elem.addContent(new Element("getString").setText(XslUtils.getString()));
      elem.addContent(new Element("getClassIdentity").setText(XslUtils.getClassIdentity()));
      elem.addContent(new Element("getClassLoader").setText(XslUtils.getClassLoader()));
      
      TransformerFactory transformerFactory = TransformerFactory.newInstance();
      StreamSource streamSource = new StreamSource(new StringReader(
          "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n" +
          "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" [^] " +
          " xmlns:resin3Test=\"resin3Test\">\n" +
          " <xsl:template match=\"/\">\n" +
          " <root>" +
          " <transformed> " +
          " <getString><xsl:value-of select=\"resin3Test:XslUtils.getString()\"/></getString>" +
          " <getClassIdentity><xsl:value-of select=\"resin3Test:XslUtils.getClassIdentity()\"/></getClassIdentity>" +
          " <getClassLoader><xsl:value-of select=\"resin3Test:XslUtils.getClassLoader()\"/></getClassLoader>" +
          " </transformed> " +
          " </root>\n" +
          " </xsl:template>\n" +
          "</xsl:stylesheet>"));

      Transformer transformer = transformerFactory.newTransformer(streamSource);
      JDOMSource source = new JDOMSource(new org.jdom.Document(new Element("foo")));
      JDOMResult result = new JDOMResult();
      transformer.transform(source, result);

      Document doc = result.getDocument();
      doc.getRootElement().addContent(elem); // Add manually generated output
      
      response.setContentType("text/xml");
      new XMLOutputter(Format.getPrettyFormat()).output(doc, response.getWriter());
    }
    catch(Exception ex) {
      throw new ServletException(ex);
    }
  }
}

-------------------------------------------------------------------------------

package resin3Test;

import java.util.WeakHashMap;
import java.util.Map;
import java.util.Collections;

/**
 * @author Mattias Jiderhamn
 */
public class XslUtils {
  
  private static Map cache = Collections.synchronizedMap(new WeakHashMap());
  
  public static void setString(String s) {
    cache.put(Thread.currentThread(), s);
  }
  
  public static String getString() {
    return "foo" + cache.get(Thread.currentThread());
  }
  
  public static String getClassIdentity() {
    return "XslUtils:" + System.identityHashCode(XslUtils.class);
  }

  public static String getClassLoader() {
    return "ClassLoader:" + XslUtils.class.getClassLoader();
  }
}
(0000886)
mate   
02-20-06 02:04   
One thing I forgot to mention: it doesn't matter if the XSL is "inlined" or if it is a separate .xsl file.

And switching to Xalan removes the problem.
(0000922)
ferg   
03-08-06 09:43   
xsl/0a00