EXTSCRIPT-154: Code Rewrite/Refactoring,cleaning up the compiler interface ongoing works on the components


git-svn-id: https://svn.apache.org/repos/asf/myfaces/extensions/scripting/trunk@1300132 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/api/WeavingContext.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/api/WeavingContext.java
index 2c361c3..c0572d7 100644
--- a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/api/WeavingContext.java
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/api/WeavingContext.java
@@ -23,6 +23,7 @@
 import rewrite.org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
 import rewrite.org.apache.myfaces.extensions.scripting.core.engine.FactoryEngines;
 import rewrite.org.apache.myfaces.extensions.scripting.core.engine.api.ClassScanner;
+import rewrite.org.apache.myfaces.extensions.scripting.core.engine.api.CompilationResult;
 import rewrite.org.apache.myfaces.extensions.scripting.core.engine.api.ScriptingEngine;
 import rewrite.org.apache.myfaces.extensions.scripting.core.loader.ThrowAwayClassloader;
 import rewrite.org.apache.myfaces.extensions.scripting.core.monitor.ClassResource;
@@ -34,10 +35,7 @@
 import java.io.IOException;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Proxy;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Logger;
@@ -51,23 +49,90 @@
 
 public class WeavingContext
 {
+    static final Logger log = Logger.getLogger(WeavingContext.class.getName());
+
     /**
      * lock var which can be used for recompilation
      */
     public AtomicBoolean recompileLock = new AtomicBoolean(false);
+    /**
+     * configuration which stores all external configuration entries
+     */
     protected Configuration configuration = new Configuration();
 
     //ClassDependencies _dependencyMap = new ClassDependencies();
-
+    /**
+     * Service provider for the implementation under which this extension
+     * runs
+     */
     ImplementationSPI _implementation = null;
+    /**
+     * the collection of reloading strategies depending on their artifact type
+     */
     GlobalReloadingStrategy _reloadingStrategy = new GlobalReloadingStrategy();
+    /**
+     * the annotation scanning reference
+     */
     ClassScanner _annotationScanner = null;
-    Logger log = Logger.getLogger(this.getClass().getName());
+
+    /**
+     * true only if the startup has performed without errors
+     */
     boolean _scriptingEnabled = true;
 
-    /*holder for various operations within our lifecycle*/
+    /**
+     * holder for various operations within our lifecycle
+     */
     ConcurrentHashMap<String, Long> lifecycleRegistry = new ConcurrentHashMap<String, Long>();
 
+    /**
+     * This is a log which keeps track of the taints
+     * over time, we need that mostly for bean refreshes
+     * in multiuser surroundings, because only tainted beans need
+     * to be refreshed.
+     * Now if a user misses multiple updates he has to get a full
+     * set of changed classes to be able to drop all personal scoped beans tainted
+     * since the he refreshed last! Hence we have to move away from our
+     * two dimensional &lt;class, taint&gt; to a three dimensional &lt;class, taint, time&gt;
+     * view of things
+     */
+    private List<TaintingHistoryEntry> _taintLog = Collections.synchronizedList(new LinkedList<TaintingHistoryEntry>());
+
+    /**
+     * compilation results holder for the compiler listeners (components etc...)
+     */
+    private static final Map<Integer, CompilationResult> _compilationResults = new ConcurrentHashMap<Integer, CompilationResult>();
+
+    /**
+     * we keep a 10 minutes timeout period to keep the performance in place
+     */
+    private final long TAINT_HISTORY_TIMEOUT = 10 * 60 * 1000;
+
+    /**
+     * internal class used by our own history log
+     */
+    static class TaintingHistoryEntry
+    {
+        long _timestamp;
+        ClassResource _data;
+
+        public TaintingHistoryEntry(ClassResource data)
+        {
+            _data = data;
+            _timestamp = System.currentTimeMillis();
+        }
+
+        public long getTimestamp()
+        {
+            return _timestamp;
+        }
+
+        public ClassResource getData()
+        {
+            return _data;
+        }
+    }
+
     public void initEngines() throws IOException
     {
         FactoryEngines.getInstance().init();
@@ -115,6 +180,9 @@
         return ret;
     }
 
+    /**
+     * @return a map of all watched resources over all engines
+     */
     public Map<String, ClassResource> getAllWatchedResources()
     {
         Map<String, ClassResource> ret = new HashMap<String, ClassResource>();
@@ -129,6 +197,10 @@
         return ret;
     }
 
+    /**
+     * @param key the watched resource classname
+     * @return the watched resource from the given key or null
+     */
     public ClassResource getWatchedResource(String key)
     {
         for (ScriptingEngine engine : getEngines())
@@ -139,6 +211,12 @@
         return null;
     }
 
+    /**
+     * checks if a resource idenified by key is tainted
+     *
+     * @param key the identifier for the resource
+     * @return true in case of being tainted
+     */
     public boolean isTainted(String key)
     {
         ClassResource res = getWatchedResource(key);
@@ -353,6 +431,9 @@
         lifecycleRegistry.put("LIFECYCLE_LAST_TAINTED", System.currentTimeMillis());
     }
 
+    /**
+     * @return the time value of the last taint happening
+     */
     public long getLastTaint()
     {
         Long lastTainted = lifecycleRegistry.get("LIFECYCLE_LAST_TAINTED");
@@ -360,11 +441,17 @@
         return lastTainted;
     }
 
+    /**
+     * marks the last annotation scan
+     */
     public void markLastAnnotationScan()
     {
         lifecycleRegistry.put("LIFECYCLE_LAST_ANN_SCAN", System.currentTimeMillis());
     }
 
+    /**
+     * @return a the time value of the last annotation scan
+     */
     public long getLastAnnotationScan()
     {
         Long lastTainted = lifecycleRegistry.get("LIFECYCLE_LAST_ANN_SCAN");
@@ -372,6 +459,111 @@
         return lastTainted;
     }
 
+    //------------------------------ tainting history entries -----------------------
+
+    /**
+     * adds a new entry into our taint log
+     * which allows us to access tainting data
+     * from a given point in time
+     *
+     * @param data the tainting data to be added
+     */
+    public void addTaintLogEntry(ClassResource data)
+    {
+        _taintLog.add(new TaintingHistoryEntry(data));
+    }
+
+    /**
+     * garbage collects our tainting data
+     * and removes all entries which are not
+     * present anymore due to timeout
+     * this gc code is called asynchronously
+     * from our tainting thread to keep the
+     * performance intact
+     */
+    public void gcTaintLog()
+    {
+        long timeoutTimestamp = System.currentTimeMillis() - TAINT_HISTORY_TIMEOUT;
+        Iterator<TaintingHistoryEntry> it = _taintLog.iterator();
+
+        while (it.hasNext())
+        {
+            TaintingHistoryEntry entry = it.next();
+            if (entry.getTimestamp() < timeoutTimestamp)
+            {
+                it.remove();
+            }
+        }
+    }
+
+    /**
+     * returns the last noOfEntries entries in the taint history
+     *
+     * @param noOfEntries the number of entries to be delivered
+     * @return a collection of the last &lt;noOfEntries&gt; entries
+     */
+    public Collection<ClassResource> getLastTainted(int noOfEntries)
+    {
+        Iterator<TaintingHistoryEntry> it = _taintLog.subList(Math.max(_taintLog.size() - noOfEntries, 0), _taintLog.size()).iterator();
+        List<ClassResource> retVal = new LinkedList<ClassResource>();
+        while (it.hasNext())
+        {
+            TaintingHistoryEntry entry = it.next();
+            retVal.add(entry.getData());
+        }
+        return retVal;
+    }
+
+    /**
+     * Returns a set of tainting data from a given point in time up until now
+     *
+     * @param timestamp the point in time from which the tainting data has to be derived from
+     * @return a set of entries which are a union of all points in time beginning from timestamp
+     */
+    public Collection<ClassResource> getTaintHistory(long timestamp)
+    {
+        List<ClassResource> retVal = new LinkedList<ClassResource>();
+        Iterator<TaintingHistoryEntry> it = _taintLog.iterator();
+
+        while (it.hasNext())
+        {
+            TaintingHistoryEntry entry = it.next();
+            if (entry.getTimestamp() >= timestamp)
+            {
+                retVal.add(entry.getData());
+            }
+        }
+        return retVal;
+    }
+
+    /**
+     * Returns a set of tainted classes from a given point in time up until now
+     *
+     * @param timestamp the point in time from which the tainting data has to be derived from
+     * @return a set of classnames which are a union of all points in time beginning from timestamp
+     */
+    public Set<String> getTaintHistoryClasses(long timestamp)
+    {
+        Set<String> retVal = new HashSet<String>();
+        Iterator<TaintingHistoryEntry> it = _taintLog.iterator();
+
+        while (it.hasNext())
+        {
+            TaintingHistoryEntry entry = it.next();
+            if (entry.getTimestamp() >= timestamp)
+            {
+                if (entry.getData() instanceof ClassResource)
+                {
+                    retVal.add(((ClassResource) entry.getData()).getAClass().getName());
+                } else
+                {
+                    retVal.add(entry.getData().getFile().getAbsolutePath());
+                }
+            }
+        }
+        return retVal;
+    }
+
     //----------------------------------------------------------------------
     /*public ClassDependencies getDependencyMap()
     {
@@ -410,4 +602,14 @@
         _scriptingEnabled = scriptingEnabled;
     }
 
+    public CompilationResult getCompilationResult(Integer scriptingEngine)
+    {
+        return _compilationResults.get(scriptingEngine);
+    }
+
+    public void setCompilationResult(Integer scriptingEngine, CompilationResult result)
+    {
+        _compilationResults.put(scriptingEngine, result);
+    }
+
 }
diff --git a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/common/util/FileUtils.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/common/util/FileUtils.java
index 5894256..472acd9 100644
--- a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/common/util/FileUtils.java
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/common/util/FileUtils.java
@@ -76,7 +76,7 @@
         File tempDir;
 
         String baseTempPath = System.getProperty("java.io.tmpdir");
-        String tempDirName = "myfaces_compilation_"; //+ _tempMarker;
+        String tempDirName = "myfaces_compilation_" + _tempMarker;
 
         tempDir = new File(baseTempPath + File.separator + tempDirName);
         /*while (tempDir.exists()) {
diff --git a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/EngineJava.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/EngineJava.java
index 36b6764..e329ff7 100644
--- a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/EngineJava.java
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/EngineJava.java
@@ -23,6 +23,7 @@
 import rewrite.org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
 import rewrite.org.apache.myfaces.extensions.scripting.core.common.util.ClassUtils;
 import rewrite.org.apache.myfaces.extensions.scripting.core.engine.api.CompilationException;
+import rewrite.org.apache.myfaces.extensions.scripting.core.engine.api.CompilationResult;
 import rewrite.org.apache.myfaces.extensions.scripting.core.engine.api.ScriptingEngine;
 import rewrite.org.apache.myfaces.extensions.scripting.core.engine.compiler.JSR199Compiler;
 
@@ -62,15 +63,12 @@
         Collection<String> sourceDirs = configuration.getSourceDirs(getEngineType());
         for (String sourceRoot : sourceDirs)
         {
-            try
-            {
-                compiler.compile(new File(sourceRoot), targetDir, ClassUtils.getContextClassLoader());
+            CompilationResult res =  compiler.compile(new File(sourceRoot), targetDir,
+                ClassUtils.getContextClassLoader());
+            if(res.hasErrors()) {
+                log.severe(res.getCompilerOutput());
             }
-            catch (CompilationException e)
-            {
-                log.severe(e.getMessage());
-                e.printStackTrace();
-            }
+
         }
     }
 
diff --git a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/api/Compiler.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/api/Compiler.java
index 34f8fcd..038f801 100644
--- a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/api/Compiler.java
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/api/Compiler.java
@@ -40,7 +40,6 @@
      * @throws CompilationException
      *          if a severe error occurred while trying to compile a file
      */
-    public CompilationResult compile(File sourcePath, File targetPath, ClassLoader classLoader)
-            throws CompilationException;
+    public CompilationResult compile(File sourcePath, File targetPath, ClassLoader classLoader);
 
 }
