https://issues.apache.org/jira/browse/EXTSCRIPT-143

fixup for the refactoring bugs introduced by changing the central watchdog datastructure to something closer to bernhards code
also we now move the trunk over to the latest 2.0.2-snapshot for testing reasons

git-svn-id: https://svn.apache.org/repos/asf/myfaces/extensions/scripting/trunk@996518 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/api/Configuration.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/api/Configuration.java
index 3af7f33..2637fac 100644
--- a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/api/Configuration.java
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/api/Configuration.java
@@ -144,6 +144,29 @@
         dirs.add(sourceDir);
     }
 
+    /**
+     * <p>Resolves and returns a File handle that represents the class file of
+     * the given class on the file system. However, note that this method only
+     * returns <code>null</code> if an error occured while resolving the class
+     * file. A non-null valuee doesn't necessarily mean that the class file
+     * actually exists. In oder to check the existence call the according
+     * method on the returned object.</p>
+     *
+     * @param className the name of the class that you want to resolve
+     * @return a File handle that represents the class file of the given class
+     *         on the file system
+     * @see java.io.File#exists()
+     */
+    public File resolveClassFile(String className) {
+        //if(className.contains("rg/apache/myfaces/javaloader/componentTest/JavaTestComponent$PropertyKeys")) {
+        //    System.out.println("Debuginfo found");
+        //}
+        //int subClassPos = className.indexOf("$");
+        //className = (subClassPos != -1)? className.substring(0, subClassPos) :className;
+        return new File(getCompileTarget(),
+                className.replaceAll("\\.", "/").concat(".class"));
+    }
+
     public File getCompileTarget() {
         return _compileTarget;
     }
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/api/MyFacesBeanHandler.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/api/MyFacesBeanHandler.java
index 41da284..654b4cd 100644
--- a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/api/MyFacesBeanHandler.java
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/api/MyFacesBeanHandler.java
@@ -22,18 +22,19 @@
 import org.apache.myfaces.config.annotation.LifecycleProvider;
 import org.apache.myfaces.config.annotation.LifecycleProviderFactory;
 import org.apache.myfaces.config.element.ManagedBean;
+import org.apache.myfaces.config.element.ManagedProperty;
 import org.apache.myfaces.extensions.scripting.core.util.ReflectUtil;
 import org.apache.myfaces.extensions.scripting.core.util.WeavingContext;
 import org.apache.myfaces.extensions.scripting.monitor.ClassResource;
 import org.apache.myfaces.extensions.scripting.monitor.RefreshContext;
 import org.apache.myfaces.extensions.scripting.monitor.RefreshAttribute;
+import org.apache.myfaces.util.ContainerUtils;
 
