r1090 - xwiki/trunk/src/main/java/com/xpn/xwiki/render/groovy

Ludovic Dubost ludovic at users.forge.objectweb.org
Mon Jul 24 14:37:50 CEST 2006


Author: ludovic
Date: 2006-07-24 14:37:50 +0200 (Mon, 24 Jul 2006)
New Revision: 1090

Modified:
   xwiki/trunk/src/main/java/com/xpn/xwiki/render/groovy/GroovyTemplateEngine.java
Log:
Fixed memory leaks in Groovy scripting. 
Memory consumption should be reduced while running groovy scripts
But still flushCache is needed to completely flush the groovy execution cache.
A hack was needed to cleanup the Groovy MetaClassRegistry on flushCache

Modified: xwiki/trunk/src/main/java/com/xpn/xwiki/render/groovy/GroovyTemplateEngine.java
===================================================================
--- xwiki/trunk/src/main/java/com/xpn/xwiki/render/groovy/GroovyTemplateEngine.java	2006-07-24 12:12:56 UTC (rev 1089)
+++ xwiki/trunk/src/main/java/com/xpn/xwiki/render/groovy/GroovyTemplateEngine.java	2006-07-24 12:37:50 UTC (rev 1090)
@@ -36,10 +36,7 @@
 
 package com.xpn.xwiki.render.groovy;
 
-import groovy.lang.Binding;
-import groovy.lang.GroovyShell;
-import groovy.lang.Script;
-import groovy.lang.Writable;
+import groovy.lang.*;
 import groovy.text.Template;
 import groovy.text.TemplateEngine;
 
@@ -51,242 +48,311 @@
 import java.io.Writer;
 import java.util.Map;
 import java.util.HashMap;
+import java.beans.Introspector;
 
 import org.codehaus.groovy.control.CompilationFailedException;
 import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.DefaultGroovyStaticMethods;
+import com.xpn.xwiki.cache.impl.XWikiCachedObject;
 
 
-
-
-   /**
+/**
 * This simple template engine uses JSP <% %> script and <%= %> expression syntax.  It also lets you use normal groovy expressions in
 * the template text much like the new JSP EL functionality.  The variable 'out' is bound to the writer that the template is being written to.
 *
 */