diff --git a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/compiler/GroovyCompiler.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/compiler/GroovyCompiler.java
index b522c27..0ce2f82 100644
--- a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/compiler/GroovyCompiler.java
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/compiler/GroovyCompiler.java
@@ -96,6 +96,10 @@
             result.registerWarning(convertMessage(collector.getWarning(i)));
         }
 
+        if(result != null) {
+             WeavingContext.getInstance().setCompilationResult(ScriptingConst.ENGINE_TYPE_JSF_GROOVY, result);
+        }
+
         return result;
     }
 
diff --git a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/compiler/JSR199Compiler.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/compiler/JSR199Compiler.java
index e69fba7..68a0e56 100644
--- a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/compiler/JSR199Compiler.java
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/engine/compiler/JSR199Compiler.java
@@ -20,6 +20,7 @@
 package rewrite.org.apache.myfaces.extensions.scripting.core.engine.compiler;
 
 import rewrite.org.apache.myfaces.extensions.scripting.core.api.Configuration;
+import rewrite.org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
 import rewrite.org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
 import rewrite.org.apache.myfaces.extensions.scripting.core.common.util.FileUtils;
 import rewrite.org.apache.myfaces.extensions.scripting.core.engine.api.CompilationException;
@@ -51,7 +52,7 @@
 
     javax.tools.JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
     ContainerFileManager fileManager = null;