+import javax.el.ELContext;
+import javax.el.ExpressionFactory;
 import javax.faces.context.FacesContext;
 import java.lang.reflect.InvocationTargetException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -76,6 +77,41 @@
     }
 
     /**
+     * scans the dependencies on el level
+     * Not working out for now
+     */
+  /*  public void scanElDependencies() {
+        Map<String, ManagedBean> mbeans = RuntimeConfig.getCurrentInstance(FacesContext.getCurrentInstance().getExternalContext()).getManagedBeans();
+        for(Map.Entry<String, ManagedBean> entry: mbeans.entrySet()) {
+            Object bean = entry.getValue();
+            if(bean instanceof org.apache.myfaces.config.impl.digester.elements.ManagedBean) {
+                org.apache.myfaces.config.impl.digester.elements.ManagedBean workBean = (org.apache.myfaces.config.impl.digester.elements.ManagedBean) bean;
+
+                Object props = ReflectUtil.executeMethod(workBean,"getManagedProperties");
+                if(props instanceof Iterator) {
+                    //myfaces 1.2
+                } else {
+                    for(ManagedProperty prop: ((Collection<ManagedProperty>) props)) {
+                          ExpressionFactory expFactory = FacesContext.getCurrentInstance().getApplication().getExpressionFactory();
+                          ELContext elContext = FacesContext.getCurrentInstance().getELContext();
+                           expFactory.coerceToType("#{myFactory['booga']}");   
+                          //if(ContainerUtils.isValueReference((String) prop.getV))
+                          elContext.getELResolver().getType(elContext,"myFactory","booga");
+                    }
+                }
+
+
+            }
+            //Iterator<ManagedProperty> it = bean.getManagedProperties();
+            //we rescan all managed props to cover pure object
+            //references as well as class references
+            //while(it.hasNext()) {
+            //    ManagedProperty prop = it.next();
+            //}
+        }
+    } */
+
+    /**
      * Refreshes all managed beans
      * session, and personal scoped ones
      * <p/>
@@ -92,6 +128,8 @@
 
         Set<String> tainted = getTaintedClasses();
 
+        //scanElDependencies();
+
         if (tainted.size() > 0) {
             //We now have to check if the tainted classes belong to the managed beans
             Set<String> managedBeanClasses = new HashSet<String>();
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/loaders/java/RecompiledClassLoader.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/loaders/java/RecompiledClassLoader.java
index d1ea4dc..1deb1f1 100644
--- a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/loaders/java/RecompiledClassLoader.java
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/loaders/java/RecompiledClassLoader.java
@@ -18,6 +18,9 @@
  */
 package org.apache.myfaces.extensions.scripting.loaders.java;
 
+import org.apache.myfaces.extensions.scripting.core.util.WeavingContext;
+import org.apache.myfaces.extensions.scripting.monitor.ClassResource;
+
 import java.io.InputStream;
 import java.security.AccessController;
 import java.security.PrivilegedActionException;
@@ -111,18 +114,32 @@
     public Class<?> loadClass(String className) throws ClassNotFoundException {
         //check if our class exists in the tempDir
         final ClassLoader _parent = getParent();
+        if(className.contains("JavaTestComponent")) {
+            System.out.println("Debugpoint found");
+        }
+        ClassResource resource = WeavingContext.getFileChangedDaemon().getClassMap().get(className);
+        
+        //preemptive check if the resource either is not loaded or requires a refresh
+        //if yes we generated a new classloader to load the class anew
+        if(resource == null || resource.getAClass() == null || resource.isRecompiled())  {
+            return _loadClass(className, _parent);
+        }
+        return resource.getAClass();
+    }
+
+    private Class<?> _loadClass(String className, ClassLoader _parent) throws ClassNotFoundException {
         try {
             _throwAwayLoader = AccessController.doPrivileged(
                     new _Action(_parent, _scriptingEngine, _engineExtension, _unTaintClasses)
             );
-            //_throwAwayLoader.setSourceRoot(getSourceRoot());
             return _throwAwayLoader.loadClass(className);
         } catch (PrivilegedActionException e) {
             logSevere(e);
+            return null;
         }
-        return null;
     }
 
+
     protected Class<?> findClass(String name) throws ClassNotFoundException {
         return _throwAwayLoader.findClassExposed(name);
     }
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/loaders/java/ThrowawayClassloader.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/loaders/java/ThrowawayClassloader.java
index 90fecfe..280d917 100644
--- a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/loaders/java/ThrowawayClassloader.java
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/loaders/java/ThrowawayClassloader.java
@@ -46,7 +46,6 @@
     int _scriptingEngine;
     String _engineExtension;
 
-
     public ThrowawayClassloader(ClassLoader classLoader, int scriptingEngine, String engineExtension) {
         super(classLoader);
 
@@ -89,25 +88,36 @@
     }
 
     @Override