-   public class GroovyTemplateEngine extends TemplateEngine {
+public class GroovyTemplateEngine extends TemplateEngine {
 
-       private class GTEClassLoader extends ClassLoader {
-           private HashMap classMap = new HashMap();
+    private class GTEClassLoader extends ClassLoader {
+        private HashMap classMap = new HashMap();
 
-           public GTEClassLoader(ClassLoader parent) {
-               super(parent);
-           }
-           protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
-               Object cached = classMap.get(name);
-               if (cached!=null) return (Class) cached;
-               ClassLoader parent = getParent();
-               if (parent!=null) return parent.loadClass(name);
-               return super.loadClass(name,resolve);
-           }
-       }
+        public GTEClassLoader(ClassLoader parent) {
+            super(parent);
+        }
+        protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+            Object cached = classMap.get(name);
+            if (cached!=null) return (Class) cached;
+            ClassLoader parent = getParent();
+            if (parent!=null) return parent.loadClass(name);
+            return super.loadClass(name,resolve);
+        }
+    }
 
 
-   /* (non-Javadoc)
-    * @see groovy.util.TemplateEngine#createTemplate(java.io.Reader)
-    */
-   public Template createTemplate(Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
-       ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+/* (non-Javadoc)
+* @see groovy.util.TemplateEngine#createTemplate(java.io.Reader)
+*/
+public Template createTemplate(Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
+        com.xpn.xwiki.render.groovy.GroovyTemplateEngine.SimpleTemplate template = new com.xpn.xwiki.render.groovy.GroovyTemplateEngine.SimpleTemplate();
+        GroovyShell shell = new GroovyShell();
+        String script = template.parse(reader);
+        template.script = shell.parse(script);
+        return template;
+}
 
-       try {
-           Thread.currentThread().setContextClassLoader(new GTEClassLoader(oldClassLoader));
-           com.xpn.xwiki.render.groovy.GroovyTemplateEngine.SimpleTemplate template = new com.xpn.xwiki.render.groovy.GroovyTemplateEngine.SimpleTemplate();
-           GroovyShell shell = new GroovyShell();
-           String script = template.parse(reader);
-           template.script = shell.parse(script);
-           return template;
-       } finally {
-           Thread.currentThread().setContextClassLoader(oldClassLoader);
-       }
-   }
+private static class SimpleTemplate implements Template, XWikiCachedObject {
 
-   private static class SimpleTemplate implements Template {
+    private Script script;
+    private Binding binding;
+    private Map map;
 
-       private Script script;
-       private Binding binding;
-       private Map map;
 
-       /**
-        * Set the binding for the template.  Keys will be converted to Strings.
-        *
-        * @see groovy.text.Template#setBinding(java.util.Map)
-        */
-       public void setBinding(final Map map) {
-           this.map = map;
-           binding = new Binding(map);
-       }
+    public void finalize() throws Throwable
+    {
+        try {
+            if (script!=null) {
+                InvokerHelper.removeClass(script.getClass());
+                removeClass(script.getClass());
+            }
+        } finally {
+            super.finalize();
+        }
+    }
 
-       /**
-        * Write the template document with the set binding applied to the writer.
-        *
-        * @see groovy.lang.Writable#writeTo(java.io.Writer)
-        */
-       public Writer writeTo(Writer writer) throws IOException {
-           if (binding == null) binding = new Binding();
-           Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);
-           PrintWriter pw = new PrintWriter(writer);
-           scriptObject.setProperty("out", pw);
-           scriptObject.run();
-           pw.flush();
-           return writer;
-       }
+    /**
+     * Set the binding for the template.  Keys will be converted to Strings.
+     *
+     * @see groovy.text.Template#setBinding(java.util.Map)
+     */
+    public void setBinding(final Map map) {
+        this.map = map;
+        binding = new Binding(map);
+    }
 
-       /**
-        * Convert the template and binding into a result String.
-        *
-        * @see java.lang.Object#toString()
-        */
-       public String toString() {
-           try {
-               StringWriter sw = new StringWriter();
-               writeTo(sw);
-               return sw.toString();
-           } catch (Exception e) {
-               return e.toString();
-           }
-       }
+    /**
+     * Write the template document with the set binding applied to the writer.
+     *
+     * @see groovy.lang.Writable#writeTo(java.io.Writer)
+     */
+    public Writer writeTo(Writer writer) throws IOException {
+        if (binding == null) binding = new Binding();
+        Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);
+        PrintWriter pw = new PrintWriter(writer);
+        scriptObject.setProperty("out", pw);
+        scriptObject.run();
+        pw.flush();
+        return writer;
+    }
 
-       /**
-        * Parse the text document looking for <% or <%= and then call out to the appropriate handler, otherwise copy the text directly
-        * into the script while escaping quotes.
-        *
-        * @param reader
-        * @return
-        * @throws IOException
-        */
-       private String parse(Reader reader) throws IOException {
-           if (!reader.markSupported()) {
-               reader = new BufferedReader(reader);
-           }
-           StringWriter sw = new StringWriter();
-           startScript(sw);
-           boolean start = false;
-           int c;
-           while((c = reader.read()) != -1) {
-               if (c == '<') {
-                   c = reader.read();
-                   if (c != '%') {
-                       sw.write('<');
-                   } else {
-                       reader.mark(1);
-                       c = reader.read();
-                       if (c == '=') {
-                           groovyExpression(reader, sw);
-                       } else {
-                           reader.reset();
-                           groovySection(reader, sw);
-                       }
-                       continue;
-                   }
-               }
-               if (c == '\"') {
-                   sw.write('\\');
-               }
-               if ((c!='\r')&&(c!='\n'))
-                 sw.write(c);
-               else if (c=='\n') {
-                 sw.write("\");out.println();out.print(\"");
-               }
-           }
-           endScript(sw);
-           String result = sw.toString();
-           //System.out.println( "source text:\n" + result );
-           return result;
-       }
+    /**
+     * Convert the template and binding into a result String.
+     *
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        try {
+            StringWriter sw = new StringWriter();
+            writeTo(sw);
+            return sw.toString();
+        } catch (Exception e) {
+            return e.toString();
+        }
+    }
 
-       private void startScript(StringWriter sw) {
-           sw.write("/* Generated by GroovyTemplateEngine */ ");
-           sw.write("out.print(\"");
-       }
+    /**
+     * Parse the text document looking for <% or <%= and then call out to the appropriate handler, otherwise copy the text directly
+     * into the script while escaping quotes.
+     *
+     * @param reader
+     * @return
+     * @throws IOException
+     */
+    private String parse(Reader reader) throws IOException {
+        if (!reader.markSupported()) {
+            reader = new BufferedReader(reader);
+        }
+        StringWriter sw = new StringWriter();
+        startScript(sw);
+        boolean start = false;
+        int c;
+        while((c = reader.read()) != -1) {
+            if (c == '<') {
+                c = reader.read();
+                if (c != '%') {
+                    sw.write('<');
+                } else {
+                    reader.mark(1);
+                    c = reader.read();
+                    if (c == '=') {
+                        groovyExpression(reader, sw);
+                    } else {
+                        reader.reset();
+                        groovySection(reader, sw);
+                    }
+                    continue;
+                }
+            }
+            if (c == '\"') {
+                sw.write('\\');
+            }
+            if ((c!='\r')&&(c!='\n'))
+              sw.write(c);
+            else if (c=='\n') {
+              sw.write("\");out.println();out.print(\"");
+            }
+        }
+        endScript(sw);
+        String result = sw.toString();
+        //System.out.println( "source text:\n" + result );
+        return result;
+    }
 