-    CompilationResult result = null;
+
 
     public JSR199Compiler()
     {
@@ -59,56 +60,6 @@
     }
 
     /**
-     * Compile a single file
-     *
-     * @param sourceRoot the source search path (root of our source)
-     * @return the compilation result of the  compilation
-     * @throws org.apache.myfaces.extensions.scripting.api.CompilationException
-     *          in case of a compilation error
-     * @deprecated note we will move over to a single
-     *             compile step in the beginning in the long run
-     *             we will deprecate it as soon as the full
-     *             compile at the beginning of the request
-     *             is implemented
-     *             <p/>
-     *             TODO move this code over to the weaver instead of the compiler
-     *             we do not do a single compile step anymore
-     */
-    public CompilationResult compile(File sourceRoot, File targetPath, File toCompile, ClassLoader classPathHolder) throws CompilationException
-    {
-        try
-        {
-            fileManager = new ContainerFileManager(javaCompiler.getStandardFileManager(new DiagnosticCollector<JavaFileObject>(), null, null));
-
-            DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
-
-            //TODO add whitelist check here
-
-            getLog().info("[EXT-SCRIPTING] Doing a full recompile");
-
-            Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects(toCompile);
-            String[] options = new String[]{JC_CLASSPATH, fileManager.getClassPath(), JC_TARGET_PATH,
-                                            targetPath.getAbsolutePath(),
-                                            JC_SOURCEPATH,
-                                            sourceRoot.getAbsolutePath(), JC_DEBUG};
-            javaCompiler.getTask(null, fileManager, diagnosticCollector, Arrays.asList(options), null, fileObjects).call();
-            try
-            {
-                handleDiagnostics(diagnosticCollector);
-            }
-            catch (ClassNotFoundException e)
-            {
-                throw new CompilationException(e);
-            }
-            return this.result;
-        }
-        finally
-        {
-            this.result = null;
-        }
-    }
-
-    /**
      * compile all files starting from a given root
      * <p/>
      * note, the java compiler interface does not allow per se
@@ -122,10 +73,7 @@
      * @throws org.apache.myfaces.extensions.scripting.api.CompilationException
      *          in case of a compilation error
      */