+    /**
+     * load called either if the class is not loaded at all
+     * or if the class has been recompiled (check upfront)
+     */
     public Class<?> loadClass(String className) throws ClassNotFoundException {
         //check if our class exists in the tempDir
 
-        File target = getClassFile(className);
+        if(className.contains("JavaTestComponent")) {
+            System.out.println("Debuginfo found");
+        }
+
+        //TODO handle the $ case which should not revert to a new classloader
+
+        File target = WeavingContext.getConfiguration().resolveClassFile(className);
         if (target.exists()) {
-            _logger.log(Level.FINE,"[EXT-SCRIPTING] target {0} exists", className);
+                       
+            _logger.log(Level.FINE, "[EXT-SCRIPTING] target {0} exists", className);
 
             ClassResource data = WeavingContext.getFileChangedDaemon().getClassMap().get(className);
-            if (data != null && !data.getRefreshAttribute().requiresRefresh()) {
-                _logger.log(Level.INFO,"[EXT-SCRIPTING] data from target {0} found but not tainted yet", className);
 
+            //this check must be present because
+            //the vm recycles old classloaders to load classes a anew
+            //if we dont do it we get an exception
+            if(data != null && !data.isRecompiled()) {
                 return data.getAClass();
             }
-             _logger.log(Level.FINER,"[EXT-SCRIPTING] loading class {0} from filesystem", className);
-
-            FileInputStream iStream = null;
-
+            //a load must happen anyway because the target was recompiled
             int fileLength;
             byte[] fileContent;
+            FileInputStream iStream = null;
             try {
                 //we cannot load while a compile is in progress
                 //we have to wait until it is one
@@ -119,23 +129,6 @@
                     _logger.log(Level.FINER, "read {0} bytes", String.valueOf(result));
                 }
 
-                Class retVal;
-
-                //we have to do it here because just in case
-                //a dependent class is loaded as well we run into classcast exceptions
-                if (data != null) {
-                    data.getRefreshAttribute().executedRefresh();
-
-                    retVal = super.defineClass(className, fileContent, 0, fileLength);
-
-                    data.setAClass(retVal);
-                    return retVal;
-                } else {
-                    //we store the initial reloading meta data information so that it is refreshed
-                    //later on, this we we cover dependent classes on the initial load
-                    return storeReloadableDefinitions(className, fileLength, fileContent);
-                }
-
             } catch (FileNotFoundException e) {
                 throw new ClassNotFoundException(e.toString());
             } catch (IOException e) {
@@ -150,8 +143,32 @@
                     }
                 }
             }
+
+            if (data != null) {
+                File sourceFile = data.getFile();
+
+                _logger.log(Level.FINER, "[EXT-SCRIPTING] loading class {0} from filesystem", className);
+
+                Class retVal;
+
+                //we have to do it here because just in case
+                //a dependent class is loaded as well we run into classcast exceptions
+                //we only store the class the weaver has to trigger the refresh time trigger
+                retVal = super.defineClass(className, fileContent, 0, fileLength);
+                data.setAClass(retVal);
+                data.executeLastLoaded();
+                return retVal;
+
+            } else {
+                //we store the initial reloading meta data information so that it is refreshed
+                //later on, this we we cover dependent classes on the initial load
+                if(className.contains("JavaTestComponen")) {
+                    System.out.println("Debuginfo found");
+                }
+                return storeReloadableDefinitions(className, fileLength, fileContent);
+            }
         }
-         _logger.log(Level.FINER,"[EXT-SCRIPTING] target {0} does not exist", target.getAbsolutePath());
+        _logger.log(Level.FINER, "[EXT-SCRIPTING] target {0} does not exist", target.getAbsolutePath());
         return super.loadClass(className);
     }
 
@@ -163,7 +180,10 @@
         //find the source for the given class and then
         //store the filename
         String separator = FileUtils.getFileSeparatorForRegex();
-        String fileName = className.replaceAll("\\.", separator) + getStandardFileExtension();
+        String fileName = className.replaceAll("\\.", separator);
+        fileName = (fileName.indexOf("$") != -1)?  fileName.substring(0,fileName.indexOf("$")): fileName;
+
+        fileName = fileName.replaceAll("\\.", separator) + getStandardFileExtension();
         Collection<String> sourceDirs = WeavingContext.getConfiguration().getSourceDirs(_scriptingEngine);
         String rootDir = null;
         File sourceFile = null;