-       private void endScript(StringWriter sw) {
-           sw.write("\");");
-       }
+    private void startScript(StringWriter sw) {
+        sw.write("/* Generated by GroovyTemplateEngine */ ");
+        sw.write("out.print(\"");
+    }
 
-       /**
-        * Closes the currently open write and writes out the following text as a GString expression until it reaches an end %>.
-        *
-        * @param reader
-        * @param sw
-        * @throws IOException
-        */
-       private void groovyExpression(Reader reader, StringWriter sw) throws IOException {
-           sw.write("\");out.print(\"${");
-           int c;
-           while((c = reader.read()) != -1) {
-               if (c == '%') {
-                   c = reader.read();
-                   if (c != '>') {
-                       sw.write('%');
-                   } else {
-                       break;
-                   }
-               }
-               sw.write(c);
-           }
-           sw.write("}\");out.print(\"");
-       }
+    private void endScript(StringWriter sw) {
+        sw.write("\");");
+    }
 
-       /**
-        * Closes the currently open write and writes the following text as normal Groovy script code until it reaches an end %>.
-        *
-        * @param reader
-        * @param sw
-        * @throws IOException
-        */
-       private void groovySection(Reader reader, StringWriter sw) throws IOException {
-           sw.write("\");");
-           int c;
-           while((c = reader.read()) != -1) {
-               if (c == '%') {
-                   c = reader.read();
-                   if (c != '>') {
-                       sw.write('%');
-                   } else {
-                       break;
-                   }
-               }
-               sw.write(c);
-           }
-           sw.write(";out.print(\"");
-       }
+    /**
+     * Closes the currently open write and writes out the following text as a GString expression until it reaches an end %>.
+     *
+     * @param reader
+     * @param sw
+     * @throws IOException
+     */
+    private void groovyExpression(Reader reader, StringWriter sw) throws IOException {
+        sw.write("\");out.print(\"${");
+        int c;
+        while((c = reader.read()) != -1) {
+            if (c == '%') {
+                c = reader.read();
+                if (c != '>') {
+                    sw.write('%');
+                } else {
+                    break;
+                }
+            }
+            sw.write(c);
+        }
+        sw.write("}\");out.print(\"");
+    }
 
-       public Writable make() {
-           return make(null);
-       }
+    /**
+     * Closes the currently open write and writes the following text as normal Groovy script code until it reaches an end %>.
+     *
+     * @param reader
+     * @param sw
+     * @throws IOException
+     */
+    private void groovySection(Reader reader, StringWriter sw) throws IOException {
+        sw.write("\");");
+        int c;
+        while((c = reader.read()) != -1) {
+            if (c == '%') {
+                c = reader.read();
+                if (c != '>') {
+                    sw.write('%');
+                } else {
+                    break;
+                }
+            }
+            sw.write(c);
+        }
+        sw.write(";out.print(\"");
+    }
 