-    public CompilationResult compile(File sourceRoot, File destination, ClassLoader loader) throws CompilationException
-    {
-        try
-        {
+    public CompilationResult compile(File sourceRoot, File destination, ClassLoader loader)   {
             WeavingContext context = WeavingContext.getInstance();
             Configuration configuration = context.getConfiguration();
 
@@ -135,8 +83,6 @@
 
             getLog().info("[EXT-SCRIPTING] Doing a full recompile");
 
-            //List<File> sourceFiles = FileUtils.fetchSourceFiles(WeavingContext.getConfiguration()
-            //    .getWhitelistedSourceDirs(ENGINE_TYPE_JSF_JAVA), JAVA_WILDCARD);
             List<File> sourceFiles = FileUtils.fetchSourceFiles(configuration.getWhitelistedSourceDirs
                     (ENGINE_TYPE_JSF_JAVA), JAVA_WILDCARD);
 
@@ -152,20 +98,13 @@
                                             destination.getAbsolutePath(), JC_SOURCEPATH,
             sourceRoot.getAbsolutePath(), JC_DEBUG};
             javaCompiler.getTask(null, fileManager, diagnosticCollector, Arrays.asList(options), null, fileObjects).call();
-            try
-            {
-                handleDiagnostics(diagnosticCollector);
-            }
-            catch (ClassNotFoundException e)
-            {
-                throw new CompilationException(e);
-            }
-            return this.result;
-        }
-        finally
-        {
-            this.result = null;
-        }
+
+
+            CompilationResult result =  handleDiagnostics(diagnosticCollector);
+
+            WeavingContext.getInstance().setCompilationResult(ScriptingConst.ENGINE_TYPE_JSF_JAVA, result);
+            return result;
+
     }
 
     /**
@@ -177,8 +116,7 @@
      * @throws ClassNotFoundException in case of an error (this is enforced by the compiler interface
      *                                and probably will be overhauled in the long run)
      */
-    private void handleDiagnostics(DiagnosticCollector<JavaFileObject> diagnosticCollector) throws
-                                                                                            ClassNotFoundException
+    private CompilationResult handleDiagnostics(DiagnosticCollector<JavaFileObject> diagnosticCollector)
     {
         if (diagnosticCollector.getDiagnostics().size() > 0)
         {
@@ -200,15 +138,13 @@
                     result.getWarnings().add(new CompilationResult.CompilationMessage(diagnostic.getLineNumber(), diagnostic.getMessage(Locale.getDefault())));
                 }
                 errors.append(error);
+
             }