@@ -182,12 +202,14 @@
             return retVal;
         }
 
-        reloadingMetaData.setFile(new File(rootDir+File.separator+fileName));
+        reloadingMetaData.setFile(new File(rootDir + File.separator + fileName));
         reloadingMetaData.getRefreshAttribute().requestRefresh();
         reloadingMetaData.getRefreshAttribute().executedRefresh();
+
         reloadingMetaData.setScriptingEngine(_scriptingEngine);
 
         WeavingContext.getFileChangedDaemon().getClassMap().put(className, reloadingMetaData);
+         reloadingMetaData.executeLastLoaded();
         return retVal;
     }
 
@@ -202,16 +224,17 @@
     //some classloaders fail to resolve the resource properly, we have
     //to drag our local paths in to keep track of the compiled resources
     //for different scripting languages
-    public URL getResource(String resource) {
-       URL res = super.getResource(resource);
-       if(res != null) return res;
-       //if we do get a null value we try to remap to our custom paths
-       if(!resource.endsWith(".class")) return null;
-       resource = resource.substring(0, resource.length() - 6);
-       resource = resource.replaceAll("\\/",".");
 
-       File clsFile = getClassFile(resource);
-       if(!clsFile.exists()) return null;
+    public URL getResource(String resource) {
+        URL res = super.getResource(resource);
+        if (res != null) return res;
+        //if we do get a null value we try to remap to our custom paths
+        if (!resource.endsWith(".class")) return null;
+        resource = resource.substring(0, resource.length() - 6);
+        resource = resource.replaceAll("\\/", ".");
+
+        File clsFile = WeavingContext.getConfiguration().resolveClassFile(resource);
+        if (!clsFile.exists()) return null;
         try {
             return clsFile.toURI().toURL();
         } catch (MalformedURLException e) {
@@ -220,10 +243,4 @@
         }
     }
 
-
-    public File getClassFile(String className) {
-        return ClassUtils.classNameToFile(WeavingContext.getConfiguration().getCompileTarget().getAbsolutePath(), className);
-    }
-
-   
 }
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/monitor/ClassResource.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/monitor/ClassResource.java
index b07f820..4f5254b 100644
--- a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/monitor/ClassResource.java
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/monitor/ClassResource.java
@@ -24,6 +24,7 @@
 
 import java.io.File;
 import java.util.Collection;
+import java.util.logging.Logger;
 
 /**
  * @author Werner Punz (latest modification by $Author$)
@@ -42,6 +43,7 @@
     //caching the info however probably is faster
     volatile Class _aClass = null;
     volatile File  _sourceFile;
+    volatile long  _lastLoaded = -1l;
     volatile int _scriptingEngine = ScriptingConst.ENGINE_TYPE_JSF_NO_ENGINE;
 
     //todo clean up the sourcepath and filename
@@ -102,6 +104,7 @@
         return _sourceFile.getAbsolutePath().substring(getSourceDir().length()+1);
     }
 
+    
     public String getSourceDir() {
         Collection<String> sourceRoots = WeavingContext.getConfiguration().getSourceDirs(_scriptingEngine);
         String fileDir = _sourceFile.getAbsolutePath();
@@ -115,4 +118,27 @@
         return null;
     }
 
+    public void executeLastLoaded() {
+        _lastLoaded = System.currentTimeMillis();
+    }
+
+    public long getLastLoaded() {
+        return _lastLoaded;
+    }
+
+    /**
+     *
+     * @return true if the class file has been recompiled since the last request for recompilation
+     */
+    public boolean isRecompiled() {
+        File classFile = WeavingContext.getConfiguration().resolveClassFile(_aClass.getName());
+        if(!classFile.exists()) {
+            return false;
+        }
+        Logger log = Logger.getLogger(this.getClass().getName());
+        log.info(this.getAClass().getName() + (classFile.lastModified() - _lastLoaded));
+        return _sourceFile.lastModified() > _lastLoaded;
+    }
+
+
 }
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/CustomChainLoader.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/CustomChainLoader.java
index 3a84933..bfaea46 100644
--- a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/CustomChainLoader.java
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/servlet/CustomChainLoader.java
@@ -66,7 +66,9 @@
         else if (name.startsWith("org.apache") && !name.startsWith("org.apache.myfaces")) {
             return null;
         }