-       public Writable make(final Map map) {
-           return new Writable() {
-               /**
-                * Write the template document with the set binding applied to the writer.
-                *
-                * @see groovy.lang.Writable#writeTo(java.io.Writer)
-                */
-               public Writer writeTo(Writer writer) throws IOException {
-                   Binding binding;
-                   if (map == null) binding = new Binding(); else binding = new Binding(map);
-                   Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);
-                   PrintWriter pw = new PrintWriter(writer);
-                   scriptObject.setProperty("out", pw);
-                   scriptObject.run();
-                   pw.flush();
-                   return writer;
-               }
+    public Writable make() {
+        return make(null);
+    }
 
-               /**
-                * Convert the template and binding into a result String.
-                *
-                * @see java.lang.Object#toString()
-                */
-               public String toString() {
-                   try {
-                       StringWriter sw = new StringWriter();
-                       writeTo(sw);
-                       return sw.toString();
-                   } catch (Exception e) {
-                       return e.toString();
-                   }
-               }
-           };
-       }
-   }
-   }
+    public Writable make(final Map map) {
+        return new Writable() {
+            /**
+             * Write the template document with the set binding applied to the writer.
+             *
+             * @see groovy.lang.Writable#writeTo(java.io.Writer)
+             */
+            public Writer writeTo(Writer writer) throws IOException {
+                Binding binding;
+                if (map == null) binding = new Binding(); else binding = new Binding(map);
+                Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);
+                PrintWriter pw = new PrintWriter(writer);
+                scriptObject.setProperty("out", pw);
+                scriptObject.run();
+                pw.flush();
+                return writer;
+            }
+
+            /**
+             * Convert the template and binding into a result String.
+             *
+             * @see java.lang.Object#toString()
+             */
+            public String toString() {
+                try {
+                    StringWriter sw = new StringWriter();
+                    writeTo(sw);
+                    return sw.toString();
+                } catch (Exception e) {
+                    return e.toString();
+                }
+            }
+        };
+    }
+}
+    protected static void clearMetaClassRegistry(MetaClassRegistry mcr) {
+        Map map = (Map) com.xpn.xwiki.XWiki.getPrivateField(mcr, "metaClasses");
+        map.clear();
+        Map map2 = (Map) com.xpn.xwiki.XWiki.getPrivateField(mcr, "loaderMap");
+        map2.clear();
+
+        Class[] classes;
+        Object[] objects;
+
+        classes = new Class[1];
+        classes[0] = DefaultGroovyMethods.class.getClass();
+        objects = new Object[1];
+        objects[0] = DefaultGroovyMethods.class;
+        com.xpn.xwiki.XWiki.callPrivateMethod(mcr, "lookup", classes, objects);
+
+        classes = new Class[2];
+        classes[0] = DefaultGroovyMethods.class.getClass();
+        classes[1] = boolean.class;
+        objects = new Object[2];
+        objects[0] = DefaultGroovyMethods.class;
+        objects[1] = true;
+        com.xpn.xwiki.XWiki.callPrivateMethod(mcr, "registerMethods", classes, objects);
+
+        classes = new Class[1];
+        classes[0] = DefaultGroovyStaticMethods.class.getClass();
+        objects = new Object[1];
+        objects[0] = DefaultGroovyStaticMethods.class;
+        com.xpn.xwiki.XWiki.callPrivateMethod(mcr, "lookup", classes, objects);
+
+        classes = new Class[2];
+        classes[0] = DefaultGroovyStaticMethods.class.getClass();
+        classes[1] = boolean.class;
+        objects = new Object[2];
+        objects[0] = DefaultGroovyStaticMethods.class;
+        objects[1] = false;
+        com.xpn.xwiki.XWiki.callPrivateMethod(mcr, "registerMethods", classes, objects);
+
+        com.xpn.xwiki.XWiki.callPrivateMethod(mcr, "checkInitialised");
+    }
+
+    public static void flushCache() {
+        // Clear up groovy registry
+        MetaClassRegistry mcr = MetaClassRegistry.getIntance(0);
+        clearMetaClassRegistry(mcr);
+        mcr = MetaClassRegistry.getIntance(1);
+        clearMetaClassRegistry(mcr);
+        mcr = InvokerHelper.getInstance().getMetaRegistry();
+        clearMetaClassRegistry(mcr);
+        Introspector.flushCaches();
+    }
+
+    public static void removeClass(Class clazz) {
+        // Clear up groovy registry
+        MetaClassRegistry mcr = MetaClassRegistry.getIntance(0);
+        mcr.removeMetaClass(clazz);
+        mcr = MetaClassRegistry.getIntance(1);
+        mcr.removeMetaClass(clazz);
+        mcr = InvokerHelper.getInstance().getMetaRegistry();
+        mcr.removeMetaClass(clazz);
+    }
+
+}





More information about the Xwiki-notifications mailing list