-            this.result = result;
-            //WeavingContext.setCompilationResult(ENGINE_TYPE_JSF_JAVA, result);
-            assertErrorFound(errors, hasError);
-            //return result;
+            return result;
         } else
         {
             //WeavingContext.setCompilationResult(ENGINE_TYPE_JSF_JAVA, new CompilationResult(""));
-            this.result = new CompilationResult("");
+            return new CompilationResult("");
         }
     }
 
diff --git a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/monitor/ClassResource.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/monitor/ClassResource.java
index 367fb16..a7a3297 100644
--- a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/monitor/ClassResource.java
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/core/monitor/ClassResource.java
@@ -152,6 +152,8 @@
             WeavingContext.getInstance().markLastTaint();
             //TODO add logging event here
             logger.info("[EXT-SCRIPTING] tainting " + getSourceFile());
+            WeavingContext.getInstance().addTaintLogEntry(this);
+            WeavingContext.getInstance().gcTaintLog();
         }
         tainted = value;
     }
diff --git a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/CompilerComponent.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/CompilerComponent.java
new file mode 100644
index 0000000..58cbd9b
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/CompilerComponent.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package rewrite.org.apache.myfaces.extensions.scripting.jsf.components;
+
+
+import rewrite.org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import rewrite.org.apache.myfaces.extensions.scripting.core.common.util.StringUtils;
+
+import javax.el.ValueExpression;
+import javax.faces.component.UIOutput;
+import javax.faces.context.FacesContext;
+import java.util.Locale;
+
+/**
+ * Compiler component which currently
+ * just shows the last compile output in the system
+ * <p/>
+ * Not to keep backwards compatibility to JSF 1.2
+ * we do not use the StateHelper but go the old route
+ * instead
+ */
+@SuppressWarnings("unused")
+public class CompilerComponent extends UIOutput {
+
+    String _scriptingLanguage = null;
+    String _errorsLabel = null;
+    String _warningsLabel = null;
+    private static final String RENDERER_TYPE = "org.apache.myfaces.extensions.scripting.components.CompilerComponentRenderer";
+    private static final String ERRORS_LABEL = "errorsLabel";
+    private static final String WARNINGS_LABEL = "warningsLabel";
+    private static final String SCRIPTING_LANGUAGE = "scriptingLanguage";
+
+    public CompilerComponent() {
+        super();
+        setRendererType(RENDERER_TYPE);
+    }
+
+    @Override
+    public boolean isTransient() {
+        return true;
+    }
+
+    @Override
+    public Object saveState(FacesContext facesContext) {
+        Object values[] = new Object[4];
+        values[0] = super.saveState(facesContext);    //To change body of overridden methods use File | Settings | File Templates.
+        values[1] = _scriptingLanguage;
+        values[2] = _errorsLabel;
+        values[3] = _warningsLabel;
+        return values;
+    }
+
+    @Override
+    public void restoreState(FacesContext facesContext, Object state) {
+        Object[] values = (Object[]) state;
+        super.restoreState(facesContext, values[0]);
+
+        _scriptingLanguage = (String) values[1];
+        _errorsLabel = (String) values[2];
+        _warningsLabel = (String) values[3];
+    }
+
+
+    public String getScriptingLanguage() {
+        if (_scriptingLanguage != null) {
+            return _scriptingLanguage;
+        }
+        ValueExpression vb = getValueExpression(SCRIPTING_LANGUAGE);
+        return vb != null ? ((String) vb.getValue(getFacesContext().getELContext())) : null;
+    }
+
+    public Integer getScriptingLanguageAsInt() {
+        if (StringUtils.isBlank(_scriptingLanguage)) {
+            return ScriptingConst.ENGINE_TYPE_JSF_ALL;
+        } else {
+            String scriptingLanguage = _scriptingLanguage.toLowerCase(Locale.getDefault()).trim();
+            if (scriptingLanguage.equals("java")) {
+                return ScriptingConst.ENGINE_TYPE_JSF_JAVA;
+            } else if (_scriptingLanguage.toLowerCase(Locale.getDefault()).trim().equals("groovy")) {
+                return ScriptingConst.ENGINE_TYPE_JSF_GROOVY;
+            }
+        }
+        return ScriptingConst.ENGINE_TYPE_JSF_NO_ENGINE;
+    }
+
+    public void setScriptingLanguage(String scriptingLanguage) {
+        _scriptingLanguage = scriptingLanguage;
+    }
+
+    public String getErrorsLabel() {
+        if (_errorsLabel != null) {
+            return _errorsLabel;
+        }
+        ValueExpression vb = getValueExpression(ERRORS_LABEL);
+        return vb != null ? ((String) vb.getValue(getFacesContext().getELContext())) : null;
+    }
+
+    public void setErrorsLabel(String _errorsLabel) {
+        this._errorsLabel = _errorsLabel;
+    }
+
+    public String getWarningsLabel() {
+        if (_warningsLabel != null) {
+            return _warningsLabel;
+        }
+        ValueExpression vb = getValueExpression(WARNINGS_LABEL);
+        return vb != null ? ((String) vb.getValue(getFacesContext().getELContext())) : null;
+    }
+
+    public void setWarningsLabel(String _warningsLabel) {
+        this._warningsLabel = _warningsLabel;
+    }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/CompilerComponentRenderer.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/CompilerComponentRenderer.java
new file mode 100644
index 0000000..196777e
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/CompilerComponentRenderer.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package rewrite.org.apache.myfaces.extensions.scripting.jsf.components;
+
+import rewrite.org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import rewrite.org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import rewrite.org.apache.myfaces.extensions.scripting.core.common.util.StringUtils;
+import rewrite.org.apache.myfaces.extensions.scripting.core.engine.api.CompilationResult;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.render.Renderer;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+/**
+ * Renderer for the compiler component
+ * <p/>
+ * This renderer is responsible for rendering the last compiler output
+ * hosted in our weavingContext
+ */
+@SuppressWarnings("unchecked")
+public class CompilerComponentRenderer extends Renderer {
+
+    @Override
+    public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
+        super.encodeBegin(context, component);
+
+        ResponseWriter responseWriter = FacesContext.getCurrentInstance().getResponseWriter();
+        CompilerComponent compilerComp = (CompilerComponent) component;
+
+        Integer scriptingLanguage = compilerComp.getScriptingLanguageAsInt();
+        CompilationResult result = null;
+        switch (scriptingLanguage) {
+            case ScriptingConst.ENGINE_TYPE_JSF_JAVA:
+                result = WeavingContext.getInstance().getCompilationResult(ScriptingConst.ENGINE_TYPE_JSF_JAVA);
+                break;
+            case ScriptingConst.ENGINE_TYPE_JSF_GROOVY:
+                result = WeavingContext.getInstance().getCompilationResult(ScriptingConst.ENGINE_TYPE_JSF_GROOVY);
+                break;
+            case ScriptingConst.ENGINE_TYPE_JSF_ALL:
+                result = new CompilationResult("");
+                CompilationResult tempResult = WeavingContext.getInstance().getCompilationResult(ScriptingConst.ENGINE_TYPE_JSF_JAVA);
+                if (tempResult != null) {
+                    copyCompilationResult(result, tempResult);
+                }
+
+                tempResult = WeavingContext.getInstance().getCompilationResult(ScriptingConst.ENGINE_TYPE_JSF_GROOVY);
+                if (tempResult != null) {
+                    copyCompilationResult(result, tempResult);
+                }
+
+                break;
+            case ScriptingConst.ENGINE_TYPE_JSF_NO_ENGINE:
+                Logger log = Logger.getLogger(this.getClass().getName());
+                log.warning(RendererConst.WARNING_ENGINE_NOT_FOUND);
+                break;
+        }
+
+        startDiv(component, responseWriter, RendererConst.ERROR_BOX);
+        if (result == null || (!result.hasErrors() && result.getWarnings().isEmpty())) {
+            responseWriter.write(RendererConst.NO_COMPILE_ERRORS);
+        } else {
+            writeErrorsLabel(component, responseWriter, compilerComp);
+            writeErrors(component, responseWriter, result);
+            writeWarningsLabel(component, responseWriter, compilerComp);
+            writeWarnings(component, responseWriter, result);
+        }
+        endDiv(responseWriter);
+
+        responseWriter.flush();
+
+    }
+
+    private void writeWarnings(UIComponent component, ResponseWriter responseWriter, CompilationResult result) throws IOException {
+        startDiv(component, responseWriter, RendererConst.WARNINGS);
+        for (CompilationResult.CompilationMessage msg : result.getWarnings()) {
+            startDiv(component, responseWriter, RendererConst.LINE);
+            writeDiv(component, responseWriter, RendererConst.LINE_NO, String.valueOf(msg.getLineNumber()));
+            writeDiv(component, responseWriter, RendererConst.MESSAGE, msg.getMessage());
+            endDiv(responseWriter);
+        }
+        endDiv(responseWriter);
+    }
+
+    private void writeWarningsLabel(UIComponent component, ResponseWriter responseWriter, CompilerComponent compilerComp) throws IOException {
+        if (!StringUtils.isBlank(compilerComp.getWarningsLabel())) {
+            startDiv(component, responseWriter, RendererConst.WARNINGS_LABEL);
+            responseWriter.write(compilerComp.getWarningsLabel());
+            endDiv(responseWriter);
+        }
+    }
+
+    private void writeErrors(UIComponent component, ResponseWriter responseWriter, CompilationResult result) throws IOException {
+        startDiv(component, responseWriter, RendererConst.ERRORS);
+        for (CompilationResult.CompilationMessage msg : result.getErrors()) {
+            startDiv(component, responseWriter, RendererConst.LINE);
+            writeDiv(component, responseWriter, RendererConst.LINE_NO, String.valueOf(msg.getLineNumber()));
+            writeDiv(component, responseWriter, RendererConst.MESSAGE, msg.getMessage());
+            endDiv(responseWriter);
+        }
+        endDiv(responseWriter);
+    }
+
+    private String writeDiv(UIComponent component, ResponseWriter responseWriter, String styleClass, String value) throws IOException {
+        startDiv(component, responseWriter, styleClass);
+        responseWriter.write(value);
+        endDiv(responseWriter);
+        return "";
+    }
+
+    private void endDiv(ResponseWriter responseWriter) throws IOException {
+        responseWriter.endElement(RendererConst.HTML_DIV);
+    }
+
+    private void startDiv(UIComponent component, ResponseWriter responseWriter, String styleClass) throws IOException {
+        responseWriter.startElement(RendererConst.HTML_DIV, component);
+        responseWriter.writeAttribute(RendererConst.HTML_CLASS, styleClass, null);
+    }
+
+    private void writeErrorsLabel(UIComponent component, ResponseWriter responseWriter, CompilerComponent compilerComp) throws IOException {
+        if (!StringUtils.isBlank(compilerComp.getErrorsLabel())) {
+            startDiv(component, responseWriter, RendererConst.ERRORS_LABEL);
+            responseWriter.write(compilerComp.getErrorsLabel());
+            endDiv(responseWriter);
+        }
+    }
+
+    private void copyCompilationResult(CompilationResult result, CompilationResult tempResult) {
+        result.getErrors().addAll(tempResult.getErrors());
+        result.getWarnings().addAll(tempResult.getWarnings());
+    }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/RendererConst.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/RendererConst.java
new file mode 100644
index 0000000..5e31042
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/RendererConst.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package rewrite.org.apache.myfaces.extensions.scripting.jsf.components;
+
+/**
+ * Renderer Constant shared by both renderers
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class RendererConst {
+    static final String ERROR_BOX = "errorBox";
+    static final String WARNING_ENGINE_NOT_FOUND = "Warning engine not found";
+    static final String LINE_NO = "lineNo";
+    static final String MESSAGE = "message";
+    static final String NO_COMPILE_ERRORS = "No compile errors";
+    static final String HTML_DIV = "div";
+    static final String HTML_CLASS = "class";
+    static final String NO_TAINT_HISTORY_FOUND = "No taint history found";
+    static final String LINE = "line";
+    static final String TIMESTAMP = "timestamp";
+    static final String CHANGED_FILE = "changedFile";
+    static final String ERRORS_LABEL = "errorsLabel";
+    static final String WARNINGS_LABEL = "warningsLabel";
+    static final String ERRORS = "errors";
+    static final String WARNINGS = "warnings";
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/TaintHistory.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/TaintHistory.java
new file mode 100644
index 0000000..ea4af67
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/TaintHistory.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package rewrite.org.apache.myfaces.extensions.scripting.jsf.components;
+
+import javax.el.ValueExpression;
+import javax.faces.component.UIOutput;
+import javax.faces.context.FacesContext;
+
+/**
+ * Component which allows to check which files
+ * have been marked as possibly modified in the recent history
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class TaintHistory extends UIOutput {
+
+    public static final int DEFAULT_NO_ENTRIES = 10;
+
+    Integer _noEntries;
+    String _filter;
+    private static final String RENDERER_TYPE = "org.apache.myfaces.extensions.scripting.components.TaintHistoryRenderer";
+    private static final String NO_ENTRIES = "noEntries";
+
+    public TaintHistory() {
+        setRendererType(RENDERER_TYPE);
+    }
+
+    @SuppressWarnings("unused")
+    public void setNoEntries(Integer entries) {
+        _noEntries = entries;
+    }
+
+    @Override
+    public Object saveState(FacesContext facesContext) {
+        Object values[] = new Object[3];
+        values[0] = super.saveState(facesContext);    //To change body of overridden methods use File | Settings | File Templates.
+        values[1] = _noEntries;
+        values[2] = _filter;
+        return values;
+    }
+
+    @Override
+    public void restoreState(FacesContext facesContext, Object state) {
+        Object[] values = (Object[]) state;
+        super.restoreState(facesContext, values[0]);
+        _noEntries = (Integer) values[1];
+        _filter = (String) values[2];
+    }
+
+    public Integer getNoEntries() {
+        if (_noEntries != null) {
+            return _noEntries;
+        }
+        ValueExpression vb = getValueExpression(NO_ENTRIES);
+        return vb != null ? ((Integer) vb.getValue(getFacesContext().getELContext())) : DEFAULT_NO_ENTRIES;
+    }
+
+    @SuppressWarnings("unused")
+    public void setFilter(String filter) {
+        _filter = filter;
+    }
+
+    @SuppressWarnings("unused")
+    public String getFilter() {
+        if (_filter != null) {
+            return _filter;
+        }
+        ValueExpression vb = getValueExpression(NO_ENTRIES);
+        return vb != null ? ((String) vb.getValue(getFacesContext().getELContext())) : null;
+    }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/TaintHistoryRenderer.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/TaintHistoryRenderer.java
new file mode 100644
index 0000000..c89550c
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/components/TaintHistoryRenderer.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package rewrite.org.apache.myfaces.extensions.scripting.jsf.components;
+
+
+import rewrite.org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import rewrite.org.apache.myfaces.extensions.scripting.core.monitor.ClassResource;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.render.Renderer;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.util.Collection;
+
+/**
+ * A renderer which displays our taint history
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+@SuppressWarnings("unchecked")
+//we have to suppress here because of the component cast
+public class TaintHistoryRenderer extends Renderer {
+
+    @Override
+    public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
+        super.encodeBegin(context, component);
+
+        ResponseWriter responseWriter = FacesContext.getCurrentInstance().getResponseWriter();
+
+        startDiv(component, responseWriter, "historyBox");
+        int lastTainted = ((TaintHistory) component).getNoEntries();
+
+        Collection<ClassResource> result = WeavingContext.getInstance().getLastTainted(lastTainted);
+        if (result == null || result.isEmpty()) {
+            responseWriter.write(RendererConst.NO_TAINT_HISTORY_FOUND);
+        } else {
+            writeHistory(component, responseWriter, result);
+        }
+        endDiv(responseWriter);
+
+        responseWriter.flush();
+
+    }
+
+    private void writeHistory(UIComponent component, ResponseWriter responseWriter,
+                              Collection<ClassResource> result) throws IOException {
+        startDiv(component, responseWriter, "history");
+        for (ClassResource entry : result) {
+            startDiv(component, responseWriter, RendererConst.LINE);
+            writeDiv(component, responseWriter, RendererConst.TIMESTAMP, DateFormat.getInstance().format(entry.getFile().lastModified()));
+            writeDiv(component, responseWriter, RendererConst.CHANGED_FILE, entry.getFile().getAbsolutePath());
+            endDiv(responseWriter);
+        }
+
+        endDiv(responseWriter);
+    }
+
+    private String writeDiv(UIComponent component, ResponseWriter responseWriter, String styleClass, String value) throws IOException {
+        startDiv(component, responseWriter, styleClass);
+        responseWriter.write(value);
+        endDiv(responseWriter);
+        return "";
+    }
+
+    private void endDiv(ResponseWriter responseWriter) throws IOException {
+        responseWriter.endElement(RendererConst.HTML_DIV);
+    }
+
+    private void startDiv(UIComponent component, ResponseWriter responseWriter, String styleClass) throws IOException {
+        responseWriter.startElement(RendererConst.HTML_DIV, component);
+        responseWriter.writeAttribute(RendererConst.HTML_CLASS, styleClass, null);
+    }
+
+}
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/JavaCompilerTest.java b/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/JavaCompilerTest.java
index 404d408..8c93322 100644
--- a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/JavaCompilerTest.java
+++ b/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/JavaCompilerTest.java
@@ -105,14 +105,8 @@
         WeavingContext.getInstance().getConfiguration().addWhitelistPackage("compiler.myPackage");
 
         CompilationResult result = null;
-        try
-        {
-            result = compiler.compile(root, target, loader);
-        }
-        catch (CompilationException e)
-        {
-            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
-        }
+
+        result = compiler.compile(root, target, loader);
 
         assertTrue(RESULT_HAS_NO_ERRORS, !result.hasErrors());
 
@@ -141,14 +135,7 @@
         target.deleteOnExit();
 
         CompilationResult result = null;
-        try
-        {
-            result = compiler.compile(root, target, loader);
-        }
-        catch (CompilationException e)
-        {
-            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
-        }
+        result = compiler.compile(root, target, loader);
 
         assertTrue(RESULT_HAS_NO_ERRORS, !result.hasErrors());