-
+        if(name.contains("JavaTestComponent")){
+            System.out.println("Debugpoint found");    
+        }
         return _scriptingWeaver.loadScriptingClassFromName(name);
     }
 
diff --git a/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/core/classIdentifier/DynamicClassloader.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/core/classIdentifier/DynamicClassloader.java
index b82163d..b2613fa 100644
--- a/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/core/classIdentifier/DynamicClassloader.java
+++ b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/core/classIdentifier/DynamicClassloader.java
@@ -46,6 +46,10 @@
 
     public Class loadClass(String className, boolean resolve) throws ClassNotFoundException {
 
+        if(className.contains("rg/apache/myfaces/javaloader/componentTest/JavaTestComponent$PropertyKeys")) {
+            System.out.println("Debuginfo found");
+        }
+
         if (className.contains(Consts.JAVA_LANG)) {
             return super.loadClass(className, resolve);
         }
diff --git a/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/componentTest/JavaTestRenderer1.java b/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/componentTest/JavaTestRenderer1.java
index 0198789..49bed03 100644
--- a/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/componentTest/JavaTestRenderer1.java
+++ b/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/componentTest/JavaTestRenderer1.java
@@ -41,6 +41,7 @@
  * class to the other
  */
 
+@FacesRenderer(componentFamily = "javax.faces.Input", rendererType = "at.irian.JavaTestRenderer")
     
 public class JavaTestRenderer1 extends HtmlTextRendererBase {
 
diff --git a/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/componentTest/JavaTestRenderer2.java b/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/componentTest/JavaTestRenderer2.java
index d5f1845..3d1607d 100644
--- a/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/componentTest/JavaTestRenderer2.java
+++ b/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/componentTest/JavaTestRenderer2.java
@@ -32,7 +32,6 @@
  * @author Werner Punz (latest modification by $Author$)
  * @version $Revision$ $Date$
  */
-@FacesRenderer(componentFamily = "javax.faces.Input", rendererType = "at.irian.JavaTestRenderer")
 
 public class JavaTestRenderer2 extends HtmlTextareaRendererBase {
 
diff --git a/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/elResolverTest/Receiver.java b/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/elResolverTest/Receiver.java
index 626f285..3124a0b 100644
--- a/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/elResolverTest/Receiver.java
+++ b/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/elResolverTest/Receiver.java
@@ -37,7 +37,7 @@
 
     public Object getMyBean() {
         return myBean;
-    }
+    }         
 
     public void setMyBean(Object myBean) {
         this.myBean = myBean;
diff --git a/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/test/TestBean4.java b/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/test/TestBean4.java
index 8f220f3..7a95524 100644
--- a/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/test/TestBean4.java
+++ b/extscript-examples/myfaces20-example/src/main/webapp/WEB-INF/java/org/apache/myfaces/javaloader/test/TestBean4.java
@@ -26,11 +26,13 @@
 
 public class TestBean4 {
     
-    private String hello = "Hello world from test Bean 4";
+    private String hello = "Hello world from test Bean 4 xxx";
 
+    
     public String getHello() {
         return hello;
     }
+    
 
     public void setHello(String hello) {
         this.hello = hello;
diff --git a/pom.xml b/pom.xml
index fd166d1..af84733 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@
 
     <properties>
         <myfaces12.version>1.2.8</myfaces12.version>
-        <myfaces2.version>2.0.1-SNAPSHOT</myfaces2.version>
+        <myfaces2.version>2.0.2-SNAPSHOT</myfaces2.version>
         <groovy.version>1.7.1</groovy.version>
         <extval.version>2.0.4-SNAPSHOT</extval.version>
     </properties>