https://issues.apache.org/jira/browse/EXTSCRIPT-154 adding testcases
git-svn-id: https://svn.apache.org/repos/asf/myfaces/extensions/scripting/trunk@1300598 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/AnnotationScanListener.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/AnnotationScanListener.java
new file mode 100644
index 0000000..f9891f1
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/AnnotationScanListener.java
@@ -0,0 +1,66 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.api;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * We use a source code artifact observer here to register the
+ * meta data in the correct registry entries
+ */
+
+public interface AnnotationScanListener extends ClassScanListener
+{
+
+ /**
+ * returns true if the annotation marked by the incoming parameter is supported by this scanner
+ *
+ * @param annotation the supported annotation as neutral string representation of its class
+ * @return in case of support
+ */
+ public boolean supportsAnnotation(String annotation);
+
+ /**
+ * returns true if the annotation marked by the incoming parameter is supported by this scanner
+ *
+ * @param annotation the supported annotation as neutral string representation of its class
+ * @return in case of support
+ */
+ public boolean supportsAnnotation(Class annotation);
+
+
+ /**
+ * Class file registration of the supported annotation
+ *
+ * @param clazz the class to be registered
+ * @param annotationName the annotation for the class
+ */
+ public void register(Class clazz, Annotation annotationName);
+
+ /**
+ * Purges the class from the correct places of the myfaces registry
+ * so that the artifact is not reachable anymore
+ *
+ * @param className the class name for the class which needs to be purged
+ */
+ public void purge(String className);
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/ClassScanListener.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/ClassScanListener.java
new file mode 100644
index 0000000..573a32c
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/ClassScanListener.java
@@ -0,0 +1,30 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.api;
+
+/**
+ * Observer interface which will be the base
+ * of a future event system
+ * (note currently unused)
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+public interface ClassScanListener {
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/Configuration.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/Configuration.java
new file mode 100644
index 0000000..a93b26e
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/Configuration.java
@@ -0,0 +1,252 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.api;
+
+import org.apache.myfaces.extensions.scripting.core.common.util.FileUtils;
+import org.apache.myfaces.extensions.scripting.core.engine.FactoryEngines;
+import org.apache.myfaces.extensions.scripting.core.engine.api.ScriptingEngine;
+
+import javax.servlet.ServletContext;
+import java.io.File;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import static org.apache.myfaces.extensions.scripting.core.api.ScriptingConst.*;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class Configuration
+{
+ List<String> _additionalClassPath = new CopyOnWriteArrayList<String>();
+ /**
+ * the package whitelist used by our system
+ * to determine which packages are under control.
+ * <p/>
+ * Note an empty whitelist means, all packages with sourcedirs attached to.
+ */
+ Set<String> _packageWhiteList = new ConcurrentSkipListSet<String>();
+ /**
+ * we keep track of separate resource dirs
+ * for systems which can use resource loaders
+ * <p/>
+ * so that we can load various resources as well
+ * from separate source directories instead
+ */
+ volatile List<String> _resourceDirs = new CopyOnWriteArrayList<String>();
+
+
+
+ String _initialCompile;
+
+
+ /**
+ * the target compile path
+ */
+ volatile File _compileTarget = FileUtils.getTempDir();
+
+ /**
+ * the source dirs per scripting engine
+ */
+ volatile Map<Integer, CopyOnWriteArrayList<String>> _sourceDirs = new ConcurrentHashMap<Integer, CopyOnWriteArrayList<String>>();
+
+ public Configuration(ServletContext context)
+ {
+ init(context);
+ }
+
+ public Configuration()
+ {
+ }
+
+ public void init(ServletContext context)
+ {
+ String packageWhiteList = context.getInitParameter(INIT_PARAM_SCRIPTING_PACKAGE_WHITELIST);
+ packageWhiteList = (packageWhiteList == null)? "": packageWhiteList;
+ _packageWhiteList.addAll(Arrays.asList(packageWhiteList.split("\\,")));
+
+ String additionalClassPath = context.getInitParameter(INIT_PARAM_SCRIPTING_ADDITIONAL_CLASSPATH);
+ additionalClassPath = (additionalClassPath == null)? "": additionalClassPath;
+ String [] additionalClassPaths = additionalClassPath.split("\\,");
+ _additionalClassPath.addAll(Arrays.asList(additionalClassPaths));
+
+ String resourcePath = context.getInitParameter(INIT_PARAM_RESOURCE_PATH);
+ resourcePath = (resourcePath == null)? "": resourcePath;
+ _resourceDirs.addAll(Arrays.asList(resourcePath.split("\\,")));
+
+ _initialCompile = context.getInitParameter(INIT_PARAM_INITIAL_COMPILE);
+ //_additionalClassPath = context.getInitParameter(INIT_PARAM_SCRIPTING_ADDITIONAL_CLASSPATH);
+
+ for(ScriptingEngine engine: FactoryEngines.getInstance().getEngines()) {
+ engine.init(context);
+ }
+ }
+
+ public String getFileEnding(int scriptingEngine)
+ {
+ switch (scriptingEngine)
+ {
+ case ENGINE_TYPE_JSF_JAVA:
+ return JAVA_FILE_ENDING;
+ case ENGINE_TYPE_JSF_GROOVY:
+ return GROOVY_FILE_ENDING;
+ default:
+ throw new UnsupportedOperationException("Engine type unknown");
+ }
+ }
+
+ public Collection<String> getSourceDirs(int scriptingEngine)
+ {
+ return WeavingContext.getInstance().getEngine(scriptingEngine).getSourcePaths();
+ }
+
+ public Collection<String> getAllSourceDirs()
+ {
+ List<String> result = new LinkedList<String>();
+ Collection<ScriptingEngine> engines = WeavingContext.getInstance().getEngines();
+ for (ScriptingEngine engine : engines)
+ {
+ result.addAll(engine.getSourcePaths());
+ }
+ return result;
+ }
+
+ /**
+ * returns a set of whitelisted subdirs hosting the source
+ *
+ * @param scriptingEngine the scripting engine for which the dirs have to be determined
+ * (note every scripting engine has a unique integer value)
+ * @return the current whitelisted dirs hosting the sources
+ */
+ public Collection<String> getWhitelistedSourceDirs(int scriptingEngine) {
+ Collection<String> origSourceDirs = getSourceDirs(scriptingEngine);
+ if (_packageWhiteList.isEmpty()) {
+ return origSourceDirs;
+ }
+
+ return mergeWhitelisted(origSourceDirs);
+ }
+
+ /**
+ * merges the whitelisted packages with the sourcedirs and generates a final list
+ * which left join of both sets - the ones which do not exist in reality
+ *
+ * @param origSourceDirs the original source dirs
+ * @return the joined existing subset of all directories which exist
+ */
+ private Collection<String> mergeWhitelisted(Collection<String> origSourceDirs) {
+ List<String> retVal = new ArrayList<String>(_packageWhiteList.size() * origSourceDirs.size() + origSourceDirs.size());
+
+ for (String whitelisted : _packageWhiteList) {
+ whitelisted = whitelisted.replaceAll("\\.", FileUtils.getFileSeparatorForRegex());
+ for (String sourceDir : origSourceDirs) {
+ String newSourceDir = sourceDir + File.separator + whitelisted;
+ if ((new File(newSourceDir)).exists()) {
+ retVal.add(newSourceDir);
+ }
+ }
+ }
+ return retVal;
+ }
+
+
+ //----------------------- standard setter and getter --------------------------------------
+ /**
+ * Add a new source dir for the corresponding scripting engine
+ *
+ * @param scriptingEngine integer value marking the corresponding engine
+ * @param sourceDir the source directory added to the existing source dir list
+ */
+ public void addSourceDir(int scriptingEngine, String sourceDir)
+ {
+ if(!WeavingContext.getInstance().getEngine(scriptingEngine).getSourcePaths().contains(sourceDir))
+ WeavingContext.getInstance().getEngine(scriptingEngine).getSourcePaths().add(sourceDir);
+ }
+
+
+ public List<String> getAdditionalClassPath()
+ {
+ return _additionalClassPath;
+ }
+
+ public void setAdditionalClassPath(List<String> additionalClassPath)
+ {
+ _additionalClassPath = additionalClassPath;
+ }
+
+ public void addAdditionalClassPath(String additionalClassPath) {
+ _additionalClassPath.add(additionalClassPath);
+ }
+
+ public Set<String> getPackageWhiteList()
+ {
+ return _packageWhiteList;
+ }
+
+ public void setPackageWhiteList(Set<String> packageWhiteList)
+ {
+ _packageWhiteList = packageWhiteList;
+ }
+
+ public void addWhitelistPackage(String pckg) {
+ _packageWhiteList.add(pckg);
+ }
+
+ public List<String> getResourceDirs()
+ {
+ return _resourceDirs;
+ }
+
+ public void setResourceDirs(List<String> resourceDirs)
+ {
+ _resourceDirs = resourceDirs;
+ }
+
+ public void addResourceDir(String resourceDir) {
+ _resourceDirs.add(resourceDir);
+ }
+
+ public String getInitialCompile()
+ {
+ return _initialCompile;
+ }
+
+ public void setInitialCompile(String initialCompile)
+ {
+ _initialCompile = initialCompile;
+ }
+
+ public File getCompileTarget()
+ {
+ return _compileTarget;
+ }
+
+ public void setCompileTarget(File compileTarget)
+ {
+ _compileTarget = compileTarget;
+ }
+
+
+}
+
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/Decorated.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/Decorated.java
new file mode 100644
index 0000000..923a0a1
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/Decorated.java
@@ -0,0 +1,30 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.api;
+
+/**
+ * General decoration interface
+ * for the Decorator pattern
+ *
+ * @author Werner Punz
+ */
+public interface Decorated
+{
+ public Object getDelegate();
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/ImplementationSPI.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/ImplementationSPI.java
new file mode 100644
index 0000000..0a4a98d
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/ImplementationSPI.java
@@ -0,0 +1,36 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.api;
+
+import javax.servlet.ServletContext;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * SPI Interface which ever implementation
+ * connector has to implement
+ * This spi is mirrored into the WeavingContext
+ */
+public interface ImplementationSPI
+{
+ public void registerClassloadingExtension(ServletContext context);
+ public Class forName(String clazz);
+ public void refreshManagedBeans();
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/ReloadingStrategy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/ReloadingStrategy.java
new file mode 100644
index 0000000..a45a38a
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/ReloadingStrategy.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.api;
+
+
+/**
+ * <p/>
+ * Generic strategy for reloading
+ * this should encapsulate various
+ * reloading strategies
+ * which have to be applied depending
+ * on the artifact
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+public interface ReloadingStrategy {
+ /**
+ * Reload method which is the central point for this
+ * strategy pattern
+ *
+ * @param toReload the object to be reloaded
+ * @param artifactType the artifact type to be reloaded (so that the pattern either can ignore it or use it)
+ * @return either the original or the reloaded artifact depending on its type and state
+ */
+ public Object reload(Object toReload, int artifactType);
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/ScriptingConst.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/ScriptingConst.java
new file mode 100644
index 0000000..4331ba5
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/ScriptingConst.java
@@ -0,0 +1,120 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.api;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ *
+ * common constants shared amont the scriping configuration
+ */
+
+public class ScriptingConst
+{
+
+ public static final String SCRIPTING_CLASSLOADER = "org.apache.myfaces.extensions.SCRIPTING_CLASSLOADER";
+ public static final String SCRIPTING_GROOVFACTORY = "org.apache.myfaces.extensions.SCRIPTING_GROOVYFACTORY";
+ public static final String SCRIPTING_REQUSINGLETON = "org.apache.myfaces.extensions.SCRIPTING_REQUSINGLETON";
+
+ public static final String INIT_PARAM_SCRIPTING_PACKAGE_WHITELIST = "org.apache.myfaces.extensions.scripting.PGK_WHITELIST";
+ public static final String INIT_PARAM_SCRIPTING_ADDITIONAL_CLASSPATH = "org.apache.myfaces.extensions.scripting.ADDITIONAL_CLASSPATH";
+ public static final String INIT_PARAM_RESOURCE_PATH = "org.apache.myfaces.extensions.scripting.resources.LOADER_PATHS";
+ public static final String INIT_PARAM_CUSTOM_GROOVY_LOADER_PATHS = "org.apache.myfaces.extensions.scripting.groovy.LOADER_PATHS";
+ public static final String INIT_PARAM_CUSTOM_JAVA_LOADER_PATHS = "org.apache.myfaces.extensions.scripting.java.LOADER_PATHS";
+ public static final String INIT_PARAM_INITIAL_COMPILE="org.apache.myfaces.extensions.scripting.INITIAL_COMPILE_AND_SCAN";
+ public static final String INIT_PARAM_MYFACES_PLUGIN = "org.apache.myfaces.FACES_INIT_PLUGINS";
+
+ public static final String EXT_VAL_MARKER="org.apache.myfaces.extensions.validator";
+
+ public static final String CONTEXT_VALUE_DIVIDER = ",";
+
+ public static final String RELOAD_MAP = "reloadMap";
+ public static final String SESS_BEAN_REFRESH_TIMER = "sessbeanrefrsh";
+
+ public static final int TAINT_INTERVAL = 2000;
+
+ public static final int ENGINE_TYPE_JSF_ALL = -2;
+ public static final int ENGINE_TYPE_JSF_NO_ENGINE = -1;
+ public static final int ENGINE_TYPE_JSF_GROOVY = 0;
+ public static final int ENGINE_TYPE_JSF_JAVA = 1;
+ public static final int ARTIFACT_TYPE_UNKNOWN = -1;
+ public static final int ARTIFACT_TYPE_MANAGEDBEAN = 1;
+ public static final int ARTIFACT_TYPE_MANAGEDPROPERTY = 2;
+ public static final int ARTIFACT_TYPE_RENDERKIT = 3;
+ public static final int ARTIFACT_TYPE_VIEWHANDLER = 4;
+ public static final int ARTIFACT_TYPE_RENDERER = 5;
+ public static final int ARTIFACT_TYPE_COMPONENT = 6;
+ public static final int ARTIFACT_TYPE_VALIDATOR = 7;
+ public static final int ARTIFACT_TYPE_BEHAVIOR = 8;
+ public static final int ARTIFACT_TYPE_APPLICATION = 9;
+ public static final int ARTIFACT_TYPE_ELCONTEXTLISTENER = 10;
+ public static final int ARTIFACT_TYPE_ACTIONLISTENER = 11;
+ public static final int ARTIFACT_TYPE_VALUECHANGELISTENER = 12;
+ public static final int ARTIFACT_TYPE_CONVERTER = 13;
+ public static final int ARTIFACT_TYPE_LIFECYCLE = 14;
+ public static final int ARTIFACT_TYPE_PHASELISTENER = 15;
+ public static final int ARTIFACT_TYPE_FACESCONTEXT = 16;
+ public static final int ARTIFACT_TYPE_NAVIGATIONHANDLER = 17;
+ public static final int ARTIFACT_TYPE_RESPONSEWRITER = 18;
+ public static final int ARTIFACT_TYPE_RESPONSESTREAM = 19;
+ public static final int ARTIFACT_TYPE_RESOURCEHANDLER = 19;
+ public static final int ARTIFACT_TYPE_CLIENTBEHAVIORRENDERER = 20;
+ public static final int ARTIFACT_TYPE_SYSTEMEVENTLISTENER = 21;
+
+ //faclets artifacts
+ public static final int ARTIFACT_TYPE_TAG_HANDLER = 22;
+ public static final int ARTIFACT_TYPE_COMPONENT_HANDLER = 23;
+ public static final int ARTIFACT_TYPE_VALIDATOR_HANDLER = 24;
+ public static final int ARTIFACT_TYPE_CONVERTER_HANDLER = 25;
+ public static final int ARTIFACT_TYPE_BEHAVIOR_HANDLER = 26;
+
+ public static final String CTX_ATTR_REQUEST_CNT = "RequestCnt";
+ public static final String CTX_ATTR_CONFIGURATION = "ExtScriptingConfig";
+ public static final String CTX_ATTR_STARTUP = "ExtScriptingStartup";
+ public static final String CTX_ATTR_SCRIPTING_WEAVER = "ScriptingWeaver";
+ public static final String CTX_ATTR_REFRESH_CONTEXT = "RefreshContext";
+ public static final String CTX_ATTR_EXTENSION_EVENT_SYSTEM = "ExtEventSystem";
+
+ public static final String FILE_EXTENSION_GROOVY = ".groovy";
+ public static final String GROOVY_FILE_ENDING = ".groovy";
+ public static final String JAVA_FILE_ENDING = ".java";
+ public static final String JSR199_COMPILER = "org.apache.myfaces.extensions.scripting.loaders.java.jsr199.JSR199Compiler";
+ public static final String JAVA5_COMPILER = "org.apache.myfaces.extensions.scripting.loaders.java.compiler.JavacCompiler";
+ public static final String SCOPE_SESSION = "session";
+ public static final String SCOPE_APPLICATION = "application";
+ public static final String SCOPE_REQUEST = "request";
+ public static final String GROOVY_SOURCE_ROOT = "/WEB-INF/groovy/";
+ public static final String JAVA_SOURCE_ROOT = "/WEB-INF/java/";
+ public static final String ERR_SERVLET_FILTER = "[EXT-SCRIPTING] The servlet filter has not been set, please check your web.xml for following entries:" +
+ "\n <filter>\n" +
+ " <filter-name>scriptingFilter</filter-name>\n" +
+ " <filter-class>org.apache.myfaces.extensions.scripting.servlet.ScriptingServletFilter</filter-class>\n" +
+ " </filter>\n" +
+ " <filter-mapping>\n" +
+ " <filter-name>scriptingFilter</filter-name>\n" +
+ " <url-pattern>/*</url-pattern>\n" +
+ " <dispatcher>REQUEST</dispatcher>\n" +
+ " <dispatcher>FORWARD</dispatcher>\n" +
+ " <dispatcher>INCLUDE</dispatcher>\n" +
+ " <dispatcher>ERROR</dispatcher>\n" +
+ " </filter-mapping>";
+ static final String EXT_VAL_REQ_KEY = "org.apache.myfaces.extension.scripting.clearExtvalCache_Done";
+ public static final String JAVAX_FACES = "javax.faces";
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/WeavingContext.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/WeavingContext.java
new file mode 100644
index 0000000..f1e829c
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/api/WeavingContext.java
@@ -0,0 +1,646 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.api;
+
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassUtils;
+import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
+import org.apache.myfaces.extensions.scripting.core.engine.FactoryEngines;
+import org.apache.myfaces.extensions.scripting.core.engine.api.ClassScanner;
+import org.apache.myfaces.extensions.scripting.core.engine.api.CompilationResult;
+import org.apache.myfaces.extensions.scripting.core.engine.api.ScriptingEngine;
+import org.apache.myfaces.extensions.scripting.core.loader.ThrowAwayClassloader;
+import org.apache.myfaces.extensions.scripting.core.monitor.ClassResource;
+import org.apache.myfaces.extensions.scripting.core.monitor.WatchedResource;
+import org.apache.myfaces.extensions.scripting.core.reloading.GlobalReloadingStrategy;
+import org.apache.myfaces.extensions.scripting.core.reloading.MethodLevelReloadingHandler;
+import org.apache.myfaces.extensions.scripting.jsf.adapters.MyFacesSPI;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Logger;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * Central weaving context
+ */
+
+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;
+
+ /**
+ * true only if the startup has performed without errors
+ */
+ boolean _scriptingEnabled = true;
+
+ /**
+ * 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 <class, taint> to a three dimensional <class, taint, time>
+ * 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();
+ initScanner();
+ }
+
+ public void initScanner()
+ {
+ try
+ {
+ Class scanner = ClassUtils.getContextClassLoader().loadClass("org.apache.myfaces.extensions.scripting.jsf.annotation.GenericAnnotationScanner");
+ this._annotationScanner = (ClassScanner) ReflectUtil.instantiate(scanner);
+
+ }
+ catch (ClassNotFoundException e)
+ {
+ //we do nothing here
+ //generic annotation scanner can be missing in jsf 1.2 environments
+ //_logger.log(Level.FINER, "", e);
+ }
+ }
+
+ public Collection<ScriptingEngine> getEngines()
+ {
+ return FactoryEngines.getInstance().getEngines();
+ }
+
+ public ScriptingEngine getEngine(int engineType)
+ {
+ return FactoryEngines.getInstance().getEngine(engineType);
+ }
+
+ /**
+ * returns the mitable watche resource maps for the various engines
+ *
+ * @return
+ */
+ public Map<Integer, Map<String, ClassResource>> getWatchedResources()
+ {
+ Map<Integer, Map<String, ClassResource>> ret = new HashMap<Integer, Map<String, ClassResource>>();
+ for (ScriptingEngine engine : getEngines())
+ {
+ ret.put(engine.getEngineType(), engine.getWatchedResources());
+ }
+ 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>();
+ for (ScriptingEngine engine : getEngines())
+ {
+ Map<String, ClassResource> watchedResourceMap = engine.getWatchedResources();
+ for (Map.Entry<String, ClassResource> entry : watchedResourceMap.entrySet())
+ {
+ ret.put(entry.getKey(), entry.getValue());
+ }
+ }
+ 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())
+ {
+ if (!engine.getWatchedResources().containsKey(key)) continue;
+ return engine.getWatchedResources().get(key);
+ }
+ return null;
+ }
+
+ public Collection<ClassResource> getTaintedClasses(int scriptingEngine) {
+ Map<String, ClassResource> watchedResources = getEngine(scriptingEngine).getWatchedResources();
+ List<ClassResource> res = new LinkedList<ClassResource>();
+ for(Map.Entry<String, ClassResource> entry: watchedResources.entrySet()) {
+ if(entry.getValue().isTainted()) {
+ res.add(entry.getValue());
+ }
+ }
+ return res;
+ }
+
+
+ public Collection<ClassResource> getTaintedClasses() {
+ Map<String, ClassResource> watchedResources = getAllWatchedResources();
+ List<ClassResource> res = new LinkedList<ClassResource>();
+ for(Map.Entry<String, ClassResource> entry: watchedResources.entrySet()) {
+ if(entry.getValue().isTainted()) {
+ res.add(entry.getValue());
+ }
+ }
+ return res;
+ }
+
+ /**
+ * 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);
+ if (res == null) return false;
+ return res.isTainted();
+ }
+
+ public Set<String> loadPossibleDynamicClasses()
+ {
+ return getAllWatchedResources().keySet();
+ }
+
+ public Configuration getConfiguration()
+ {
+ return configuration;
+ }
+
+ public void setConfiguration(Configuration configuration)
+ {
+ this.configuration = configuration;
+ }
+
+ public boolean needsRecompile()
+ {
+ for (ScriptingEngine engine : getEngines())
+ {
+ //log.info("[EXT-SCRIPTING] scanning " + engine.getEngineType() + " files");
+ if (engine.needsRecompile()) return true;
+ //log.info("[EXT-SCRIPTING] scanning " + engine.getEngineType() + " files done");
+ }
+ return false;
+ }
+
+ public void initialFullScan()
+ {
+ for (ScriptingEngine engine : getEngines())
+ {
+ engine.scanForAddedDeleted();
+ }
+ //the scanner scans only the tainted classes
+ //hence this should work whatever happens
+
+ }
+
+ public void annotationScan()
+ {
+ if (_annotationScanner != null)
+ _annotationScanner.scanPaths();
+ }
+
+ public boolean compile()
+ {
+ boolean compile = false;
+ for (ScriptingEngine engine : getEngines())
+ {
+ if (!engine.needsRecompile()) continue;
+ compile = true;
+ log.info("[EXT-SCRIPTING] compiling " + engine.getEngineTypeAsStr() + " files");
+ CompilationResult result = engine.compile();
+ if(result != null) {
+ WeavingContext.getInstance().setCompilationResult(engine.getEngineType(), result);
+ }
+
+ log.info("[EXT-SCRIPTING] compiling " + engine.getEngineTypeAsStr() + " files done");
+ }
+ return compile;
+ }
+
+ public void scanDependencies()
+ {
+ for (ScriptingEngine engine : getEngines())
+ {
+ if (engine.isTainted())
+ {
+ log.info("[EXT-SCRIPTING] scanning " + engine.getEngineTypeAsStr() + " dependencies");
+ engine.scanDependencies();
+ log.info("[EXT-SCRIPTING] scanning " + engine.getEngineTypeAsStr() + " dependencies end");
+ }
+ }
+ }
+
+ public void markTaintedDependends()
+ {
+ for (ScriptingEngine engine : getEngines())
+ {
+ engine.markTaintedDependencies();
+ }
+ }
+
+ public WatchedResource getResource(String className)
+ {
+ WatchedResource ret = null;
+ for (ScriptingEngine engine : getEngines())
+ {
+ ret = engine.getWatchedResources().get(className);
+ if (ret != null) return ret;
+ }
+ return ret;
+ }
+
+ public boolean isDynamic(Class clazz)
+ {
+ return clazz.getClassLoader() instanceof ThrowAwayClassloader;
+ }
+
+ /**
+ * we create a proxy to an existing object
+ * which does reloading of the internal class
+ * on method level
+ * <p/>
+ * this works only on classes which implement contractual interfaces
+ * it cannot work on things like the navigation handler
+ * which rely on base classes
+ *
+ * @param o the source object to be proxied
+ * @param theInterface the proxying interface
+ * @param artifactType the artifact type to be reloaded
+ * @return a proxied reloading object of type theInterface
+ */
+ public Object createMethodReloadingProxyFromObject(Object o, Class theInterface, int artifactType)
+ {
+ //if (!isScriptingEnabled()) {
+ // return o;
+ //}
+ return Proxy.newProxyInstance(o.getClass().getClassLoader(),
+ new Class[]{theInterface},
+ new MethodLevelReloadingHandler(o, artifactType));
+ }
+
+ /**
+ * reload the class dynamically
+ */
+ public Class reload(Class clazz)
+ {
+ if (!isDynamic(clazz)) return clazz;
+ ClassResource resource = (ClassResource) getResource(clazz.getName());
+ if (resource == null) return clazz;
+ if (resource.isTainted() || resource.getAClass() == null)
+ {
+ clazz = _implementation.forName(clazz.getName());
+ //TODO not needed anymore, done by the forName now
+ resource.setAClass(clazz);
+ }
+ return clazz;
+ }
+
+ public Object reload(Object instance, int strategyType)
+ {
+ return _reloadingStrategy.reload(instance, strategyType);
+ }
+
+ /**
+ * we create a proxy to an existing object
+ * which does reloading of the internal class
+ * on newInstance level
+ *
+ * @param o the original object
+ * @param theInterface the proxy interface
+ * @param artifactType the artifact type to be handled
+ * @return the proxy of the object if scripting is enabled, the original one otherwise
+ */
+ @SuppressWarnings("unused")
+ public static Object createConstructorReloadingProxyFromObject(Object o, Class theInterface, int artifactType)
+ {
+ //if (!isScriptingEnabled()) {
+ // return o;
+ //}
+ return Proxy.newProxyInstance(o.getClass().getClassLoader(),
+ new Class[]{theInterface},
+ new MethodLevelReloadingHandler(o, artifactType));
+ }
+
+ /**
+ * un-mapping of a proxied object
+ *
+ * @param o the proxied object
+ * @return the un-proxied object
+ */
+ public static Object getDelegateFromProxy(Object o)
+ {
+ if (o == null)
+ {
+ return null;
+ }
+ if (o instanceof Decorated)
+ return ((Decorated) o).getDelegate();
+
+ if (!Proxy.isProxyClass(o.getClass())) return o;
+ InvocationHandler handler = Proxy.getInvocationHandler(o);
+ if (handler instanceof Decorated)
+ {
+ return ((Decorated) handler).getDelegate();
+ }
+ return o;
+ }
+
+ public void addDependency(int engineType, String fromClass, String toClass)
+ {
+ //TODO implement this tomorrow
+ }
+
+ public ImplementationSPI getImplementationSPI() {
+ return MyFacesSPI.getInstance();
+ }
+
+ //----------------------------------------------------------------------
+ //lifecycle related tasks
+ public boolean isPostInit()
+ {
+ return (lifecycleRegistry.get("LIFECYCLE_POST_INIT") != null);
+ }
+
+ public void markPostInit()
+ {
+ lifecycleRegistry.put("LIFECYCLE_POST_INIT", System.currentTimeMillis());
+ }
+
+ public void markLastTaint()
+ {
+ 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");
+ lastTainted = (lastTainted != null) ? lastTainted : -1L;
+ 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");
+ lastTainted = (lastTainted != null) ? lastTainted : -1L;
+ 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 <noOfEntries> 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()
+ {
+ return _dependencyMap;
+ }
+
+ public void setDependencyMap(ClassDependencies dependencyMap)
+ {
+ _dependencyMap = dependencyMap;
+ } */
+
+ protected static WeavingContext _instance = new WeavingContext();
+
+ public static WeavingContext getInstance()
+ {
+ return _instance;
+ }
+
+ public ImplementationSPI getImplementation()
+ {
+ return _implementation;
+ }
+
+ public void setImplementation(ImplementationSPI implementation)
+ {
+ _implementation = implementation;
+ }
+
+ public boolean isScriptingEnabled()
+ {
+ return _scriptingEnabled;
+ }
+
+ public void setScriptingEnabled(boolean scriptingEnabled)
+ {
+ _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/org/apache/myfaces/extensions/scripting/core/common/util/Array.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/Array.java
new file mode 100644
index 0000000..7ae1bf0
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/Array.java
@@ -0,0 +1,32 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.common.util;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+@SuppressWarnings("unused")
+public class Array extends Cast
+{
+ public Array(Class clazz, Object... value) {
+
+ super(java.lang.reflect.Array.newInstance(clazz, 0).getClass(), value);
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/Cast.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/Cast.java
new file mode 100644
index 0000000..46dbd83
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/Cast.java
@@ -0,0 +1,42 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.common.util;
+
+/**
+ * Simple casting representation for introspection
+ * calls
+ */
+public class Cast {
+
+ Class clazz;
+ Object value;
+
+ public Cast(Class clazz, Object value) {
+ this.clazz = clazz;
+ this.value = value;
+ }
+
+ public Class getClazz() {
+ return clazz;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/ClassLoaderUtils.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/ClassLoaderUtils.java
new file mode 100644
index 0000000..4111cf9
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/ClassLoaderUtils.java
@@ -0,0 +1,170 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.common.util;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>Utility class for class loading purposes, e.g. to determine the classpath of a
+ * class loader hierarchy.</p>
+ */
+public class ClassLoaderUtils {
+
+
+ // ------------------------------------------ Public methods
+
+ /**
+ * CompilationResult
+ * <p>Returns the default class loader to use.</p>
+ *
+ * @return the default class loader to use
+ */
+ public static ClassLoader getDefaultClassLoader() {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ if (classLoader != null) {
+ return classLoader;
+ } else {
+ return ClassLoaderUtils.class.getClassLoader();
+ }
+ }
+
+ /**
+ * <p>Determines whether the given class is loadable by the given class loader.</p>
+ *
+ * @param className the class you want to check
+ * @param classLoader the class loader to use for that check
+ * @return <code>true</code>, if the given class is loadable by the given class loader
+ */
+ public static boolean isClassAvailable(String className, ClassLoader classLoader) {
+ try {
+ classLoader.loadClass(className);
+ return true;
+ }
+ catch (Throwable ex) {
+ return false;
+ }
+ }
+
+ /**
+ * <p>Resolves the classpath by walking up the hierachy of class loaders. Assuming
+ * that we're only dealing with URLClassLoaders it's possible to determine the
+ * classpath. This method, however, returns the classpath as a String, where each
+ * classpath entry is separated by a ';', i.e. it returns the classpath in a format
+ * that Java tools usually expect it to be.</p>
+ * <p/>
+ * it also adds the additional classpaths issued by our configuration to the list
+ *
+ * @param classLoader the class loader which you want to resolve the class path for
+ * @return the final classpath
+ */
+ public static String buildClasspath(ClassLoader classLoader) {
+ StringBuilder classpath = new StringBuilder();
+
+ URL[] urls = resolveClasspath(classLoader);
+ for (URL url : urls) {
+ classpath.append(url.getPath());
+
+ // Note that the classpath separator character is platform
+ // dependent. On Windows systems it's ";" whereas on other
+ // UNIX systems it's ":".
+ classpath.append(File.pathSeparatorChar);
+ }
+
+ String retVal = classpath.toString();
+ if (retVal.endsWith(File.pathSeparator)) {
+ retVal = retVal.substring(0, retVal.length() - 1);
+ }
+ return retVal;
+ }
+
+ /**
+ * <p>Resolves the classpath by walking up the hierarchy of class loaders. Assuming
+ * that we're only dealing with URLClassLoaders it's possible to determine the
+ * classpath.</p>
+ *
+ * @param parent the class loader which you want to resolve the class path for
+ * @return the final classpath
+ */
+ public static URL[] resolveClasspath(ClassLoader parent) {
+ List<URL> classpath = new ArrayList<URL>();
+
+ ClassLoader classLoader = parent;
+ // Walk up the hierachy of class loaders in order to determine the current classpath.
+ File target = WeavingContext.getInstance().getConfiguration().getCompileTarget();
+ if (target != null) {
+ addFile(classpath, target);
+ }
+
+ while (classLoader != null) {
+ if (classLoader instanceof URLClassLoader) {
+ URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
+
+ URL[] urls = urlClassLoader.getURLs();
+ if (urls != null) {
+ classpath.addAll(Arrays.asList(urls));
+ }
+ } /*else {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Resolving the classpath of the classloader '" + parent + "' - One of its parent class"
+ + " loaders is no URLClassLoader '" + classLoader + "', which means it's possible that"
+ + " some classpath entries aren't in the final outcome of this method call.");
+ }
+ } */
+
+ //we disable this warning entirely for now because our own url classloader
+ //can deal with this properly, due to extra startup context classpath determination
+
+ // Inspect the parent class loader next.
+ classLoader = classLoader.getParent();
+ }
+
+ List<String> additionalClassPaths = WeavingContext.getInstance().getConfiguration().getAdditionalClassPath();
+ if (!(additionalClassPaths == null || additionalClassPaths.isEmpty())) {
+ for (String additionalClassPath : additionalClassPaths) {
+ File additionalPath = new File(additionalClassPath);
+ addFile(classpath, additionalPath);
+ }
+ }
+
+ return classpath.toArray(new URL[classpath.size()]);
+ }
+
+ private static void addFile(List<URL> classpath, File additionalPath) {
+ if (additionalPath.exists()) {
+ try {
+ classpath.add(additionalPath.toURI().toURL());
+ } catch (MalformedURLException e) {
+ Logger log = Logger.getLogger(ClassLoaderUtils.class.getName());
+ log.log(Level.SEVERE, "Additionalclasspath wrong url", e);
+ }
+ }
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/ClassUtils.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/ClassUtils.java
new file mode 100644
index 0000000..7cc6c5a
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/ClassUtils.java
@@ -0,0 +1,119 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.common.util;
+
+//TODO this needs to be moved into the JSF 2.0 and 1.2 packages
+//reason due to the changes caused by the shade plugin it the ClassLoader
+//Extensions is in shared_impl in 1.2 and in shared in 2.x
+
+import java.io.File;
+
+/**
+ * A generic utils class dealing with different aspects
+ * (naming and reflection) of java classes
+ *
+ * @author werpu
+ * <p/>
+ */
+public class ClassUtils
+{
+
+ public static Class forName(String name)
+ {
+ try
+ {
+ return Class.forName(name);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static boolean isPresent(String clazz)
+ {
+ try
+ {
+ getContextClassLoader().loadClass(clazz);
+ }
+ catch (ClassNotFoundException e)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public static File classNameToFile(String classPath, String className)
+ {
+ String classFileName = classNameToRelativeFileName(className);
+ return new File(classPath + File.separator + classFileName);
+ }
+
+ private static String classNameToRelativeFileName(String className)
+ {
+ String separator = FileUtils.getFileSeparatorForRegex();
+
+ return className.replaceAll("\\.", separator) + ".class";
+ }
+
+ public static String relativeFileToClassName(String relativeFileName)
+ {
+ String className = relativeFileName.replaceAll("\\\\", ".").replaceAll("\\/", ".");
+ className = className.substring(0, className.lastIndexOf("."));
+ if(className.startsWith(".")) {
+ className = className.substring(1);
+ }
+ return className;
+ }
+
+ public static void addClassLoadingExtension(Object extension, boolean top)
+ {
+ try
+ {
+ ReflectUtil.executeStaticMethod(forName("org.apache.myfaces.shared_impl.util.ClassUtils"),
+ "addClassLoadingExtension");
+ }
+ catch (Exception e)
+ {
+ ReflectUtil.executeStaticMethod(forName("org.apache.myfaces.shared.util.ClassUtils"),
+ "addClassLoadingExtension", extension, top);
+ }
+
+ //ClassUtils.addClassLoadingExtension(extension, top);
+ }
+
+ public static ClassLoader getContextClassLoader()
+ {
+ try
+ {
+ return (ClassLoader) ReflectUtil.executeStaticMethod(forName("org.apache.myfaces.shared_impl.util.ClassUtils"),
+ "getContextClassLoader");
+ }
+ catch (Exception e)
+ {
+ return (ClassLoader) ReflectUtil.executeStaticMethod(forName("org.apache.myfaces.shared.util.ClassUtils"),
+ "getContextClassLoader");
+ }
+
+ //return (ClassLoader) ReflectUtil.executeStaticMethod(forName("org.apache.myfaces.extensions.scripting.util" +
+ // ".ClassUtils"),
+ // "getContextClassLoader");
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/DirStrategy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/DirStrategy.java
new file mode 100644
index 0000000..c3e734d
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/DirStrategy.java
@@ -0,0 +1,49 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.common.util;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class DirStrategy implements Strategy
+{
+ List<File> _foundFiles = new LinkedList<File>();
+
+ public void apply(Object element) {
+ File foundFile = (File) element;
+ if (foundFile.isDirectory()) {
+ _foundFiles.add(foundFile);
+ }
+ }
+
+ public List<File> getFoundFiles() {
+ return _foundFiles;
+ }
+
+ public void setFoundFiles(List<File> foundFiles) {
+ _foundFiles = foundFiles;
+ }
+}
+
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/FileStrategy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/FileStrategy.java
new file mode 100644
index 0000000..324463a
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/FileStrategy.java
@@ -0,0 +1,71 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.common.util;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * Java file strategy pattern to filter out all java files which are possible sources
+ * so that we can recompile them
+ */
+
+public class FileStrategy implements Strategy
+{
+ Pattern _rePattern;
+
+ public FileStrategy(String pattern) {
+ pattern = pattern.trim().replaceAll("\\.", "\\\\.");
+ pattern = "." + pattern;
+
+ _rePattern = Pattern.compile(pattern);
+
+ }
+
+ public FileStrategy(Pattern pattern) {
+
+
+ _rePattern = pattern;
+
+ }
+
+
+ List<File> _foundFiles = new LinkedList<File>();
+
+ public void apply(Object element) {
+ File foundFile = (File) element;
+ String fileName = foundFile.getName().toLowerCase(Locale.getDefault());
+ Matcher matcher = _rePattern.matcher(fileName);
+
+ if (!matcher.matches()) return;
+ _foundFiles.add(foundFile);
+ }
+
+ public List<File> getFoundFiles() {
+ return _foundFiles;
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/FileUtils.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/FileUtils.java
new file mode 100644
index 0000000..0a7d6db
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/FileUtils.java
@@ -0,0 +1,201 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.common.util;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class FileUtils {
+ static double _tempMarker = Math.random();
+
+ /**
+ * Get the file separator for this platform.
+ *
+ * @return The file separator.
+ */
+ public static String getFileSeparator() {
+ return File.separator;
+ }
+
+ /**
+ * touch functionality to mark
+ * compiles and dependencies as changed
+ * to get a clean reloading state
+ * <p/>
+ * the touch is basically just the same as unix touch
+ *
+ * @param fileToTouch
+ */
+ public static void touch(File fileToTouch) {
+ //we change our lastMofied to the current system time
+ if (!fileToTouch.exists()) return;
+ fileToTouch.setLastModified(System.currentTimeMillis());
+ }
+
+ /**
+ * Get the file separator for this platform, properly escaped for usage in a regular expression.
+ * workaround for http://bugs.sun.com/view_bug.do?bug_id=4626653 another workaround would be
+ * to use the Matcher.quoteReplacement as of http://bugs.sun.com/view_bug.do?bug_id=5024613 instead
+ * of using String.replaceAll
+ *
+ * @return The file separator, escaped for in a regex.
+ */
+ public static String getFileSeparatorForRegex() {
+ String sep = getFileSeparator();
+
+ if ("\\".equals(sep)) {
+ sep = "\\\\";
+ }
+
+ return sep;
+ }
+
+ public static File getTempDir() {
+ File tempDir;
+
+ String baseTempPath = System.getProperty("java.io.tmpdir");
+ String tempDirName = "myfaces_compilation_" + _tempMarker;
+
+ tempDir = new File(baseTempPath + File.separator + tempDirName);
+ /*while (tempDir.exists()) {
+ tempDirName = "myfaces_compilation_" + System.currentTimeMillis() + Math.random();
+ tempDir = new File(baseTempPath + File.separator + tempDirName);
+ }
+
+ synchronized (FileUtils.class) {
+ if (tempDir.exists()) {
+ return tempDir;
+ }
+ if (tempDir.mkdirs()) {
+ tempDir.deleteOnExit();
+ }
+ } */
+ return tempDir;
+ }
+
+ /**
+ * we roll our own tree walker here
+ * to avoid a dependency into commons fileutils
+ * and to apply an easier pattern than
+ * commons fileutils uses
+ *
+ * @param rootDir the root dir for our walking
+ * @param strategy the strategy to apply to for our walking
+ */
+ public static void listFiles(File rootDir, Strategy strategy) {
+ if (!rootDir.isDirectory()) {
+ strategy.apply(rootDir);
+ return;
+ }
+
+ File[] files = rootDir.listFiles();
+ for (File file : files) {
+ boolean isDirectory = file.isDirectory();
+ if (isDirectory && !file.getName().endsWith(".")) {
+ listFiles(file, strategy);
+ strategy.apply(file);
+ } else if (!isDirectory) {
+ strategy.apply(file);
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * target path check to check if the targetPath is valid or can be created
+ * </p>
+ *
+ * @param path the path to be investigated
+ */
+ public static void assertPath(File path) {
+ // The destination directory must already exist as javac will not create the destination directory.
+ if (!path.exists()) {
+ if (!path.mkdirs()) {
+ throw new IllegalStateException("It wasn't possible to create the target " +
+ "directory for the compiler ['" + path.getAbsolutePath() + "'].");
+ }
+
+ // If we've created the destination directory, we'll delete it as well once the application exits
+ path.deleteOnExit();
+ }
+ }
+
+ /**
+ * fetches recursively the files under the current root
+ *
+ * @param sourcePath the source path from which the walker should start from
+ * @param fileType the pattern upon which the file has to be matched to aka *.java etc...
+ * @return a list of source files
+ */
+ public static List<File> fetchSourceFiles(File sourcePath, String fileType) {
+ FileStrategy strategy = new FileStrategy(fileType);
+ listFiles(sourcePath, strategy);
+
+ return strategy.getFoundFiles();
+ }
+
+ /**
+ * fetches the source files from a list of source paths
+ *
+ * @param sourcePaths the collection of paths to be searched for
+ * @param fileType the filetype to be searched for
+ * @return a list of files found
+ */
+ public static List<File> fetchSourceFiles(Collection<String> sourcePaths, String fileType) {
+ FileStrategy strategy = new FileStrategy(fileType);
+
+ for (String sourcePath : sourcePaths) {
+ File fSourcePath = new File(sourcePath);
+ if (fSourcePath.exists()) {
+ listFiles(fSourcePath, strategy);
+ }
+ }
+
+ return strategy.getFoundFiles();
+ }
+
+ /**
+ * fetches the source paths from a given root directory in the format
+ * <path>/<appendix>;...
+ *
+ * @param sourcePath the sourcePath from which the directory traversal should happen from
+ * @param appendix the appendix which has to be appended to every path found
+ * @return a string builder of the paths found
+ */
+ @SuppressWarnings("unused")
+ public static StringBuilder fetchSourcePaths(File sourcePath, String appendix) {
+ DirStrategy dirStrategy = new DirStrategy();
+ StringBuilder sourcesList = new StringBuilder(512);
+
+ listFiles(sourcePath, dirStrategy);
+ for (File foundDir : dirStrategy.getFoundFiles()) {
+ String dirName = foundDir.getAbsolutePath();
+ sourcesList.append(dirName);
+ sourcesList.append(File.separator);
+ sourcesList.append(appendix);
+ }
+ return sourcesList;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/Null.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/Null.java
new file mode 100644
index 0000000..888819a
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/Null.java
@@ -0,0 +1,32 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.common.util;
+
+/**
+ * @Author Werner Punz
+ * Null representation for easier introspection calls
+ */
+public class Null extends Cast
+{
+
+ public Null(Class clazz) {
+ super(clazz, null);
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/ReflectUtil.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/ReflectUtil.java
new file mode 100644
index 0000000..e61febd
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/ReflectUtil.java
@@ -0,0 +1,432 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.common.util;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ReflectUtil {
+
+ static final Logger _logger = Logger.getLogger(ReflectUtil.class.getName());
+
+ public static Object instantiate(String clazz, Object... varargs) {
+ return instantiate(ClassUtils.forName(clazz), varargs);
+ }
+
+ /**
+ * A simplified instantiation over reflection
+ *
+ * @param clazz the class to be instantiated
+ * @param varargs the instantiation parameters
+ * @return the instantiated object
+ */
+ public static Object instantiate(Class clazz, Object... varargs) {
+ Class[] classes = new Class[varargs.length];
+ for (int cnt = 0; cnt < varargs.length; cnt++) {
+
+ if (varargs[cnt] instanceof Cast) {
+ classes[cnt] = ((Cast) varargs[cnt]).getClazz();
+ varargs[cnt] = ((Cast) varargs[cnt]).getValue();
+ } else {
+ classes[cnt] = varargs[cnt].getClass();
+ }
+ }
+
+ try {
+ Constructor constr = clazz.getConstructor(classes);
+ return constr.newInstance(varargs);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ }
+ }/*this is mostly just a helper to bypass a groovy bug in a more
+ * complex delegation environment. Groovy throws a classcast
+ * exception wrongly, delegating the instantiation code to java
+ * fixes that
+ * */
+
+ public static Object newObject(Class clazz) throws IllegalAccessException, InstantiationException {
+ return clazz.newInstance();
+ }
+
+ /**
+ * Generic execute method which simplifies the reflection api
+ * down to a usable system
+ *
+ * @param obj the target object the method has to be executed upon
+ * @param methodName the method name
+ * @param varargs the arguments which have to be passed to the method
+ * @return the return value of the method
+ */
+ public static Object executeStaticMethod(Class obj, String methodName, Object... varargs) {
+
+ Collection<Method> methods = getMethods(obj, methodName, varargs.length);
+
+ Object retVal = handleStaticMethod(obj, methodName, methods, varargs);
+ if (!methodNotFound(retVal)) {
+ return retVal;
+ }
+
+ methods = getAllMethods(obj, methodName, varargs.length);
+ retVal = handleStaticMethod(obj, methodName, methods, varargs);
+ if (!methodNotFound(retVal)) {
+ return retVal;
+ }
+
+ throw new RuntimeException("Static Method of :" + methodName + " from class " + obj.getClass().getName() + " not found");
+
+ }
+
+ public static Collection<Method> getAllMethods(Class clazz, String methodName, int varargLength) {
+ ArrayList<Method> retVal = new ArrayList<Method>(30);
+ while (clazz != null) {
+ for (Method m : clazz.getDeclaredMethods()) {
+ if (m.getParameterTypes().length == varargLength && m.getName().equals(methodName)) {
+ retVal.add(m);
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+
+ return retVal;
+ }
+
+ public static Collection<Method> getMethods(Class clazz, String methodName, int varargLength) {
+ ArrayList<Method> retVal = new ArrayList<Method>(30);
+ for (Method m : clazz.getDeclaredMethods()) {
+ if (m.getParameterTypes().length == varargLength && m.getName().equals(methodName)) {
+ retVal.add(m);
+ }
+ }
+
+ return retVal;
+ }
+
+ /**
+ * Generic execute method which simplifies the reflection api
+ * down to a usable system
+ *
+ * @param obj the target object the method has to be executed upon
+ * @param methodName the method name
+ * @param varargs the arguments which have to be passed to the method
+ * @return the return value of the method
+ * @throws RuntimeException a generic runtime exception in case of a failure
+ * we use unmanaged exceptions here to get a behavior similar to scripting
+ * language execution where failures can happen but method executions
+ * should not enforce exception handling
+ */
+ public static Object executeMethod(Object obj, String methodName, Object... varargs) {
+
+ Collection<Method> methods;
+ //if we have an invocationHandler here we
+ //can work over the generic invoke interface
+ //That way we can cover more dynamic stuff
+ //our reload invocation handler is treated differently here
+
+ if (obj instanceof InvocationHandler) {
+ InvocationHandler objToInvoke = (InvocationHandler) obj;
+
+ Object realTarget = WeavingContext.getInstance().getDelegateFromProxy(objToInvoke);
+
+ //first we try only the public because they are the most likely ones
+ //to be accessed
+ methods = getMethods(realTarget.getClass(), methodName, varargs.length);
+ Object retVal = handleInvHandlerMethod(objToInvoke, methodName, methods, varargs);
+ if (!methodNotFound(retVal)) {
+ return retVal;
+ }
+ //if not we try all of them until we have a match
+ methods = getAllMethods(realTarget.getClass(), methodName, varargs.length);
+ retVal = handleInvHandlerMethod(objToInvoke, methodName, methods, varargs);
+ if (!(methodNotFound(retVal))) {
+ return retVal;
+ }
+
+ throw new RuntimeException("Method of :" + methodName + " from class " + obj.getClass().getName() + " not found");
+ }
+
+ Class clazz = obj.getClass();
+
+ //first we try only the public because they are the most likely ones
+ //to be accessed
+ methods = getMethods(clazz, methodName, varargs.length);
+ Object retVal = handleObjMethod(obj, methodName, methods, varargs);
+ if (!methodNotFound(retVal)) {
+ return retVal;
+ }
+
+ //if not we try all of them until we have a match
+ methods = getAllMethods(clazz, methodName, varargs.length);
+ retVal = handleObjMethod(obj, methodName, methods, varargs);
+ if (!methodNotFound(retVal)) {
+ return retVal;
+ }
+
+ throw new RuntimeException("Method of :" + methodName + " from class " + obj.getClass().getName() + " not found");
+ }
+
+ /**
+ * special marker class which is a special return value indicating
+ * that not method has been found which can be executed
+ */
+ static class _MethodNotFound {
+ }
+
+ /**
+ * check if the return value is a method not found return val which
+ * indicates we have to follow the next workflow step
+ *
+ * @param retVal the retVal which has to be investigated
+ * @return true if the retVal is instance of _MethodNotFound false otherwise
+ */
+ private static boolean methodNotFound(Object retVal) {
+ return retVal instanceof _MethodNotFound;
+ }
+
+ /**
+ * executes a method in an invocation handler with a set of
+ * methods which are canidates for execution
+ *
+ * @param objToInvoke the invokee object
+ * @param methodName the method name
+ * @param methods the methods which are under investigation for invoking
+ * @param varargs the list of varargs to be passed to the method
+ * @return the result of the invocation, or an object of type _MethodNotFound otherwise
+ */
+ static private Object handleInvHandlerMethod(InvocationHandler objToInvoke, String methodName, Collection<Method> methods, Object... varargs) {
+ for (Method m : methods) {
+ if (!m.getName().equals(methodName) || m.getParameterTypes().length != varargs.length) {
+ continue;
+ }
+ try {
+ return objToInvoke.invoke(objToInvoke, m, varargs);
+ } catch (Throwable e) {
+ handleException(e);
+ }
+ }
+ return new _MethodNotFound();
+ }
+
+ /**
+ * executes a method on an object
+ *
+ * @param objToInvoke the invokee object
+ * @param methodName the method name
+ * @param methods the methods which are under investigation for invoking
+ * @param varargs the list of varargs to be passed to the method
+ * @return the result of the invocation, or an object of type _MethodNotFound otherwise
+ */
+ static private Object handleObjMethod(Object objToInvoke, String methodName, Collection<Method> methods, Object... varargs) {
+ for (Method m : methods) {
+ if (!m.getName().equals(methodName) || m.getParameterTypes().length != varargs.length) {
+ continue;
+ }
+ try {
+ return m.invoke(objToInvoke, varargs);
+ } catch (Throwable e) {
+ handleException(e);
+ }
+ }
+ return new _MethodNotFound();
+ }
+
+ /**
+ * executes a static method on a class
+ *
+ * @param objToInvoke the invokee object
+ * @param methodName the method name
+ * @param methods the methods which are under investigation for invoking
+ * @param varargs the list of varargs to be passed to the method
+ * @return the result of the invocation, or an object of type _MethodNotFound otherwise
+ */
+ static private Object handleStaticMethod(Class objToInvoke, String methodName, Collection<Method> methods, Object... varargs) {
+ for (Method m : methods) {
+ if (!m.getName().equals(methodName) || m.getParameterTypes().length != varargs.length) {
+ continue;
+ }
+ try {
+ return m.invoke(objToInvoke, varargs);
+ } catch (Throwable e) {
+ handleException(e);
+ }
+ }
+ return new _MethodNotFound();
+ }
+
+ private static void handleException(Throwable e) {
+ if (e instanceof IllegalAccessException) {
+ if (_logger.isLoggable(Level.FINEST)) {
+ _logger.log(Level.FINEST, "", e);
+ }
+ } else if (e instanceof IllegalArgumentException) {
+ if (_logger.isLoggable(Level.FINEST)) {
+ _logger.log(Level.FINEST, "", e);
+ }
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * executes a function method on a target object
+ *
+ * @param obj the target object
+ * @param methodName the method name
+ * @param varargs a list of objects casts or nulls defining the parameter classes and its values
+ * if something occurs on introspection level an unmanaged exception is throw, just like
+ * it would happen in a scripting class
+ * @return the result object for the Method(method) call
+ * @throws RuntimeException an unmanaged runtime exception in case of an introspection error
+ */
+ public static Object fastExecuteMethod(Object obj, String methodName, Object... varargs) {
+ Class[] classes = new Class[varargs.length];
+ for (int cnt = 0; cnt < varargs.length; cnt++) {
+
+ if (varargs[cnt] instanceof Cast) {
+ classes[cnt] = ((Cast) varargs[cnt]).getClazz();
+ varargs[cnt] = ((Cast) varargs[cnt]).getValue();
+ } else {
+ classes[cnt] = varargs[cnt].getClass();
+ }
+ }
+
+ try {
+ Method m = fastGetMethod(obj, methodName, classes);
+ return m.invoke(obj, varargs);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ /**
+ * faster reflection call
+ * if we know the data types exactly we can
+ * trigger a direct call instead of walking through all methods
+ * note this method only allows to trigger against directly declared methods
+ * it ignores the inheritance hierarchy for faster access
+ *
+ * @param obj the invokee object
+ * @param methodName the metod name
+ * @param classes the parameter type classes
+ * @return the method if found
+ * @throws NoSuchMethodException in case it could not be found
+ */
+ public static Method fastGetMethod(Object obj, String methodName, Class[] classes) throws NoSuchMethodException {
+ Method m;
+ try {
+ m = obj.getClass().getDeclaredMethod(methodName, classes);
+ } catch (NoSuchMethodException e) {
+ m = obj.getClass().getMethod(methodName, classes);
+ }
+ return m;
+ }
+
+ /**
+ * executes a function method on a target object
+ *
+ * @param obj the target object
+ * @param methodName the method name
+ * @param varargs a list of objects casts or nulls defining the parameter classes and its values
+ * if something occurs on introspection level an unmanaged exception is throw, just like
+ * it would happen in a scripting class
+ * @return the result object for the Method(method) call
+ * @throws RuntimeException an unmanaged runtime exception in case of an introspection error
+ */
+ public static Object fastExecuteStaticMethod(Class obj, String methodName, Object... varargs) {
+ Class[] classes = new Class[varargs.length];
+ for (int cnt = 0; cnt < varargs.length; cnt++) {
+
+ if (varargs[cnt] instanceof Cast) {
+ classes[cnt] = ((Cast) varargs[cnt]).getClazz();
+ varargs[cnt] = ((Cast) varargs[cnt]).getValue();
+ } else {
+ classes[cnt] = varargs[cnt].getClass();
+ }
+ }
+
+ try {
+ Method m = fastGetStaticMethod(obj, methodName, classes);
+ return m.invoke(obj, varargs);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ public static Method fastGetStaticMethod(Class obj, String methodName, Class[] classes) throws NoSuchMethodException {
+ Method m;
+ try {
+ m = obj.getDeclaredMethod(methodName, classes);
+ } catch (NoSuchMethodException e) {
+ m = obj.getMethod(methodName, classes);
+ }
+ return m;
+ }
+
+ /**
+ * convenience method which makes the code a little bit more readable
+ * use it in conjunction with static imports
+ *
+ * @param clazz the cast target for the method call
+ * @param value the value object to be used as param
+ * @return a Cast object of the parameters
+ */
+ public static Cast cast(Class clazz, Object value) {
+ return new Cast(clazz, value);
+ }
+
+ /**
+ * convenience method which makes the code a little bit more readable
+ * use it in conjunction with static imports
+ *
+ * @param clazz the cast target for the method call
+ * @return a null value Cast object of the parameters
+ */
+ public static Null nullCast(Class clazz) {
+ return new Null(clazz);
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/Strategy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/Strategy.java
new file mode 100644
index 0000000..cff2760
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/Strategy.java
@@ -0,0 +1,33 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.common.util;
+
+/**
+ * Applied strategy class for iteration walkers
+ * to make the handling of iterated objects
+ * more scripting language like (aka a pattern similar to closures)
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+public interface Strategy {
+
+ public void apply(Object element);
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/StringUtils.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/StringUtils.java
new file mode 100644
index 0000000..bb7a0e0
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/common/util/StringUtils.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.common.util;
+
+/**
+ * Replacement for commons lang
+ * because we want to get the dependency out
+ * after all we only used StringUtils
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class StringUtils {
+ public static boolean isBlank(String in) {
+ return in == null || in.trim().equals("");
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/BaseEngine.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/BaseEngine.java
new file mode 100644
index 0000000..e5634fe
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/BaseEngine.java
@@ -0,0 +1,242 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassUtils;
+import org.apache.myfaces.extensions.scripting.core.common.util.FileUtils;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.DependencyRegistry;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.core.ClassDependencies;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.registry.DependencyRegistryImpl;
+import org.apache.myfaces.extensions.scripting.core.monitor.ClassResource;
+
+import javax.servlet.ServletContext;
+import java.io.File;
+import java.net.URL;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.logging.Logger;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public abstract class BaseEngine
+{
+ CopyOnWriteArrayList<String> _sourcePaths = new CopyOnWriteArrayList<String>();
+ Map<String, ClassResource> _watchedResources = new ConcurrentHashMap<String, ClassResource>();
+ //both belong together but the dependencyregistry is just
+ //a wrapper for the dep map with additional functionality
+ ClassDependencies _dependencyMap = new ClassDependencies();
+ DependencyRegistry _dependencyRegistry = new DependencyRegistryImpl(getEngineType(), _dependencyMap);
+
+ Logger log = Logger.getLogger(this.getClass().getName());
+
+
+ protected BaseEngine()
+ {
+
+ }
+
+ public Map<String, ClassResource> getWatchedResources()
+ {
+ return _watchedResources;
+ }
+
+ public List<String> getSourcePaths()
+ {
+ return _sourcePaths;
+ }
+
+ public abstract String getFileEnding();
+
+ public abstract int getEngineType();
+
+ /**
+ * @return a collection of possible dynamic classes
+ */
+ public Collection<String> getPossibleDynamicClasses()
+ {
+ return _watchedResources.keySet();
+ }
+
+ /**
+ * runs a full scan of files to get a list of files which need to be processed
+ * for the future
+ */
+ public void scanForAddedDeleted()
+ {
+ Set<String> processedClasses = new HashSet<String>();
+ processedClasses.addAll(_watchedResources.keySet());
+
+ for (String sourcePath : getSourcePaths())
+ {
+ Collection<File> sourceFiles = FileUtils.fetchSourceFiles(new File(sourcePath), "*." + getFileEnding());
+
+ for (File sourceFile : sourceFiles)
+ {
+ ClassResource classToProcess = new ClassResource();
+ classToProcess.setFile(sourceFile);
+
+
+ classToProcess.setScriptingEngine(getEngineType());
+ if (!_watchedResources.containsKey(classToProcess.getIdentifier()))
+ {
+ _watchedResources.put(classToProcess.getIdentifier(), classToProcess);
+ } else
+ {
+ processedClasses.remove(classToProcess.getIdentifier());
+
+ classToProcess = _watchedResources.get(classToProcess.getIdentifier());
+ }
+ if (classToProcess.needsRecompile())
+ {
+ //TODO add entry for logging component here
+ log.info("[EXT-SCRIPTING] tainting " + classToProcess.getIdentifier());
+ classToProcess.setTainted(true);
+ classToProcess.setChangedForCompile(true);
+ }
+ }
+ }
+ for (String deleted : processedClasses)
+ {
+ _watchedResources.remove(deleted);
+ }
+
+ }
+
+ /**
+ * checks whether we have resources which are in need of a recompile
+ *
+ * @return
+ */
+ public boolean needsRecompile()
+ {
+ //TODO buffer this from scan
+ for (Map.Entry<String, ClassResource> resource : _watchedResources.entrySet())
+ {
+ if (resource.getValue().needsRecompile()) return true;
+ }
+ return false;
+ }
+
+ /**
+ * checks whether we have resources which are tainted
+ *
+ * @return
+ */
+ public boolean isTainted()
+ {
+ //TODO buffer this from scan
+ for (Map.Entry<String, ClassResource> resource : _watchedResources.entrySet())
+ {
+ if (resource.getValue().isTainted()) return true;
+ }
+ return false;
+ }
+
+ public DependencyRegistry getDependencyRegistry()
+ {
+ return _dependencyRegistry;
+ }
+
+ public void setDependencyRegistry(DependencyRegistry dependencyRegistry)
+ {
+ _dependencyRegistry = dependencyRegistry;
+ }
+
+ public ClassDependencies getDependencyMap()
+ {
+ return _dependencyMap;
+ }
+
+ public void setDependencyMap(ClassDependencies dependencyMap)
+ {
+ _dependencyMap = dependencyMap;
+ }
+
+ /**
+ * marks all the dependencies of the tainted objects
+ * also as tainted to allow proper refreshing.
+ */
+ public void markTaintedDependencies()
+ {
+ //basic tainted set by the full scall
+ Set<String> _processedClasses = new HashSet<String>();
+ for (Map.Entry<String, ClassResource> entry : _watchedResources.entrySet())
+ {
+ //TODO add entry for logging component here
+ ClassResource resource = entry.getValue();
+ if (!resource.isChangedForCompile()) continue;
+ resource.setChangedForCompile(false);
+ log.info("[EXT-SCRIPTING] tainting dependency " + resource.getIdentifier());
+ resource.setTainted(true);
+ //classname
+ String identifier = resource.getIdentifier();
+ if (_processedClasses.contains(identifier)) continue;
+ markDependencies(_processedClasses, identifier);
+
+ }
+
+ }
+
+ /*marks all backward dependencies of the existing class*/
+ private void markDependencies(Set<String> _processedClasses, String identifier)
+ {
+ Set<String> referringClasses = _dependencyMap.getReferringClasses(identifier);
+ if (referringClasses == null) return;
+ for (String referringClass : referringClasses)
+ {
+ if (_processedClasses.contains(referringClass)) continue;
+ ClassResource toTaint = _watchedResources.get(referringClass);
+ if (toTaint == null) continue;
+ //TODO add entry for logging component here
+ if (toTaint.isTainted())
+ {
+ log.info("[EXT-SCRIPTING] dependency already tainted:" + toTaint.getIdentifier());
+ _processedClasses.add(toTaint.getIdentifier());
+ continue;
+ }
+ toTaint.setTainted(true);
+ toTaint.setChangedForCompile(false);
+ log.info("[EXT-SCRIPTING] tainting dependency " + toTaint.getIdentifier());
+ _processedClasses.add(toTaint.getIdentifier());
+ markDependencies(_processedClasses, toTaint.getIdentifier());
+ }
+
+ }
+
+ protected void initPaths(ServletContext context, String initParam, String defaultValue)
+ {
+ String pathSeparatedList = context.getInitParameter(initParam);
+ pathSeparatedList = (pathSeparatedList != null) ? pathSeparatedList : defaultValue;
+ if (pathSeparatedList.equals(defaultValue))
+ {
+ URL resource = ClassUtils.getContextClassLoader().getResource("./");
+ pathSeparatedList = FilenameUtils.normalize(resource.getPath() + "../.." + defaultValue);
+ }
+ String[] paths = pathSeparatedList.split(",");
+ for (String path : paths)
+ {
+ getSourcePaths().add(path);
+ }
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/BaseScanner.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/BaseScanner.java
new file mode 100644
index 0000000..5db635a
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/BaseScanner.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 org.apache.myfaces.extensions.scripting.core.engine;
+
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.engine.api.ScriptingEngine;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.StandardDependencyScanner;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.DependencyScanner;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.filter.WhitelistFilter;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.loaders.ScannerClassloader;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.registry.ExternalFilterDependencyRegistry;
+
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public abstract class BaseScanner
+{
+ List<String> _scanPaths = new LinkedList<String>();
+ DependencyScanner _depencyScanner = new StandardDependencyScanner();
+ Logger _log = Logger.getLogger(JavaDependencyScanner.class.getName());
+
+ public abstract int getEngineType();
+
+ public abstract String getFileEnding();
+
+ public synchronized void scanPaths()
+ {
+ //only one dependency check per refresh makes sense in our case
+ /* if (WeavingContext.getRefreshContext().isDependencyScanned(getEngineType())) {
+ return;
+ } else {
+ WeavingContext.getRefreshContext().setDependencyScanned(getEngineType(), true);
+ }*/
+ ScriptingEngine engine = WeavingContext.getInstance().getEngine(ScriptingConst.ENGINE_TYPE_JSF_JAVA);
+
+ if (_log.isLoggable(Level.INFO))
+ {
+ _log.info("[EXT-SCRITPING] starting class dependency scan");
+ }
+ long start = System.currentTimeMillis();
+ final Set<String> possibleDynamicClasses = new HashSet<String>(engine.getPossibleDynamicClasses());
+
+ final ClassLoader loader = getClassLoader();
+ for (String dynamicClass : possibleDynamicClasses)
+ {
+ runScan(possibleDynamicClasses, loader, dynamicClass);
+ }
+
+ long end = System.currentTimeMillis();
+ if (_log.isLoggable(Level.FINE))
+ {
+ _log.log(Level.FINE, "[EXT-SCRITPING] class dependency scan finished, duration: {0} ms", Long.toString(end - start));
+ }
+
+ }
+
+ public void scanClass(Class clazz)
+ {
+ //TODO do nothing here
+ }
+
+ private void runScan(final Set<String> possibleDynamicClasses, final ClassLoader loader, String dynamicClass)
+ {
+ //TODO implement the dep registry
+ ExternalFilterDependencyRegistry scanRegistry = (ExternalFilterDependencyRegistry) WeavingContext.getInstance()
+ .getEngine(getEngineType()).getDependencyRegistry();
+
+ scanRegistry.clearFilters();
+ //We have to dynamically readjust the filters
+ scanRegistry.addFilter(new WhitelistFilter(possibleDynamicClasses));
+ _depencyScanner.fetchDependencies(loader, getEngineType(), dynamicClass,
+ WeavingContext.getInstance().getEngine(getEngineType()).getDependencyRegistry());
+ }
+
+ protected ClassLoader getClassLoader()
+ {
+ try
+ {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<ScannerClassloader>()
+ {
+ public ScannerClassloader run()
+ {
+ return new ScannerClassloader(Thread.currentThread().getContextClassLoader(), getEngineType(),
+ getFileEnding(), WeavingContext.getInstance().getConfiguration().getCompileTarget());
+ }
+ });
+ }
+ catch (PrivilegedActionException e)
+ {
+ _log.log(Level.SEVERE, "", e);
+ }
+ return null;
+ }
+
+ public void addScanPath(String scanPath)
+ {
+ _scanPaths.add(scanPath);
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/EngineGroovy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/EngineGroovy.java
new file mode 100644
index 0000000..41a3320
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/EngineGroovy.java
@@ -0,0 +1,101 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine;
+
+import org.apache.myfaces.extensions.scripting.groovyloader.core.StandardGroovyReloadingStrategy;
+import org.apache.myfaces.extensions.scripting.core.api.Configuration;
+import org.apache.myfaces.extensions.scripting.core.api.ReloadingStrategy;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassUtils;
+import org.apache.myfaces.extensions.scripting.core.engine.api.CompilationResult;
+import org.apache.myfaces.extensions.scripting.core.engine.api.ScriptingEngine;
+import org.apache.myfaces.extensions.scripting.core.engine.compiler.GroovyCompiler;
+
+import javax.servlet.ServletContext;
+import java.io.File;
+import java.util.Collection;
+import java.util.logging.Logger;
+
+import static org.apache.myfaces.extensions.scripting.core.api.ScriptingConst.*;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class EngineGroovy extends BaseEngine implements ScriptingEngine
+{
+ Logger log = Logger.getLogger(this.getClass().getName());
+
+ @Override
+ public void init(ServletContext context)
+ {
+ initPaths(context, INIT_PARAM_CUSTOM_GROOVY_LOADER_PATHS, GROOVY_SOURCE_ROOT);
+ }
+
+ @Override
+ public int getEngineType()
+ {
+ return ENGINE_TYPE_JSF_GROOVY;
+ }
+
+ public String getEngineTypeAsStr() {
+ return "Groovy";
+ }
+
+ @Override
+ public ReloadingStrategy getBasicReloadingStrategy()
+ {
+ return new StandardGroovyReloadingStrategy();
+ }
+
+ @Override
+ public String getFileEnding()
+ {
+ return "groovy";
+ }
+
+ @Override
+ //full compile
+ public CompilationResult compile()
+ {
+ WeavingContext context = WeavingContext.getInstance();
+ Configuration configuration = context.getConfiguration();
+ GroovyCompiler compiler = new GroovyCompiler();
+ File targetDir = configuration.getCompileTarget();
+ Collection<String> sourceDirs = configuration.getSourceDirs(ENGINE_TYPE_JSF_GROOVY);
+ CompilationResult res = null;
+ for (String sourceRoot : sourceDirs)
+ {
+ res = compiler.compile(new File(sourceRoot), targetDir, ClassUtils.getContextClassLoader());
+ }
+ return res;
+ }
+
+ public void scanDependencies()
+ {
+ log.info("[EXT-SCRIPTING] starting dependency scan");
+ GroovyDependencyScanner scanner = new GroovyDependencyScanner();
+ scanner.scanPaths();
+ log.info("[EXT-SCRIPTING] ending dependency scan");
+ }
+
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/EngineJava.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/EngineJava.java
new file mode 100644
index 0000000..b08c543
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/EngineJava.java
@@ -0,0 +1,113 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine;
+
+import org.apache.myfaces.extensions.scripting.core.api.Configuration;
+import org.apache.myfaces.extensions.scripting.core.api.ReloadingStrategy;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassUtils;
+import org.apache.myfaces.extensions.scripting.core.engine.api.CompilationException;
+import org.apache.myfaces.extensions.scripting.core.engine.api.CompilationResult;
+import org.apache.myfaces.extensions.scripting.core.engine.api.ScriptingEngine;
+import org.apache.myfaces.extensions.scripting.core.engine.compiler.JSR199Compiler;
+import org.apache.myfaces.extensions.scripting.core.reloading.SimpleReloadingStrategy;
+
+import javax.servlet.ServletContext;
+import java.io.File;
+import java.util.Collection;
+import java.util.logging.Logger;
+
+import static org.apache.myfaces.extensions.scripting.core.api.ScriptingConst.*;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class EngineJava extends BaseEngine implements ScriptingEngine
+{
+
+ Logger log = Logger.getLogger(this.getClass().getName());
+
+ @Override
+ public void init(ServletContext context)
+ {
+ initPaths(context, INIT_PARAM_CUSTOM_JAVA_LOADER_PATHS, JAVA_SOURCE_ROOT);
+ }
+
+ /**
+ * full compile will be called cyclicly
+ * from the startup and daemon thread
+ */
+ public CompilationResult compile()
+ {
+ WeavingContext context = WeavingContext.getInstance();
+ Configuration configuration = context.getConfiguration();
+ JSR199Compiler compiler = new JSR199Compiler();
+ File targetDir = configuration.getCompileTarget();
+ Collection<String> sourceDirs = configuration.getSourceDirs(getEngineType());
+ CompilationResult res = null;
+ for (String sourceRoot : sourceDirs)
+ {
+ res = compiler.compile(new File(sourceRoot), targetDir,
+ ClassUtils.getContextClassLoader());
+ if(res.hasErrors()) {
+ for(CompilationResult.CompilationMessage msg :res.getErrors()) {
+ log.severe(msg.getMessage());
+ }
+ // log.severe(res.getCompilerOutput());
+ }
+ }
+ return res;
+ }
+
+ public void scanDependencies()
+ {
+ log.info("[EXT-SCRIPTING] starting dependency scan "+getEngineTypeAsStr());
+ JavaDependencyScanner scanner = new JavaDependencyScanner();
+ scanner.scanPaths();
+ log.info("[EXT-SCRIPTING] ending dependency scan" + getEngineTypeAsStr());
+ }
+
+ //-------------------------------------------------------------------------------------
+
+ @Override
+ public int getEngineType()
+ {
+ return ENGINE_TYPE_JSF_JAVA;
+ }
+
+ public String getEngineTypeAsStr() {
+ return "Java";
+ }
+
+ @Override
+ public ReloadingStrategy getBasicReloadingStrategy()
+ {
+ return new SimpleReloadingStrategy();
+ }
+
+ @Override
+ public String getFileEnding()
+ {
+ return "java";
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/FactoryEngines.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/FactoryEngines.java
new file mode 100644
index 0000000..ce6eb3e
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/FactoryEngines.java
@@ -0,0 +1,134 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine;
+
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassUtils;
+import org.apache.myfaces.extensions.scripting.core.common.util.FileStrategy;
+import org.apache.myfaces.extensions.scripting.core.common.util.FileUtils;
+import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
+import org.apache.myfaces.extensions.scripting.core.engine.api.ScriptingEngine;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * holds references to the scripting engines
+ * initializes the engins dynamically
+ */
+public class FactoryEngines
+{
+ final Logger _log = Logger.getLogger(this.getClass().getName());
+
+ Map<Integer, ScriptingEngine> _engines = new ConcurrentHashMap<Integer, ScriptingEngine>();
+ List<ScriptingEngine> _engineOrder = new CopyOnWriteArrayList<ScriptingEngine>();
+
+ public void init() throws IOException
+ {
+ //loadEnginesDynamically();
+
+ EngineJava javaEngine = new EngineJava();
+ EngineGroovy groovyEngine = new EngineGroovy();
+ if (_engines.isEmpty())
+ {
+ _engines.put(javaEngine.getEngineType(), javaEngine);
+ _engines.put(groovyEngine.getEngineType(), groovyEngine);
+ _engineOrder.add(javaEngine);
+ _engineOrder.add(groovyEngine);
+ }
+ }
+
+ /**
+ * loads the engins dynamically from
+ * their corresponding package and name
+ *
+ * @throws IOException
+ */
+ private void loadEnginesDynamically() throws IOException
+ {
+ ClassLoader currentLoader = ClassUtils.getContextClassLoader();//this.getClass().getClassLoader();
+ String canonicalPackageName = this.getClass().getPackage().getName().replaceAll("\\.", File.separator);
+ //TODO not working in a servlet environment we for now map it hardcoded
+ Enumeration<URL> enumeration = currentLoader.getResources(canonicalPackageName);
+ while (enumeration.hasMoreElements())
+ {
+ //we load all classes which start with engine initially those are our
+ //enginesvTH
+ URL element = enumeration.nextElement();
+ File file = new File(element.getFile());
+ FileStrategy strategy = new FileStrategy(Pattern.compile("engine[^\\.(test)]+\\.class$"));
+ FileUtils.listFiles(file, strategy);
+ for (File foundFile : strategy.getFoundFiles())
+ {
+ String absoluteDir = foundFile.getAbsolutePath();
+
+ //TODO windows
+ String rootDir = absoluteDir.substring(0, absoluteDir.indexOf(canonicalPackageName));
+ String className = absoluteDir.substring(rootDir.length()).replaceAll(File.separator, ".");
+ className = className.substring(0, className.length() - 6);
+ try
+ {
+ ScriptingEngine engine = (ScriptingEngine) ReflectUtil.instantiate(currentLoader.loadClass
+ (className));
+ _engines.put(engine.getEngineType(), engine);
+ String supportedLanguage = className.substring(className.indexOf(".Engine") + ".Engine".length
+ ());
+ _log.info("[EXT-SCRIPTING] initializing Engine " + supportedLanguage);
+ _engineOrder.add(engine);
+ }
+ catch (ClassNotFoundException e)
+ {
+ //cannot happen
+ }
+ }
+ }
+ }
+
+ public Collection<ScriptingEngine> getEngines()
+ {
+ return _engineOrder;
+ }
+
+ public ScriptingEngine getEngine(int engineType)
+ {
+ return _engines.get(engineType);
+ }
+
+ private static FactoryEngines _ourInstance = new FactoryEngines();
+
+ public static FactoryEngines getInstance()
+ {
+ return _ourInstance;
+ }
+
+ private FactoryEngines()
+ {
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/GroovyDependencyScanner.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/GroovyDependencyScanner.java
new file mode 100644
index 0000000..3a4a8fc
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/GroovyDependencyScanner.java
@@ -0,0 +1,44 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine;
+
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.engine.api.ClassScanner;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class GroovyDependencyScanner extends BaseScanner implements ClassScanner
+{
+
+ public GroovyDependencyScanner() {
+ }
+
+ public int getEngineType() {
+ return ScriptingConst.ENGINE_TYPE_JSF_GROOVY;
+ }
+ public String getFileEnding() {
+ return ScriptingConst.FILE_EXTENSION_GROOVY;
+ }
+
+}
+
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/JavaDependencyScanner.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/JavaDependencyScanner.java
new file mode 100644
index 0000000..274dc43
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/JavaDependencyScanner.java
@@ -0,0 +1,44 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine;
+
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.engine.api.ClassScanner;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class JavaDependencyScanner extends BaseScanner implements ClassScanner
+{
+
+ public JavaDependencyScanner() {
+ }
+
+ public int getEngineType() {
+ return ScriptingConst.ENGINE_TYPE_JSF_JAVA;
+ }
+ public String getFileEnding() {
+ return ScriptingConst.JAVA_FILE_ENDING;
+ }
+
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/ClassScanner.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/ClassScanner.java
new file mode 100644
index 0000000..0274fc4
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/ClassScanner.java
@@ -0,0 +1,39 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.api;
+
+/**
+ * Generic class scanner interface
+ * which is a helper to plug in external scanners
+ * as adapters for the annotation and dependency handling
+ * we cannot deal with annotations directly in the core
+ * because we are bound by the jsf 1.2 lower threshold limit
+ * hence this indirection
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+public interface ClassScanner {
+
+ public void scanPaths();
+ public void scanClass(Class clazz);
+
+ public void addScanPath(String scanPath);
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/CompilationException.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/CompilationException.java
new file mode 100644
index 0000000..57dac04
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/CompilationException.java
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.api;
+
+/**
+ * <p>This exception will usually be thrown if an error occurred while compiling a
+ * set of source files. However, note that it doesn't mean that the source files
+ * themselves contained errors but rather that the system couldn't managed to
+ * find an appropriate compiler implementation, etc.</p>
+ * <p/>
+ * <p>In order to determine whether the compiler successfully compiled a certain
+ * source file you have to look for the compilation result instead.</p>
+ */
+public class CompilationException extends Exception {
+
+ // ------------------------------------------ Constructors
+
+ /**
+ * <p>Constructs a new compilation exception with the specified detail message.</p>
+ *
+ * @param message the detail message. The detail message is saved for
+ * later retrieval by the {@link #getMessage()} method.
+ */
+ public CompilationException(String message) {
+ super(message);
+ }
+
+ /**
+ * <p>Constructs a compilation new exception with the specified detail message
+ * and cause. <p>Note that the detail message associated with
+ * <code>cause</code> is <i>not</i> automatically incorporated in
+ * this exception's detail message.</p>
+ *
+ * @param message the detail message (which is saved for later retrieval
+ * by the {@link #getMessage()} method).
+ * @param cause the cause (which is saved for later retrieval by the
+ * {@link #getCause()} method). (A <tt>null</tt> value is
+ * permitted, and indicates that the cause is nonexistent or
+ * unknown.)
+ */
+ public CompilationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * <p>Constructs a new compilation exception with the specified cause and
+ * a detail message of <tt>(cause==null ? null : cause.toString())</tt>
+ * (which typically contains the class and detail message of
+ * <tt>cause</tt>).</p>
+ *
+ * @param cause the cause (which is saved for later retrieval by the
+ * {@link #getCause()} method). (A <tt>null</tt> value is
+ * permitted, and indicates that the cause is nonexistent or
+ * unknown.)
+ */
+ public CompilationException(Throwable cause) {
+ super(cause);
+ }
+
+}
+
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/CompilationResult.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/CompilationResult.java
new file mode 100644
index 0000000..c16ba58
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/CompilationResult.java
@@ -0,0 +1,199 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.api;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>Contains all information regarding the result of a particular compilation process.</p>
+ */
+public class CompilationResult {
+
+ /**
+ * The compiler output, i.e. simply everything that the compiler would usually
+ * print to the console, if you executed the same process on the command line
+ * instead.
+ */
+ private String _compilerOutput;
+
+ /**
+ * A list of error messages that the compiler has produced. Note that if there
+ * are no error messages, it's safe to assume that compilation succeeded.
+ */
+ private List<CompilationMessage> _errors;
+
+ /**
+ * A list of warnings that the compiler has produced.
+ */
+ private List<CompilationMessage> _warnings;
+
+ // ------------------------------------------ Constructors
+
+ /**
+ * <p>Constructs a new compilation result object using the compiler output. However,
+ * note that this constructor doesn't attempt to parse the compiler output to get the
+ * error messages and warnings. You'll have to register those messages yourself
+ * afterwards.</p>
+ *
+ * @param compilerOutput the compiler output, i.e. simply everything that the compiler would
+ * usually print to the console, if you executed the same process on
+ * the command line instead
+ */
+ public CompilationResult(String compilerOutput) {
+ this._compilerOutput = compilerOutput;
+
+ this._errors = new ArrayList<CompilationMessage>();
+ this._warnings = new ArrayList<CompilationMessage>();
+ }
+
+ // ------------------------------------------ Public methods
+
+ /**
+ * <p>Returns the compiler output, i.e. simply everything that the compiler would usually
+ * print to the console, if you executed the same process on the command line
+ * instead.</p>
+ *
+ * @return the compiler output
+ */
+ public String getCompilerOutput() {
+ return _compilerOutput;
+ }
+
+ /**
+ * <p>Determines whether any error messages have been registered, i.e. whether compilation
+ * was successful.</p>
+ *
+ * @return <code>true</code if no error messages have been registered, i.e. if compilation
+ * was sucessful; <code>false</code> otherwise
+ */
+ public boolean hasErrors() {
+ return !_errors.isEmpty();
+ }
+
+ /**
+ * <p>Registers the given message as an error message.</p>
+ *
+ * @param message the error message you want to register
+ */
+ public void registerError(CompilationMessage message) {
+ if (message != null) {
+ _errors.add(message);
+ }
+ }
+
+ /**
+ * <p>Returns a list of error messages that the compiler has produced,
+ * i.e. the error messages that have been registered previously.</p>
+ *
+ * @return a list of error messages
+ */
+ public List<CompilationMessage> getErrors() {
+ return _errors;
+ }
+
+ /**
+ * <p>Registers the given message as a warning. You can register as many warnings as you want
+ * and it won't affect whether compilation was sucessful or not.</p>
+ *
+ * @param message the warning you want to register
+ */
+ public void registerWarning(CompilationMessage message) {
+ if (message != null) {
+ _warnings.add(message);
+ }
+ }
+
+ /**
+ * <p>Returns a list of warnings that the compiler has produced,
+ * i.e. the warnings that have been registered previously.</p>
+ *
+ * @return a list of warnings
+ */
+ public List<CompilationMessage> getWarnings() {
+ return _warnings;
+ }
+
+ // ------------------------------------------ Public static classes
+
+ /**
+ * <p>Utility class that contains all the required information regarding
+ * a single compilation message.</p>
+ */
+ public static class CompilationMessage {
+
+ /**
+ * the line number of this compilation message
+ */
+ private long lineNumber;
+
+ /**
+ * the actual compilation message
+ */
+ private String message;
+
+ // -------------------------------------- Constructors
+
+ /**
+ * <p>Constructs a new compilation message using the line number
+ * and the actual compilation message as a string.</p>
+ *
+ * @param lineNumber the line number
+ * @param message the actual compilation message
+ */
+ public CompilationMessage(long lineNumber, String message) {
+ this.lineNumber = lineNumber;
+ this.message = message;
+ }
+
+ // -------------------------------------- Public methods
+
+ /**
+ * <p>The number of the relevant line where this warning or error
+ * has occured, or <code>-1</code> if it is not known.</p>
+ *
+ * @return the line number
+ */
+ public long getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * <p>Returns the message itself as a string, i.e. the textual content
+ * of whatever the compiler complained about.</p>
+ *
+ * @return the message itself as a string
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * <p>Returns a string representation of this compilation message.</p>
+ *
+ * @return a string representation of this compilation message
+ */
+ @Override
+ public String toString() {
+ return String.format(
+ "CompilationMessage[lineNumber='%s', message='%s']", lineNumber, message);
+ }
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/Compiler.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/Compiler.java
new file mode 100644
index 0000000..548713a
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/Compiler.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 org.apache.myfaces.extensions.scripting.core.engine.api;
+
+import java.io.File;
+
+/**
+ * <p>An abstract compiler interface that enables you to compile one particular file at a time.</p>
+ */
+public interface Compiler {
+
+
+
+ /**
+ * <p>Compiles the given file and creates an according class file in the given target path. Note that
+ * it is possible for the given class to reference any other classes as long as the dependent classes
+ * are available on the classpath. The given class loader determines the classes that are available
+ * on the classpath.</p>
+ *
+ * @param sourcePath the path to the source directory
+ * @param targetPath the path to the target directory
+ * @param classLoader the class loader for dependent classes
+ * @return the compilation result, i.e. the compiler output, a list of errors and a list of warnings
+ * @throws CompilationException
+ * if a severe error occurred while trying to compile a file
+ */
+ public CompilationResult compile(File sourcePath, File targetPath, ClassLoader classLoader);
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/CompilerConst.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/CompilerConst.java
new file mode 100644
index 0000000..ae940d0
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/CompilerConst.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.api;
+
+/**
+ * Various constants shared over the various compiler implementations
+ * JSR or non JSR!
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class CompilerConst {
+ public static final String STD_ERROR_HEAD = "Java Compiler, error on line: ";
+ public static final String STD_WARN_HEAD = "Java Compiler, warning on line: ";
+ public static final String STD_MANDATORY_WARN_HEAD = "Java Compiler, mandatory warning on line: ";
+ public static final String STD_OTHER_HEAD = "Java Compiler, other diagnostic on line: ";
+ public static final String STD_NOTE_HEAD = "Java Compiler, Info on line: ";
+ public static final String JC_CLASSPATH = "-cp";
+ public static final String JC_TARGET_PATH = "-d";
+ public static final String JC_SOURCEPATH = "-sourcepath";
+ public static final String JC_DEBUG = "-g";
+ public static final String JAVA_WILDCARD = "*.java ";
+ public static final String JC_VERBOSE = "-verbose";
+ @SuppressWarnings("unused")
+ public static final String JC_SOURCE = "-source";
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/ScriptingEngine.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/ScriptingEngine.java
new file mode 100644
index 0000000..b14ce26
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/api/ScriptingEngine.java
@@ -0,0 +1,123 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.api;
+
+import org.apache.myfaces.extensions.scripting.core.api.ReloadingStrategy;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.DependencyRegistry;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.core.ClassDependencies;
+import org.apache.myfaces.extensions.scripting.core.monitor.ClassResource;
+import org.apache.myfaces.extensions.scripting.core.monitor.WatchedResource;
+
+import javax.servlet.ServletContext;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+public interface ScriptingEngine
+{
+ public void init(ServletContext context);
+
+ public Collection<String> getPossibleDynamicClasses();
+
+ public List<String> getSourcePaths();
+
+ public Map<String, ClassResource> getWatchedResources();
+
+ /**
+ * @return the identifier for the engine type
+ * each identifier must be unique to the system
+ */
+ public int getEngineType();
+
+ /**
+ * returns the file ending sufficient for this engine
+ * @return
+ */
+ public String getFileEnding();
+
+ /**
+ * runs a full scan of files to get a list of files which need to be processed
+ * for the future
+ */
+ public void scanForAddedDeleted();
+
+ /**
+ * runs the compile cycle for this engine
+ */
+ public CompilationResult compile();
+
+ /**
+ * checks if the current engine has tainted classes
+ * @return
+ */
+ public boolean isTainted();
+
+ /**
+ * checks if the current engine has a need for a recompile of certain classes
+ * @return
+ */
+ public boolean needsRecompile();
+
+ /**
+ * gets the dependency map hosted in this engine
+ *
+ * @return the dependency map
+ */
+ public ClassDependencies getDependencyMap();
+
+ /**
+ * fetches the dependency registry
+ *
+ * @return the dependency registry
+ */
+ public DependencyRegistry getDependencyRegistry();
+
+ /**
+ * Scan dependencies for this submodule
+ */
+ public void scanDependencies();
+
+ /**
+ * mark the classes which are dependend
+ * as tainted according to the dependency graph
+ * The function has to walk the graph recursively
+ * according to its state and mark all backward references
+ * as tainted.
+ */
+ public void markTaintedDependencies();
+
+ /**
+ *
+ * @return a string representation
+ * of the corresponding engine
+ */
+ public String getEngineTypeAsStr();
+
+ /**
+ * loads the basic strategy which hosts also the property copying algorithm
+ *
+ * @return the basic strategy
+ */
+ public ReloadingStrategy getBasicReloadingStrategy();
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/compiler/ContainerFileManager.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/compiler/ContainerFileManager.java
new file mode 100644
index 0000000..d34ec3e
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/compiler/ContainerFileManager.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 org.apache.myfaces.extensions.scripting.core.engine.compiler;
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassLoaderUtils;
+
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * File manager, enforced by the Java Compiler API
+ * which handles the source files
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ContainerFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
+
+ StandardJavaFileManager _delegate = null;
+ String _classPath = null;
+
+
+ public ContainerFileManager(StandardJavaFileManager standardJavaFileManager) {
+ super(standardJavaFileManager);
+ _delegate = standardJavaFileManager;
+ }
+
+ @Override
+ public JavaFileObject getJavaFileForOutput(Location location, String s, JavaFileObject.Kind kind, FileObject fileObject) throws IOException {
+ return super.getJavaFileForOutput(location, s, kind, fileObject);
+ }
+
+ @Override
+ public ClassLoader getClassLoader(Location location) {
+ return Thread.currentThread().getContextClassLoader();
+ }
+
+ public ClassLoader getClassLoader() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+
+ public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) {
+ return _delegate.getJavaFileObjects(files);
+ }
+
+ public Iterable<? extends JavaFileObject> getJavaFileObjects(String... files) {
+ return _delegate.getJavaFileObjects(files);
+ }
+
+ public Iterable<? extends JavaFileObject> getJavaFileObjectsSingle(String files) {
+ return _delegate.getJavaFileObjects(files);
+ }
+
+ public String getClassPath() {
+ if (_classPath != null) {
+ return _classPath;
+ }
+
+ String retStr = ClassLoaderUtils.buildClasspath(getClassLoader(null));
+
+ return (_classPath = retStr);
+ }
+
+ public File getTempDir() {
+ return WeavingContext.getInstance().getConfiguration().getCompileTarget();
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/compiler/GroovyCompiler.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/compiler/GroovyCompiler.java
new file mode 100644
index 0000000..1089e57
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/compiler/GroovyCompiler.java
@@ -0,0 +1,160 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.compiler;
+
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.ErrorCollector;
+import org.codehaus.groovy.control.messages.Message;
+import org.codehaus.groovy.control.messages.SimpleMessage;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.apache.myfaces.extensions.scripting.core.api.Configuration;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassLoaderUtils;
+import org.apache.myfaces.extensions.scripting.core.common.util.FileUtils;
+import org.apache.myfaces.extensions.scripting.core.engine.api.CompilationResult;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>A compiler implementation that can be used to compile Groovy source files.</p>
+ */
+public class GroovyCompiler implements org.apache.myfaces.extensions.scripting.core.engine.api.Compiler
+{
+
+ /**
+ * The logger instance for this class.
+ */
+ private static final Logger _logger = Logger.getLogger(GroovyCompiler.class.getName());
+
+ CompilationResult result = null;
+ // ------------------------------------------ Compiler methods
+
+
+ public CompilationResult compile(File sourcePath, File targetPath, ClassLoader classLoader) {
+ WeavingContext context = WeavingContext.getInstance();
+ Configuration configuration = context.getConfiguration();
+
+
+
+ List<File> sourceFiles = FileUtils.fetchSourceFiles(configuration.getWhitelistedSourceDirs(ScriptingConst
+ .ENGINE_TYPE_JSF_GROOVY), "*.groovy");
+
+ StringWriter compilerOutput = new StringWriter();
+
+ CompilationUnit compilationUnit = new CompilationUnit(
+ buildCompilerConfiguration(sourcePath, targetPath, classLoader));
+ compilationUnit.getConfiguration().setOutput(new PrintWriter(compilerOutput));
+
+ for (File sourceFile : sourceFiles) {
+ compilationUnit.addSource(sourceFile);
+ }
+
+ CompilationResult result;
+
+ try {
+ compilationUnit.compile();
+
+ result = new CompilationResult(compilerOutput.toString());
+ //context.setCompilationResult(ScriptingConst.ENGINE_TYPE_JSF_GROOVY, result);
+ //this.result = result;
+ } catch (CompilationFailedException ex) {
+ // Register all collected error messages from the Groovy compiler
+ result = new CompilationResult(compilerOutput.toString());
+ ErrorCollector collector = compilationUnit.getErrorCollector();
+ for (int i = 0; i < collector.getErrorCount(); ++i) {
+ result.registerError(convertMessage(collector.getError(i)));
+ }
+ }
+
+ // Register all collected warnings from the Groovy compiler
+ ErrorCollector collector = compilationUnit.getErrorCollector();
+ for (int i = 0; i < collector.getWarningCount(); ++i) {
+ result.registerWarning(convertMessage(collector.getWarning(i)));
+ }
+
+
+ return result;
+ }
+
+
+
+ // ------------------------------------------ Utility methods
+
+ /**
+ * <p>Converts the given Groovy compiler message into a compilation message that
+ * our compilation API consists of.</p>
+ *
+ * @param message the Groovy compiler message you want to convert
+ * @return the final converted compilation message
+ */
+ protected CompilationResult.CompilationMessage convertMessage(Message message) {
+ if (message instanceof SimpleMessage) {
+ SimpleMessage simpleMessage = (SimpleMessage) message;
+ return new CompilationResult.CompilationMessage(-1, simpleMessage.getMessage());
+ } else if (message instanceof SyntaxErrorMessage) {
+ SyntaxErrorMessage syntaxErrorMessage = (SyntaxErrorMessage) message;
+ return new CompilationResult.CompilationMessage(
+ syntaxErrorMessage.getCause().getLine(), syntaxErrorMessage.getCause().getMessage());
+ } else {
+ if (_logger.isLoggable(Level.FINE)) {
+ _logger.log(Level.FINE,
+ "This compiler came across an unknown message kind ['{0}']. It will be ignored.", message);
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * <p>Configures the compiler by building its configuration object.</p>
+ *
+ * @param sourcePath the path to the source directory
+ * @param targetPath the path to the target directory
+ * @param classLoader the class loader to use to determine the classpath
+ * @return the compiler configuration
+ */
+ protected CompilerConfiguration buildCompilerConfiguration(File sourcePath, File targetPath, ClassLoader classLoader) {
+ CompilerConfiguration configuration = new CompilerConfiguration();
+
+ // Set the destination / target directory for the compiled .class files.
+ configuration.setTargetDirectory(targetPath.getAbsoluteFile());
+
+ // Specify the classpath of the given class loader. This enables the user to write new Java
+ // "scripts" that depend on classes that have already been loaded previously. Otherwise he
+ // wouldn't be able to use for example classes that are available in a library.
+ configuration.setClasspath(ClassLoaderUtils.buildClasspath(classLoader));
+
+ // Enable verbose output.
+ configuration.setVerbose(true);
+
+ // Generate debugging information.
+ configuration.setDebug(true);
+
+ return configuration;
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/compiler/JSR199Compiler.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/compiler/JSR199Compiler.java
new file mode 100644
index 0000000..f02489a
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/compiler/JSR199Compiler.java
@@ -0,0 +1,220 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.compiler;
+
+import org.apache.myfaces.extensions.scripting.core.api.Configuration;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.FileUtils;
+import org.apache.myfaces.extensions.scripting.core.engine.api.CompilationException;
+import org.apache.myfaces.extensions.scripting.core.engine.api.CompilationResult;
+
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticCollector;
+import javax.tools.JavaFileObject;
+import javax.tools.ToolProvider;
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static org.apache.myfaces.extensions.scripting.core.api.ScriptingConst.ENGINE_TYPE_JSF_JAVA;
+import static org.apache.myfaces.extensions.scripting.core.engine.api.CompilerConst.*;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * a JSR 199 based compiler which implements
+ * our simplified compiler interface
+ */
+public class JSR199Compiler implements org.apache.myfaces.extensions.scripting.core.engine.api.Compiler
+{
+
+ javax.tools.JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
+ ContainerFileManager fileManager = null;
+
+
+ public JSR199Compiler()
+ {
+ super();
+ }
+
+ /**
+ * compile all files starting from a given root
+ * <p/>
+ * note, the java compiler interface does not allow per se
+ * wildcards due to its file object indirection
+ * we deal with that problem by determine all files manually and then
+ * push the list into the jsr compiler interface
+ *
+ *
+ * @param sourceRoot the root for all java sources to be compiled
+ * @param loader the classpath holder for the compilation
+ * @return the collected compilation results as bundle
+ */
+ public CompilationResult compile(File sourceRoot, File destination, ClassLoader loader) {
+ WeavingContext context = WeavingContext.getInstance();
+ Configuration configuration = context.getConfiguration();
+ destination.mkdirs();
+ fileManager = new ContainerFileManager(javaCompiler.getStandardFileManager(new DiagnosticCollector<JavaFileObject>(), null, null));
+
+ DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
+
+ getLog().info("[EXT-SCRIPTING] Doing a full recompile");
+
+ List<File> sourceFiles = FileUtils.fetchSourceFiles(configuration.getWhitelistedSourceDirs
+ (ENGINE_TYPE_JSF_JAVA), JAVA_WILDCARD);
+
+ for (File sourceFile : sourceFiles)
+ {
+ if (!sourceFile.exists())
+ {
+ getLog().log(Level.WARNING, "[EXT-SCRIPTING] Source file with path {0} does not exist it might cause an error in the compilation process", sourceFile.getAbsolutePath());
+ }
+ }
+ Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects(sourceFiles.toArray(new File[sourceFiles.size()]));
+ String[] options = new String[]{JC_CLASSPATH, fileManager.getClassPath(), JC_TARGET_PATH,
+ destination.getAbsolutePath(), JC_SOURCEPATH,
+ sourceRoot.getAbsolutePath(), JC_DEBUG};
+ javaCompiler.getTask(null, fileManager, diagnosticCollector, Arrays.asList(options), null, fileObjects).call();
+
+
+ CompilationResult result = handleDiagnostics(diagnosticCollector);
+
+
+ return result;
+
+ }
+
+ /**
+ * internal diagnostics handler
+ * which just logs the errors
+ *
+ * @param diagnosticCollector the compilation results, the jsr 199 uses a DiagnosticsCollector object
+ * to keep the errors and warnings of the compiler
+ * @throws ClassNotFoundException in case of an error (this is enforced by the compiler interface
+ * and probably will be overhauled in the long run)
+ */
+ private CompilationResult handleDiagnostics(DiagnosticCollector<JavaFileObject> diagnosticCollector)
+ {
+ if (diagnosticCollector.getDiagnostics().size() > 0)
+ {
+ Logger log = Logger.getLogger(this.getClass().getName());
+ StringBuilder errors = new StringBuilder();
+ CompilationResult result = new CompilationResult("");
+ boolean hasError = false;
+ for (Diagnostic diagnostic : diagnosticCollector.getDiagnostics())
+ {
+ String error = createErrorMessage(diagnostic);
+ log.log(Level.WARNING, "[EXT-SCRIPTING] Compiler: {0}", error);
+
+ if (diagnostic.getKind().equals(Diagnostic.Kind.ERROR))
+ {
+ hasError = true;
+ result.getErrors().add(new CompilationResult.CompilationMessage(diagnostic.getLineNumber(), diagnostic.getMessage(Locale.getDefault())));
+ } else
+ {
+ result.getWarnings().add(new CompilationResult.CompilationMessage(diagnostic.getLineNumber(), diagnostic.getMessage(Locale.getDefault())));
+ }
+ errors.append(error);
+
+ }
+ return result;
+ } else
+ {
+ //WeavingContext.setCompilationResult(ENGINE_TYPE_JSF_JAVA, new CompilationResult(""));
+ return new CompilationResult("");
+ }
+ }
+
+ /**
+ * interruption of the compile flow should only
+ * happen if an error has occurred otherwise we will proceed
+ * as expected
+ *
+ * @param errors the errors messages found
+ * @param hasError marker if an error was found or not
+ * @throws ClassNotFoundException in case of a compile error
+ */
+ private void assertErrorFound(StringBuilder errors, boolean hasError) throws ClassNotFoundException
+ {
+ if (hasError)
+ {
+ throw new ClassNotFoundException("Compile error of java file:" + errors.toString());
+ }
+ }
+
+ /**
+ * creates a standardized error message
+ *
+ * @param diagnostic the diagnostic of the compiler containing the error data
+ * @return a formatted string with the standardized error message which then later
+ * can be processed by the user
+ */
+ private String createErrorMessage(Diagnostic diagnostic)
+ {
+ StringBuilder retVal = new StringBuilder(256);
+ if (diagnostic == null)
+ {
+ return retVal.toString();
+ }
+ if (diagnostic.getKind().equals(Diagnostic.Kind.ERROR))
+ {
+ retVal.append(STD_ERROR_HEAD);
+ } else if (diagnostic.getKind().equals(Diagnostic.Kind.NOTE))
+ {
+ retVal.append(STD_NOTE_HEAD);
+ } else if (diagnostic.getKind().equals(Diagnostic.Kind.WARNING))
+ {
+ retVal.append(STD_WARN_HEAD);
+ } else if (diagnostic.getKind().equals(Diagnostic.Kind.MANDATORY_WARNING))
+ {
+ retVal.append(STD_MANDATORY_WARN_HEAD);
+ } else if (diagnostic.getKind().equals(Diagnostic.Kind.OTHER))
+ {
+ retVal.append(STD_OTHER_HEAD);
+ }
+ String message = diagnostic.getMessage(Locale.getDefault());
+ message = (message == null) ? "" : message;
+ retVal.append(message);
+ retVal.append(diagnostic.getLineNumber());
+
+ retVal.append("\n\n");
+
+ String source = "No additional source info";
+
+ if (diagnostic.getSource() != null)
+ {
+ source = diagnostic.getSource().toString();
+ }
+ retVal.append(source);
+
+ return retVal.toString();
+ }
+
+ private Logger getLog()
+ {
+ return Logger.getLogger(this.getClass().getName());
+ }
+
+}
\ No newline at end of file
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/StandardDependencyScanner.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/StandardDependencyScanner.java
new file mode 100644
index 0000000..d4860c9
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/StandardDependencyScanner.java
@@ -0,0 +1,146 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan;
+
+import org.objectweb.asm.ClassReader;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.DependencyRegistry;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.DependencyScanner;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.core.ClassScanUtils;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.core.ClassScanVisitor;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.core.ExtendedClassReader;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A dependency scanner for
+ * our classes. This class is thread save on object level
+ * and can be used as a singleton
+ * <p/>
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+public class StandardDependencyScanner implements DependencyScanner
+{
+ final ClassScanVisitor _cp = new ClassScanVisitor();
+ Logger _logger = Logger.getLogger(this.getClass().getName());
+
+ public StandardDependencyScanner() {
+
+ }
+
+ public synchronized final void fetchDependencies(ClassLoader loader, Integer engineType, String className, DependencyRegistry registry) {
+ _cp.setEngineType(engineType);
+ _cp.setRootClass(className);
+ _cp.setDependencyRegistry(registry);
+ investigateInheritanceHierarchy(loader, className);
+ registry.flush(engineType);
+ }
+
+ /**
+ * this investigates the classes inheritance hierarchy for
+ * more dependencies, for now annotations and interfaces
+ * are omitted since they are not vital to our jsf dependency checks
+ * (maybe in the long run we will add interfaces and annotations as well
+ * but for now we will leave them away for speed reasons)
+ *
+ * @param loader the classLoader which should be used for the hierarchy scanning
+ * @param className the className which has to be investigated
+ */
+ private void investigateInheritanceHierarchy(ClassLoader loader, String className) {
+ //we now have to fetch the parent hierarchy
+
+ try {
+ Class toCheck = loader.loadClass(className);
+ if (toCheck == null) {
+ return;
+ }
+ scanCurrentClass(loader, className);
+
+ //we scan the hierarchy because we might have compiled-uncompiled-compiled connections, the same goes for the interfaces
+ //the basic stuff can be covered by our class scanning but for more advanced usecase we have to walk the entire hierarchy per class!
+ scanHierarchy(loader, toCheck);
+ //our asm code normally covers this but since the scanner has to work outside of asm we do it twice, the same goes for the hierarchy
+ scanInterfaces(loader, toCheck);
+ } catch (ClassNotFoundException e) {
+ _logger.log(Level.SEVERE, "DefaultDependencyScanner.investigateInheritanceHierarchy() ", e);
+ }
+ }
+
+ private void scanInterfaces(ClassLoader loader, Class toCheck) {
+ Class[] interfaces = toCheck.getInterfaces();
+ if (interfaces == null || interfaces.length == 0) {
+ return;
+ }
+
+ for (Class currentInterface : interfaces) {
+ if (ClassScanUtils.isStandardNamespace(currentInterface.getName())) {
+ continue;
+ }
+ scanCurrentClass(loader, currentInterface.getName());
+
+ //We scan also our parent interfaces to get a full coverage
+ //but since interfaces do not implement anything we can cover
+ //the parents
+ scanHierarchy(loader, currentInterface);
+ }
+ }
+
+ /**
+ * scans the parent child relationship hierarchy
+ * We have to go through the entire hierarchy except for standard
+ * namespaces due to the fact that we have to cover source <->binary<->source
+ * dependencies with binary being binary classes never to be refreshed
+ * <p/>
+ * Note we can optionally do some interface checks here
+ * for now annotations are only processed by the class scanner itself
+ * so we do not process any annotation inheritance on this level
+ * we will add the feature later
+ *
+ * @param loader the infrastructural classloader
+ * @param toCheck the class which needs to be checked
+ */
+ private void scanHierarchy(ClassLoader loader, Class toCheck) {
+ Class parent = toCheck.getSuperclass();
+
+ while (parent != null && !ClassScanUtils.isStandardNamespace(parent.getName())) {
+ scanCurrentClass(loader, parent.getName());
+ parent = parent.getSuperclass();
+ }
+ }
+
+ /**
+ * scans one level of the inheritance hierarchy
+ *
+ * @param loader the classLoader which should be used for the hierarchy scanning
+ * @param currentClassName the className which has to be investigated
+ */
+ private void scanCurrentClass(ClassLoader loader, String currentClassName) {
+ ClassReader cr;
+ try {
+ cr = new ExtendedClassReader(loader, currentClassName);
+ cr.accept(_cp, 0);
+ } catch (IOException e) {
+ _logger.log(Level.SEVERE, "scanCurrentClass() ", e);
+ }
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/api/ClassFilter.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/api/ClassFilter.java
new file mode 100644
index 0000000..2bfdb7c
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/api/ClassFilter.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api;
+
+/**
+ * Generic filter pattern interface
+ * used by our dependency registry to pre-filter the classes
+ */
+public interface ClassFilter {
+
+ /**
+ * checks whether the class is allowed to be processed by the filter or not
+ *
+ * @param engineType integer value of the engine type of the class
+ * @param clazz the class itself to be processed by the filter
+ * @return true if it is allowed to be processed false otherwise
+ */
+ public boolean isAllowed(Integer engineType, String clazz);
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/api/DependencyRegistry.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/api/DependencyRegistry.java
new file mode 100644
index 0000000..ef858ad
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/api/DependencyRegistry.java
@@ -0,0 +1,50 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api;
+
+/**
+ * General contractual interface for a dependency registry
+ * The dependency registry is a class which stores dependencies
+ * according to an internal whitelisting system.
+ * <p/>
+ * Only classes which pass the whitelisting check will be processed
+ */
+public interface DependencyRegistry {
+ /**
+ * adds a source dependency if it is able to pass the
+ * filters
+ * A dependency is only allowed to pass if it is able
+ * to pass the internal filter list
+ *
+ * @param engineType the engine type for this dependency
+ * @param rootClass the root class of this scan which all dependencies are referenced from
+ * @param currentlyVisitedClass the source which includes or casts the dependencies
+ * @param dependency the dependency to be added
+ */
+ void addDependency(Integer engineType, String rootClass, String currentlyVisitedClass, String dependency);
+
+ /**
+ * Flush which is issued at the end of processing to flush
+ * any content which has not been yet processed into our content holding
+ * data structures
+ *
+ * @param engineType the engine type which has issued the flush operation
+ */
+ void flush(Integer engineType);
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/api/DependencyScanner.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/api/DependencyScanner.java
new file mode 100644
index 0000000..6d4c50e
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/api/DependencyScanner.java
@@ -0,0 +1,37 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api;
+
+/**
+ * Standard dependency scanner interface
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+public interface DependencyScanner {
+ /**
+ * main method every dependency scanner has to implement
+ *
+ * @param loader the classloader which is able to serve the requested class resources
+ * @param engineType integer value of the scanning triggering engine type
+ * @param className of the class to be scanned
+ * @param registry the registry which should receive the results of the scan
+ */
+ public void fetchDependencies(ClassLoader loader, Integer engineType, String className, DependencyRegistry registry);
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/ClassDependencies.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/ClassDependencies.java
new file mode 100644
index 0000000..bcc9516
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/ClassDependencies.java
@@ -0,0 +1,102 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.core;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * class dependency maps
+ * note this class is thread save
+ */
+public class ClassDependencies {
+
+ /**
+ * reverse index which shows which
+ * a class name and which classes in the system depend on that
+ * classname
+ * <p/>
+ * <p/>
+ * the key is a dependency a class has the _value is a set of classes which depend on the current class
+ */
+ private Map<String, Set<String>> reverseIndex = new ConcurrentHashMap<String, Set<String>>();
+
+ public void addDependency(String referencingClass, String referencedClass) {
+ Set<String> reverseDependencies = getReverseDependencies(referencedClass);
+ reverseDependencies.add(referencingClass);
+ }
+
+ /**
+ * adds a set of dependencies to the
+ * reverse lookup index
+ *
+ * @param referencingClass the referencing class of this dependency
+ * @param referencedClasses the referenced class of this dependency
+ */
+ public void addDependencies(String referencingClass, Collection<String> referencedClasses) {
+ for (String referencedClass : referencedClasses) {
+ addDependency(referencingClass, referencedClass);
+ }
+ }
+
+ /**
+ * removes a referenced class an all its referencing classes!
+ *
+ * @param clazz the referenced class to be deleted
+ */
+ public void removeReferenced(String clazz) {
+ reverseIndex.remove(clazz);
+ }
+
+ /**
+ * removes a referencing class
+ * and deletes the referenced
+ * entry if it is not referenced anymore
+ *
+ * @param clazz the referencing class to delete
+ */
+ @SuppressWarnings("unused")
+ public void removeReferrer(String clazz) {
+ List<String> emptyReferences = new ArrayList<String>(reverseIndex.size());
+ for (Map.Entry<String, Set<String>> entry : reverseIndex.entrySet()) {
+ Set<String> entrySet = entry.getValue();
+ entrySet.remove(clazz);
+ if (entrySet.isEmpty()) {
+ emptyReferences.add(entry.getKey());
+ }
+ }
+ for (String toDelete : emptyReferences) {
+ removeReferenced(toDelete);
+ }
+ }
+
+ public Set<String> getReferringClasses(String referencedClass) {
+ return reverseIndex.get(referencedClass);
+ }
+
+ private Set<String> getReverseDependencies(String dependency) {
+ Set<String> dependencies = reverseIndex.get(dependency);
+ if (dependencies == null) {
+ dependencies = Collections.synchronizedSet(new HashSet<String>());
+ reverseIndex.put(dependency, dependencies);
+ }
+ return dependencies;
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/ClassScanUtils.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/ClassScanUtils.java
new file mode 100644
index 0000000..f9f43d7
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/ClassScanUtils.java
@@ -0,0 +1,78 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.core;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * Utils which store the shared code
+ */
+public class ClassScanUtils {
+
+ private static final String DOMAIN_JAVA = "java.";
+ private static final String DOMAIN_JAVAX = "javax.";
+ private static final String DOMAIN_COM_SUN = "com.sun";
+ private static final String DOMAIN_APACHE = "org.apache.";
+ private static final String DOMAIN_MYFACES = "org.apache.myfaces";
+ private static final String DOMAIN_JBOSS = "org.jboss";
+ private static final String DOMAIN_SPRING = "org.springframework";
+ private static final String DOMAIN_JUNIT = "org.junit";
+ private static final String DOMAIN_ECLIPSE = "org.eclipse";
+ private static final String DOMAIN_NETBEANS = "org.netbeans";
+ private static final String DOMAIN_GROOVY = "groovy.";
+ private static final String DOMAIN_SCALA = "scala.";
+ private static final String DOMAIN_JYTHON = "jython.";
+ private static final String DOMAIN_JRUBY = "jruby.";
+
+ /**
+ * checks if a given package or class
+ * belongs to a standard namespaces which is
+ * untouchable by an implementer
+ *
+ * @param in the page or fully qualified classname
+ * @return true if it belongs to one of the standard namespaces, false if not
+ */
+ public static boolean isStandardNamespace(String in) {
+ //We don't use a regexp here, because an test has shown that direct startsWith is 5 times as fast as applying
+ //a precompiled regexp with match
+
+ //shortcuts for a faster killing of the add before going into the heavier
+ //whitelist check, this one kills off classes which belong to standard
+ //and semi standard namespaces before whitelisting the rest
+ return in.startsWith(DOMAIN_JAVA) ||
+ in.startsWith(DOMAIN_JAVAX) ||
+ in.startsWith(DOMAIN_COM_SUN) ||
+ in.startsWith(DOMAIN_GROOVY) ||
+ in.startsWith(DOMAIN_JYTHON) ||
+ in.startsWith(DOMAIN_JRUBY) ||
+ in.startsWith(DOMAIN_SCALA) ||
+ in.startsWith(DOMAIN_JBOSS) ||
+ in.startsWith(DOMAIN_SPRING) ||
+ in.startsWith(DOMAIN_JUNIT) ||
+ in.startsWith(DOMAIN_ECLIPSE) ||
+ in.startsWith(DOMAIN_NETBEANS) ||
+
+ //apache domain has to be treated specially myfaces can be referenced due to our tests and demos, otherwise this one
+ //is also treated as taboo zone
+ ((in.startsWith(DOMAIN_APACHE) &&
+ !in.startsWith(DOMAIN_MYFACES)));
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/ClassScanVisitor.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/ClassScanVisitor.java
new file mode 100644
index 0000000..d3048b5
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/ClassScanVisitor.java
@@ -0,0 +1,166 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.core;
+
+import org.objectweb.asm.*;
+import org.objectweb.asm.signature.SignatureReader;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.DependencyRegistry;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.registry.ExternalFilterDependencyRegistry;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.registry.ExternalFilterDependencyRegistry;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The central visitor for the class scanner. ASM uses a visitor interface for high performance
+ * to step through classes.
+ * <p/>
+ * We reuse this pattern to get the best performance possible in this critical part of the application
+ * which also is triggered by the startup process.
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+public class ClassScanVisitor implements ClassVisitor {
+
+ DependencyRegistry _dependencyRegistry;
+ String _currentlyVistedClass;
+ Integer _engineType;
+ String _rootClass;
+ static final Logger _log = Logger.getLogger(ClassScanVisitor.class.getName());
+
+ public ClassScanVisitor() {
+ }
+
+ public ClassScanVisitor(Integer engineType, String rootClass, ExternalFilterDependencyRegistry registry) {
+ _dependencyRegistry = registry;
+ _engineType = engineType;
+ _rootClass = rootClass;
+ }
+
+ public void visit(int version, int access, String name,
+ String signature, String superName, String[] interfaces) {
+ _currentlyVistedClass = Type.getObjectType(name).getClassName();
+ if (superName != null)
+ registerDependency(Type.getObjectType(superName));
+
+ handleGenerics(signature, true);
+
+ if (interfaces != null && interfaces.length > 0) {
+ for (String currInterface : interfaces) {
+ if (currInterface != null)
+ registerDependency(Type.getObjectType(currInterface));
+ }
+ }
+ }
+
+ public void visitSource(String source, String debug) {
+ _log.log(Level.FINEST, "visitSource: {0}", source);
+ }
+
+ public void visitOuterClass(String owner, String name, String description) {
+ //nothing has to be done here I guess because
+ //we only try to fetch the dependencies
+ _log.log(Level.FINEST, "visitOuterClass: {0} {1} {2}", new String[]{owner, name, description});
+
+ }
+
+ public AnnotationVisitor visitAnnotation(String description,
+ boolean visible) {
+ if (description != null)
+ registerDependency(Type.getType(description));
+
+ return null;
+ }
+
+ public void visitAttribute(Attribute attribute) {
+ }
+
+ public void visitInnerClass(String name, String outerName,
+ String innerName, int access) {
+ //same as outer class
+ _log.log(Level.FINEST, "visitInnerClass: {0} {1} {2} ", new String[]{name, outerName, innerName});
+ }
+
+ public FieldVisitor visitField(int access, String name, String description,
+ String signature, Object value) {
+ //_log._log(Level.INFO, "Field:{0} {1} ", new Object[]{description, name});
+ handleGenerics(signature, false);
+ if (description != null)
+ registerDependency(Type.getType(description));
+
+ return null;
+ }
+
+ private void registerDependency(Type dependency) {
+ String className = dependency.getClassName();
+ if (className.endsWith("[]")) {
+ className = className.substring(0, className.indexOf("["));
+ }
+
+ if (_dependencyRegistry != null) {
+ _dependencyRegistry.addDependency(_engineType, _rootClass, _currentlyVistedClass, className);
+ }
+
+ }
+
+ public MethodVisitor visitMethod(int access, String name,
+ String description, String signature, String[] exceptions) {
+
+ if (description != null)
+ registerDependency(Type.getReturnType(description));
+
+ handleGenerics(signature, true);
+
+ if (description != null) {
+ for (Type argumentType : Type.getArgumentTypes(description)) {
+ registerDependency(argumentType);
+ }
+ }
+ return new MethodScanVisitor(_engineType, _rootClass, _currentlyVistedClass, _dependencyRegistry);
+ }
+
+ private void handleGenerics(String signature, boolean accept) {
+ if (signature != null && signature.contains("<")) {
+ SignatureReader reader = new SignatureReader(signature);
+ if (accept)
+ reader.accept(new DependencySignatureVisitor(_dependencyRegistry, _engineType, _rootClass, _currentlyVistedClass));
+ else
+ reader.acceptType(new DependencySignatureVisitor(_dependencyRegistry, _engineType, _rootClass, _currentlyVistedClass));
+ }
+ }
+
+ public void visitEnd() {
+ //_log.info("}");
+ }
+
+ public void setDependencyRegistry(DependencyRegistry dependencyRegistry) {
+ _dependencyRegistry = dependencyRegistry;
+ }
+
+ public void setEngineType(Integer engineType) {
+ _engineType = engineType;
+ }
+
+ public void setRootClass(String rootClass) {
+ _rootClass = rootClass;
+ }
+}
+
+
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/DependencySignatureVisitor.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/DependencySignatureVisitor.java
new file mode 100644
index 0000000..c445ca8
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/DependencySignatureVisitor.java
@@ -0,0 +1,122 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.core;
+
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureVisitor;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.DependencyRegistry;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * We need the signature visitor to get a grip on generics
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class DependencySignatureVisitor implements SignatureVisitor {
+
+ static final Logger _log = Logger.getLogger(DependencySignatureVisitor.class.getName());
+
+ String _rootClass;
+ String _currentClass;
+ DependencyRegistry _registry;
+ Integer _engineType;
+
+ public DependencySignatureVisitor(DependencyRegistry registry, Integer engineType, String currentClass, String rootClass) {
+ _registry = registry;
+ _currentClass = currentClass;
+ _rootClass = rootClass;
+ _engineType = engineType;
+ }
+
+ public void visitFormalTypeParameter(String className) {
+ if (_log.isLoggable(Level.FINEST))
+ _log.log(Level.FINEST, "visitFormalTypeParameter: {0}", className);
+ //the information is lacking the package information on this level no fully qualified name here
+ // _registry.addDependency(_engineType, _rootClass, _currentClass, Type.getObjectType(className).getClassName());
+ }
+
+ public SignatureVisitor visitClassBound() {
+ return this;
+ }
+
+ public SignatureVisitor visitInterfaceBound() {
+ return this;
+ }
+
+ public SignatureVisitor visitSuperclass() {
+ return this;
+ }
+
+ public SignatureVisitor visitInterface() {
+ return this;
+ }
+
+ public SignatureVisitor visitParameterType() {
+ return this;
+ }
+
+ public SignatureVisitor visitReturnType() {
+ return this;
+ }
+
+ public SignatureVisitor visitExceptionType() {
+ return this;
+ }
+
+ public void visitBaseType(char c) {
+
+ }
+
+ public void visitTypeVariable(String className) {
+ if (_log.isLoggable(Level.FINEST))
+ _log.log(Level.FINEST, "visitTypeVariable: {0}", className);
+ }
+
+ public SignatureVisitor visitArrayType() {
+ return this;
+ }
+
+ public void visitClassType(String className) {
+ if (_log.isLoggable(Level.FINEST))
+ _log.log(Level.FINEST, "visitClassType: {0}", className);
+ _registry.addDependency(_engineType, _rootClass, _currentClass, Type.getObjectType(className).getClassName());
+ }
+
+ public void visitInnerClassType(String className) {
+ if (_log.isLoggable(Level.FINEST))
+ _log.log(Level.FINEST, "visitInnerClassType: {0}", className);
+ }
+
+ public void visitTypeArgument() {
+
+ }
+
+ public SignatureVisitor visitTypeArgument(char c) {
+ return this;
+ }
+
+ public void visitEnd() {
+
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/ExtendedClassReader.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/ExtendedClassReader.java
new file mode 100644
index 0000000..f8b9081
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/ExtendedClassReader.java
@@ -0,0 +1,44 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.core;
+
+import org.objectweb.asm.ClassReader;
+
+import java.io.IOException;
+
+/**
+ * Class reader for ASM which allows to plug our own loader instead
+ * of the default one
+ * <p/>
+ * (ASM makes too many assumptions regarding the loader)
+ */
+public class ExtendedClassReader extends ClassReader {
+ /**
+ * classloader pluggable classreader
+ *
+ * @param loader the loader which has to be plugged into the system
+ * @param className the class name for the class which has to be investigated
+ * @throws java.io.IOException in case of a loading error (class cannot be loaded for whatever reason)
+ */
+ public ExtendedClassReader(ClassLoader loader, String className) throws IOException {
+ super(loader.getResourceAsStream(className.replace('.', '/')
+ + ".class"));
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/MethodScanVisitor.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/MethodScanVisitor.java
new file mode 100644
index 0000000..a172509
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/core/MethodScanVisitor.java
@@ -0,0 +1,233 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.core;
+
+import org.objectweb.asm.*;
+import org.objectweb.asm.signature.SignatureReader;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.DependencyRegistry;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A scan visitor on method level
+ * to cope with method scoped dependencies like inlined
+ * fully qualified names, annotations, local variables
+ * etc...
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+class MethodScanVisitor implements MethodVisitor {
+
+ // static final Logger log = Logger.getLogger("ClassScanVisitor");
+
+ String _currentlyVisitedClass = null;
+ String _rootClass;
+ Integer _engineType = null;
+ DependencyRegistry _dependencyRegistry = null;
+
+ static Logger _log = Logger.getLogger(MethodScanVisitor.class.getName());
+
+ public MethodScanVisitor(Integer engineType, String rootClass, String currentlyVisitedClass, DependencyRegistry registry) {
+ _currentlyVisitedClass = currentlyVisitedClass;
+ _dependencyRegistry = registry;
+ _engineType = engineType;
+ _rootClass = rootClass;
+ }
+
+ public AnnotationVisitor visitAnnotationDefault() {
+ return null;
+ }
+
+ public AnnotationVisitor visitAnnotation(String description, boolean b) {
+ if(description != null) {
+ registerDependency(Type.getType(description));
+ }
+ return null;
+ }
+
+ public AnnotationVisitor visitParameterAnnotation(int opCode, String description, boolean b) {
+ if(description != null) {
+ registerDependency(Type.getType(description));
+ }
+ return null;
+ }
+
+ public void visitAttribute(Attribute attribute) {
+ if (_log.isLoggable(Level.FINEST))
+ _log.log(Level.FINEST, "visitAttribute {0}", attribute.type);
+ }
+
+ public void visitCode() {
+ //log.log(Level.INFO, "Method code");
+ }
+
+ public void visitFrame(int opCode1, int opCode2, Object[] objects, int opCode3, Object[] objects1) {
+ if (_log.isLoggable(Level.FINEST))
+ _log.log(Level.FINEST, "visitFrame {0}", "");
+
+ }
+
+ public void visitInsn(int opCode) {
+ }
+
+ public void visitIntInsn(int opCode1, int opCode2) {
+ }
+
+ public void visitVarInsn(int opCode1, int opCode2) {
+ }
+
+ public void visitTypeInsn(int opCode, String castType) {
+ //cast
+ // log.log(Level.INFO, "TypeInsn: {0} ", new String[]{castType});
+ if (castType != null) {
+ registerDependency(Type.getObjectType(castType));
+ if (_log.isLoggable(Level.FINEST))
+ _log.log(Level.FINEST, "visitTypeInsn {0}", castType);
+ }
+ }
+
+ private void registerDependency(Type dependency) {
+
+ String className = dependency.getClassName();
+ if (className.endsWith("[]")) {
+ className = className.substring(0, className.indexOf("["));
+ }
+
+ if (_dependencyRegistry != null) {
+ _dependencyRegistry.addDependency(_engineType, _rootClass, _currentlyVisitedClass, className);
+ }
+ }
+
+ /**
+ * @param opCode the opCode of the insert statement
+ * @param owner hosting classname of field (always the calling class afaik)
+ * @param name internal descriptor
+ * @param descriptor field type
+ */
+ public void visitFieldInsn(int opCode, String owner, String name, String descriptor) {
+ // log.log(Level.INFO, "visitFieldInsn {0} {1} {2}", new Object[]{owner, name, descriptor});
+ //we have to deal with static imports as special case of field insertions
+ if (name != null && name.length() > 6 && name.startsWith("class$")) {
+ //special fallback for groovy static imports which are added as fields
+ name = "L" + name.substring(6).replaceAll("\\$", ".") + ";";
+ registerDependency(Type.getType(name));
+ }
+ if (descriptor != null) {
+ registerDependency(Type.getType(descriptor));
+ }
+
+ if (_log.isLoggable(Level.FINEST))
+ _log.log(Level.FINEST, "visitFieldInsn {0}", descriptor);
+
+ }
+
+ /**
+ * Method call
+ *
+ * @param opc internal opcode
+ * @param owner hosting classname of the method
+ * @param name method name
+ * @param desc descriptor string
+ */
+ public void visitMethodInsn(int opc, String owner, String name, String desc) {
+ //s2 arguments list
+ if (desc != null) {
+ registerDependency(Type.getReturnType(desc));
+ Type[] argumentTypes = Type.getArgumentTypes(desc);
+ if (argumentTypes != null) {
+ for (Type argumentType : argumentTypes) {
+ registerDependency(argumentType);
+ }
+ }
+ }
+
+ if (owner != null)
+ registerDependency(Type.getObjectType(owner));
+
+ }
+
+ public void visitJumpInsn(int i, Label label) {
+
+ }
+
+ public void visitLabel(Label label) {
+
+ }
+
+ public void visitLdcInsn(Object o) {
+
+ }
+
+ public void visitIincInsn(int i, int i1) {
+
+ }
+
+ public void visitTableSwitchInsn(int i, int i1, Label label, Label[] labels) {
+
+ }
+
+ public void visitLookupSwitchInsn(Label label, int[] ints, Label[] labels) {
+
+ }
+
+ public void visitMultiANewArrayInsn(String s, int i) {
+ if (_log.isLoggable(Level.FINEST))
+ _log.log(Level.FINEST, "visitMultiANewArrayInsn {0}", s);
+ }
+
+ public void visitTryCatchBlock(Label label, Label label1, Label label2, String catchType) {
+ //try catch block type information in the last string
+ //log.log(Level.INFO, "visitTryCatchBlock: {0} {1} {2} {3}", new Object[]{label.toString(), label1.toString(), label2.toString(), catchType});
+ if (catchType != null) {
+ registerDependency(Type.getObjectType(catchType));
+ }
+ }
+
+ public void visitLocalVariable(String name, String description, String signature, Label label, Label label1, int i) {
+ //local variable on method level
+ if (description != null) {
+ registerDependency(Type.getType(description));
+ }
+ if (signature != null) {
+ handleGenerics(signature);
+ }
+ }
+
+ public void visitLineNumber(int i, Label label) {
+
+ }
+
+ public void visitMaxs(int i, int i1) {
+
+ }
+
+ public void visitEnd() {
+
+ }
+
+ private void handleGenerics(String signature) {
+ if (signature != null && signature.contains("<")) {
+ SignatureReader reader = new SignatureReader(signature);
+ reader.acceptType(new DependencySignatureVisitor(_dependencyRegistry, _engineType, _rootClass, _currentlyVisitedClass));
+ }
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/filter/ScanIdentifierFilter.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/filter/ScanIdentifierFilter.java
new file mode 100644
index 0000000..03be5b2
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/filter/ScanIdentifierFilter.java
@@ -0,0 +1,47 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.filter;
+
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.ClassFilter;
+
+import java.util.Arrays;
+
+/**
+ * a filter which works on the scan identifiers
+ * only classes which trigger on the same identifier
+ * are allowed to be passed through
+ */
+public class ScanIdentifierFilter implements ClassFilter
+{
+
+ private final int [] _engineType;
+
+ public ScanIdentifierFilter(int ... engineType) {
+ _engineType = Arrays.copyOf(engineType, engineType.length);
+ }
+
+ public boolean isAllowed(Integer identifier, String clazz) {
+ int id = identifier;
+ for(int engineType: _engineType) {
+ boolean allowed = engineType == id;
+ if(allowed) return true;
+ }
+ return false;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/filter/StandardNamespaceFilter.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/filter/StandardNamespaceFilter.java
new file mode 100644
index 0000000..6fc6724
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/filter/StandardNamespaceFilter.java
@@ -0,0 +1,40 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.filter;
+
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.ClassFilter;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.core.ClassScanUtils;
+
+/**
+ * Filter facade for our standard namespace check
+ */
+public class StandardNamespaceFilter implements ClassFilter
+{
+
+ /**
+ * is allowed implementation for our standard namespace filter
+ *
+ * @param engineType integer value of the engine type of the class
+ * @param clazz the class itself to be processed by the filter
+ * @return true if it is not in the standard namespaces false otherwise
+ */
+ public final boolean isAllowed(Integer engineType, String clazz) {
+ return !ClassScanUtils.isStandardNamespace(clazz);
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/filter/WhitelistFilter.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/filter/WhitelistFilter.java
new file mode 100644
index 0000000..6087ad9
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/filter/WhitelistFilter.java
@@ -0,0 +1,130 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.filter;
+
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.ClassFilter;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Filter class which depends upon a list of whitelisted packages
+ * wildcards in this filter are implicit which means
+ * <p/>
+ * org.apache.myfaces includes all files
+ * under org.apache.myfaces
+ */
+public class WhitelistFilter implements ClassFilter
+{
+
+ WhiteListNode _whiteList = new WhiteListNode();
+
+ /*we use a package tree here to make the whitelist check as performant as possible*/
+
+ class WhiteListNode {
+ Map<String, WhiteListNode> _value = new ConcurrentHashMap<String, WhiteListNode>();
+
+ public WhiteListNode addEntry(String key) {
+ if (_value.containsKey(key)) {
+ return _value.get(key);
+ }
+ WhiteListNode retVal = new WhiteListNode();
+ _value.put(key, retVal);
+ return retVal;
+ }
+
+ public boolean hasChildren() {
+ return !_value.isEmpty();
+ }
+
+ public Map<String, WhiteListNode> getValue() {
+ return _value;
+ }
+
+ public void setValue(Map<String, WhiteListNode> value) {
+ this._value = value;
+ }
+
+ public WhiteListNode get(String key) {
+ return _value.get(key);
+ }
+ }
+
+ public WhitelistFilter(String... whiteList) {
+ for (String singlePackage : whiteList) {
+ addEntry(singlePackage);
+ }
+ }
+
+ public WhitelistFilter(Collection<String> whiteList) {
+ for (String singlePackage : whiteList) {
+ addEntry(singlePackage);
+ }
+ }
+
+ /**
+ * whitespace is allowed implementation
+ *
+ * @param engineType integer value of the engine type of the class
+ * @param clazz the class itself to be processed by the filter
+ * @return true if it is white-spaced, false otherwise
+ */
+ public final boolean isAllowed(Integer engineType, String clazz) {
+ String[] subParts = clazz.split("\\.");
+ WhiteListNode currPackage = _whiteList;
+ WhiteListNode parentPackage = null;
+ for (String subPart : subParts) {
+ currPackage = currPackage.get(subPart);
+ if (isRootPackageMismatch(currPackage, parentPackage)) {
+ return false;
+ } else if (isSubpackage(currPackage, parentPackage)) {
+ return true;
+ } else if (isMismatch(currPackage)) {
+ return false;
+ }
+
+ parentPackage = currPackage;
+ }
+ return true;
+ }
+
+ private void addEntry(String singlePackage) {
+ String[] subPackages = singlePackage.split("\\.");
+ WhiteListNode currPackage = _whiteList;
+ for (String subPackage : subPackages) {
+ currPackage = currPackage.addEntry(subPackage);
+ }
+ }
+
+ //special conditions extracted for readability reasons in the core
+ //algorithm
+
+ private boolean isMismatch(WhiteListNode currPackage) {
+ return currPackage == null;
+ }
+
+ private boolean isSubpackage(WhiteListNode currPackage, WhiteListNode parentPackage) {
+ return currPackage == null && parentPackage != null && !parentPackage.hasChildren();
+ }
+
+ private boolean isRootPackageMismatch(WhiteListNode currPackage, WhiteListNode parentPackage) {
+ return currPackage == null && parentPackage == null;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/loaders/ScannerClassloader.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/loaders/ScannerClassloader.java
new file mode 100644
index 0000000..64ae5b1
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/loaders/ScannerClassloader.java
@@ -0,0 +1,147 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.loaders;
+
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassUtils;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ScannerClassloader extends ClassLoader
+{
+
+ File _tempDir = null;
+
+ Map<String, Class> _alreadyScanned = new HashMap<String, Class>();
+
+ final Logger _logger = Logger.getLogger(ScannerClassloader.class.getName());
+
+ public ScannerClassloader(ClassLoader classLoader, int scriptingEngine, String engineExtension, File tempDir)
+ {
+ super(classLoader);
+
+ this._tempDir = tempDir;
+ }
+
+ @Override
+ public InputStream getResourceAsStream(String name)
+ {
+ File resource = new File(_tempDir.getAbsolutePath() + File.separator + name);
+ if (resource.exists())
+ {
+ try
+ {
+ return new FileInputStream(resource);
+ }
+ catch (FileNotFoundException e)
+ {
+ return super.getResourceAsStream(name);
+ }
+ }
+ return super.getResourceAsStream(name);
+ }
+
+ public File getClassFile(String className)
+ {
+ return ClassUtils.classNameToFile(_tempDir.getAbsolutePath(), className);
+ }
+
+ @Override
+ public Class<?> loadClass(String className) throws ClassNotFoundException
+ {
+ //check if our class exists in the tempDir
+
+ if (_alreadyScanned.containsKey(className))
+ {
+ return _alreadyScanned.get(className);
+ }
+
+ File target = getClassFile(className);
+ if (!target.exists())
+ {
+ return super.loadClass(className);
+ }
+
+ //ClassResource data = WeavingContext.getFileChangedDaemon().getClassMap().get(className);
+ //if (data != null && !data.getRefreshAttribute().requiresRefresh()) {
+ // return data.getAClass();
+ //}
+
+ FileInputStream iStream = null;
+
+ int fileLength;
+ byte[] fileContent;
+ try
+ {
+ fileLength = (int) target.length();
+ fileContent = new byte[fileLength];
+ iStream = new FileInputStream(target);
+ int len = iStream.read(fileContent);
+ if (_logger.isLoggable(Level.FINER))
+ {
+ _logger.log(Level.FINER, "class read {0} bytes read", String.valueOf(len));
+ }
+
+ if (className.contains("TestResourceHandler"))
+ {
+ System.out.println("debugpoint found");
+ }
+ //we have to do it here because just in case
+ //a dependent class is loaded as well we run into classcast exceptions
+ Class retVal = super.defineClass(className, fileContent, 0, fileLength);
+ _alreadyScanned.put(className, retVal);
+ return retVal;
+
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new ClassNotFoundException(e.toString());
+ }
+ catch (IOException e)
+ {
+ throw new ClassNotFoundException(e.toString());
+ }
+ finally
+ {
+ if (iStream != null)
+ {
+ try
+ {
+ iStream.close();
+ }
+ catch (Exception e)
+ {
+ _logger.log(Level.SEVERE, "", e);
+ }
+ }
+ }
+
+ }
+
+}
+
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/registry/DependencyRegistryImpl.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/registry/DependencyRegistryImpl.java
new file mode 100644
index 0000000..03e1ee8
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/registry/DependencyRegistryImpl.java
@@ -0,0 +1,142 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.registry;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.common.util.StringUtils;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.ClassFilter;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.core.ClassDependencies;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.filter.ScanIdentifierFilter;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.filter.StandardNamespaceFilter;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * registry facade which is used to track our dependencies
+ */
+public class DependencyRegistryImpl implements ExternalFilterDependencyRegistry
+{
+ List<ClassFilter> _filters = new LinkedList<ClassFilter>();
+
+ ClassDependencies _dependencMap;
+
+ //private volatile Strategy _registrationStrategy;
+ final Integer _engineType;
+
+ /**
+ * constructor for our facade
+ *
+ * @param engineType the engine type this registry should support
+ * @param dependencyMap the dependency map which stores the dependencies
+ */
+ public DependencyRegistryImpl(Integer engineType, ClassDependencies dependencyMap) {
+ _dependencMap = dependencyMap;
+ _engineType = engineType;
+
+ _filters.add(new ScanIdentifierFilter(_engineType, ScriptingConst.ENGINE_TYPE_JSF_ALL, ScriptingConst.ENGINE_TYPE_JSF_NO_ENGINE));
+ _filters.add(new StandardNamespaceFilter());
+ }
+
+ /**
+ * Clears the entire filter map
+ */
+ public void clearFilters() {
+ _filters = new LinkedList<ClassFilter>();
+
+ _filters.add(new ScanIdentifierFilter(_engineType, ScriptingConst.ENGINE_TYPE_JSF_ALL,
+ ScriptingConst.ENGINE_TYPE_JSF_NO_ENGINE));
+ _filters.add(new StandardNamespaceFilter());
+ }
+
+ /**
+ * adds a new filter
+ *
+ * @param filter the filter to be added
+ */
+ public void addFilter(ClassFilter filter) {
+ _filters.add(filter);
+ }
+
+ /**
+ * checks if the className is allowed in the current filter chain
+ *
+ * @param engineType an identifier for the current scan type (jsf java scan for instance)
+ * @param className the classname to be checked
+ * @return true if a filter triggers false if not
+ */
+ public boolean isAllowed(Integer engineType, String className) {
+ Iterator<ClassFilter> it = _filters.iterator();
+ while(it.hasNext()) {
+ ClassFilter filter = it.next();
+ if (!filter.isAllowed(_engineType, className)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * adds a dependency to our dependency map (usually rootclass -> dependency and currentClass -> dependency)
+ *
+ * @param engineType the engine type for this dependency
+ * @param rootClass the root class of this scan which all dependencies are referenced from
+ * @param currentlyVisitedClass the source which includes or casts the dependencies
+ * @param dependency the dependency to be added
+ */
+ public void addDependency(Integer engineType, String rootClass, String currentlyVisitedClass, String dependency) {
+
+ if (StringUtils.isBlank(dependency)) {
+ return;
+ }
+
+ if (currentlyVisitedClass != null && currentlyVisitedClass.equals(dependency)) {
+ return;
+ }
+
+
+
+ if (!isAllowed(engineType, dependency)) {
+ return;
+ }
+
+ //not needed
+ //if(!StringUtils.isBlank(currentlyVisitedClass)) {
+ // _dependencMap.addDependency(currentlyVisitedClass, dependency);
+ //}
+
+ //for now we code it into a list like we used to do before
+ //but in the long run we have to directly register
+ //to save one step
+ //getDependencySet(source).add(dependency);
+ if(!StringUtils.isBlank(rootClass)) {
+ _dependencMap.addDependency(rootClass, dependency);
+ }
+ }
+
+ /**
+ * flush to flush down our stored dependencies into our final map
+ */
+ public void flush(Integer engineType) {
+ //_registrationStrategy.apply(_dependencies);
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/registry/ExternalFilterDependencyRegistry.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/registry/ExternalFilterDependencyRegistry.java
new file mode 100644
index 0000000..00d0e0b
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/registry/ExternalFilterDependencyRegistry.java
@@ -0,0 +1,68 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.registry;
+
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.ClassFilter;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.DependencyRegistry;
+
+/**
+ * General contractual interface for a dependency registry with external filters
+ * being settable
+ * <p/>
+ * The dependency registry is a class which stores dependencies
+ * according to an internal whitelisting system.
+ * <p/>
+ * Only classes which pass the whitelisting check will be processed
+ */
+public interface ExternalFilterDependencyRegistry extends DependencyRegistry
+{
+
+ /**
+ * Clears the internal filters
+ * for the registry
+ */
+ void clearFilters();
+
+ /**
+ * adds another filter to the internal filter list
+ *
+ * @param filter the filter to be added
+ */
+ void addFilter(ClassFilter filter);
+
+ /**
+ * Allowance check for external shortcutting
+ * This check triggers into the internal filters
+ * to pre-check if a class is allowed to pass or not
+ *
+ * @param className the classname to be checked
+ * @param engineType an identifier for the current scan type (jsf java scan for instance)
+ * @return true if it is false otherwise
+ */
+ public boolean isAllowed(Integer engineType, String className);
+
+ /**
+ * Flush operation to batch sync
+ * the current dependencies against a storage
+ * <p/>
+ * (will be removed later once we have all the code transitioned
+ * to the registry system)
+ */
+ void flush(Integer engineType);
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/registry/MasterDependencyRegistry.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/registry/MasterDependencyRegistry.java
new file mode 100644
index 0000000..6782669
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/engine/dependencyScan/registry/MasterDependencyRegistry.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 org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.registry;
+
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.api.DependencyRegistry;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A master dependency registry which keeps track of various
+ * sub-registries in our dependency scanning system
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+public class MasterDependencyRegistry implements DependencyRegistry
+{
+
+ /**
+ * We keep our central registry in a map
+ * with the engineType as key value to detect which
+ * registry needs to be triggered
+ */
+ private Map<Integer, DependencyRegistry> _subRegistries = new ConcurrentHashMap<Integer, DependencyRegistry>();
+
+ /**
+ * adds a new dependency to all registered registries
+ *
+ * @param engineType the engine type which holds the registry
+ * @param rootClass the root class of this scan which all dependencies are referenced from
+ * @param currentClass the current class scanned
+ * @param dependency the dependency to be added relative to the current class
+ */
+ public void addDependency(Integer engineType, String rootClass, String currentClass, String dependency) {
+ for (Map.Entry<Integer, DependencyRegistry> entry : _subRegistries.entrySet()) {
+ entry.getValue().addDependency(engineType, rootClass, currentClass, dependency);
+ }
+ }
+
+ /**
+ * Flush which is issued at the end of processing to flush
+ * any content which has not been yet processed into our content holding
+ * data structures
+ *
+ * @param engineType the engine type which has issued the flush operation
+ */
+ public void flush(Integer engineType) {
+ for (Map.Entry<Integer, DependencyRegistry> entry : _subRegistries.entrySet()) {
+ entry.getValue().flush(engineType);
+ }
+ }
+
+ /**
+ * adds a subregistry to our current master registry
+ *
+ * @param engineType the engine type which is the key to our subregistry
+ * @param registry the subregistry which has to be added
+ */
+ public void addSubregistry(Integer engineType, DependencyRegistry registry) {
+ _subRegistries.put(engineType, registry);
+ }
+
+ /**
+ * Getter for getting a subregistry from our given registry
+ *
+ * @param engineType the engine type to search for
+ * @return the subregistry according to the engine type, or null if none is found
+ */
+ public DependencyRegistry getSubregistry(Integer engineType) {
+ return _subRegistries.get(engineType);
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/loader/ThrowAwayClassloader.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/loader/ThrowAwayClassloader.java
new file mode 100644
index 0000000..f9a4391
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/loader/ThrowAwayClassloader.java
@@ -0,0 +1,144 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.loader;
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassUtils;
+import org.apache.myfaces.extensions.scripting.core.monitor.ClassResource;
+
+import java.io.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static java.util.logging.Level.SEVERE;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ThrowAwayClassloader extends ClassLoader
+{
+
+ static final Logger _logger = Logger.getLogger(ThrowAwayClassloader.class.getName());
+
+ public ThrowAwayClassloader(ClassLoader classLoader)
+ {
+ super(classLoader);
+ }
+
+ public ThrowAwayClassloader()
+ {
+ }
+
+ @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
+ {
+ if(className.contains("TestResourceHandler")) {
+ System.out.println("Debugppint found");
+ }
+ ClassResource res = (ClassResource) WeavingContext.getInstance().getResource(className);
+ if (res == null) return super.loadClass(className);
+ if (!res.isTainted() && res.getAClass() != null) return res.getAClass();
+ File target = resolveClassFile(className);
+ //a load must happen anyway because the target was recompiled
+ int fileLength;
+ byte[] fileContent;
+ FileInputStream iStream = null;
+ //we cannot load while a compile is in progress
+ //we have to wait until it is one
+ try
+ {
+ fileLength = (int) target.length();
+ fileContent = new byte[fileLength];
+ iStream = new FileInputStream(target);
+ int result = iStream.read(fileContent);
+ _logger.log(Level.FINER, "read {0} bytes", String.valueOf(result));
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new ClassNotFoundException(e.toString());
+ }
+ catch (IOException e)
+ {
+ throw new ClassNotFoundException(e.toString());
+ }
+ finally
+ {
+ if (iStream != null)
+ {
+ try
+ {
+ iStream.close();
+ }
+ catch (Exception e)
+ {
+ Logger log = Logger.getLogger(this.getClass().getName());
+ log.log(SEVERE, "", e);
+ }
+ }
+ }
+ //here we use trick17 we can store as many classes of the same name
+ //as long as we store with a new classloader every time it needs refresh
+ //we need to do it because the classloader can call itself recursively
+ //TODO we might run into issues here with inner classes
+ Class retVal;
+ if (res != null) {
+ retVal = (new ThrowAwayClassloader(getParent())).defineClass(className, fileContent, 0, fileLength);
+ res.setAClass(retVal);
+ res.setTainted(false);
+ } else {
+ retVal = super.defineClass(className, fileContent, 0, fileLength);
+ }
+ return retVal;
+
+ }
+
+ @Override
+ public InputStream getResourceAsStream(String name)
+ {
+ File resource = resolveClassFile(name);
+ if (resource.exists())
+ {
+ try
+ {
+ return new FileInputStream(resource);
+ }
+ catch (FileNotFoundException e)
+ {
+ return super.getResourceAsStream(name);
+ }
+ }
+ return super.getResourceAsStream(name);
+ }
+
+ private File resolveClassFile(String name)
+ {
+ return ClassUtils.classNameToFile(WeavingContext.getInstance().getConfiguration().getCompileTarget()
+ .getAbsolutePath
+ (), name);
+
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/monitor/ClassResource.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/monitor/ClassResource.java
new file mode 100644
index 0000000..f0eba64
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/monitor/ClassResource.java
@@ -0,0 +1,192 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.monitor;
+
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassUtils;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.logging.Logger;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ClassResource extends WatchedResource
+{
+
+ Logger logger = Logger.getLogger(this.getClass().getName());
+
+ /*
+ * volatile due to the ram concurrency behavior
+ * of the instance vars jdk 5+
+ */
+
+ //TODO we probably can drop the file definitions
+ //the class has all meta data internally via findResource
+ //on its corresponding classloader
+ //caching the info however probably is faster
+ volatile Class _aClass = null;
+ volatile File _sourceFile;
+
+ volatile int _scriptingEngine = ScriptingConst.ENGINE_TYPE_JSF_NO_ENGINE;
+ /*non initial change for delta change investigation*/
+ volatile boolean changedForCompile = false;
+
+
+ //todo clean up the sourcepath and filename
+
+ //--- todo move this into a separate resource handling facility
+
+
+
+ @Override
+ /**
+ * returns the source file in this case
+ */
+ public File getFile()
+ {
+ try
+ {
+ return _sourceFile;
+ }
+ catch (NullPointerException ex)
+ {
+ return null;
+ }
+ }
+
+ public void setFile(File sourceFile)
+ {
+ _sourceFile = sourceFile;
+ }
+
+ public Class getAClass()
+ {
+ return _aClass;
+ }
+
+ public void setAClass(Class aClass)
+ {
+ this._aClass = aClass;
+ }
+
+ public int getScriptingEngine()
+ {
+ return _scriptingEngine;
+ }
+
+ public void setScriptingEngine(int scriptingEngine)
+ {
+ this._scriptingEngine = scriptingEngine;
+ }
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException
+ {
+ ClassResource retVal = (ClassResource) super.clone();
+ return retVal;
+ }
+
+ public String getSourceFile()
+ {
+ return _sourceFile.getAbsolutePath().substring(getSourceDir().length());
+ }
+
+ public String getSourceDir()
+ {
+ Collection<String> sourceRoots = WeavingContext.getInstance().getConfiguration().getSourceDirs(_scriptingEngine);
+ String fileDir = _sourceFile.getAbsolutePath();
+ fileDir = fileDir.replaceAll("\\\\", "/");
+ for (String sourceRoot : sourceRoots)
+ {
+ sourceRoot = sourceRoot.replaceAll("\\\\", "/");
+ if (fileDir.startsWith(sourceRoot))
+ {
+ return sourceRoot;
+ }
+ }
+ return null;
+ }
+
+
+
+ /**
+ * identifier for this resource is the classname
+ *
+ * @return
+ */
+ public String getIdentifier()
+ {
+ String targetDir = WeavingContext.getInstance().getConfiguration().getCompileTarget().getAbsolutePath();
+ String className = ClassUtils.relativeFileToClassName(getSourceFile());
+
+ return className;
+ }
+
+ public void setTainted(boolean value)
+ {
+ //if (isTainted()) return;
+ if (value && !tainted)
+ {
+ WeavingContext.getInstance().markLastTaint();
+ //TODO add logging event here
+ logger.info("[EXT-SCRIPTING] tainting " + getSourceFile());
+ WeavingContext.getInstance().addTaintLogEntry(this);
+ WeavingContext.getInstance().gcTaintLog();
+ }
+ tainted = value;
+ }
+
+ /**
+ * @return true if the source file has been modified compared to its classfile
+ */
+ public boolean isTainted()
+ {
+ return tainted;
+ }
+
+ public boolean needsRecompile()
+ {
+ String targetDir = WeavingContext.getInstance().getConfiguration().getCompileTarget().getAbsolutePath();
+ String className = ClassUtils.relativeFileToClassName(getSourceFile());
+ className = targetDir + File.separator + className.replaceAll("\\.", File.separator) + ".class";
+ File targetClass = new File(className);
+ return !targetClass.exists() || targetClass.lastModified() < _sourceFile.lastModified();
+ }
+
+ public boolean stillExists()
+ {
+ return _sourceFile.exists();
+ }
+
+ public boolean isChangedForCompile()
+ {
+ return changedForCompile;
+ }
+
+ public void setChangedForCompile(boolean changedForCompile)
+ {
+ this.changedForCompile = changedForCompile;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/monitor/ResourceMonitor.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/monitor/ResourceMonitor.java
new file mode 100644
index 0000000..e80a340
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/monitor/ResourceMonitor.java
@@ -0,0 +1,169 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.monitor;
+
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.servlet.ServletContext;
+import java.lang.ref.WeakReference;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Central daemon thread which watches the resources
+ * for changes and marks them as changed.
+ * This watchdog daemon is the central core
+ * of the entire scripting engine it runs asynchronously
+ * to your program and keeps an eye on the resources
+ * and their dependencies, once a file has changed
+ * all the referring dependencies are also marked
+ * as being to reloaded.
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+public class ResourceMonitor extends Thread
+{
+
+ private static final String CONTEXT_KEY = "extscriptDaemon";
+
+ static ResourceMonitor _instance = null;
+
+
+ //Map<String, ClassResource> _classMap = new ConcurrentHashMap<String, ClassResource>(8, 0.75f, 1);
+ //ClassDependencies _dependencyMap = new ClassDependencies();
+
+ /**
+ * This map is a shortcut for the various scripting engines.
+ * It keeps track whether the engines source paths
+ * have dirty files or not and if true we enforce a recompile at the
+ * next refresh!
+ * <p/>
+ * We keep track on engine level to avoid to search the classMap for every refresh
+ * the classMap still is needed for various identification tasks which are reload
+ * related
+ */
+ Map<Integer, Boolean> _systemRecompileMap = new ConcurrentHashMap<Integer, Boolean>(8, 0.75f, 1);
+
+ boolean _running = false;
+ // boolean _contextInitialized = false;
+ Logger _log = Logger.getLogger(ResourceMonitor.class.getName());
+ // ScriptingWeaver _weavers = null;
+ static WeakReference<ServletContext> _externalContext;
+
+ public static synchronized void init(ServletContext externalContext)
+ {
+
+ if (_externalContext != null) return;
+ _externalContext = new WeakReference<ServletContext>(externalContext);
+ if (getInstance() != null) return;
+
+ //we currently keep it as singleton but in the long run we will move it into the context
+ //like everything else singleton-wise
+
+ _instance = new ResourceMonitor();
+
+ /**
+ * daemon thread to allow forced
+ * shutdowns for web context restarts
+ */
+ _instance.setDaemon(true);
+ _instance.setRunning(true);
+ //_instance.start();
+ _externalContext.get().setAttribute(CONTEXT_KEY, _instance);
+
+ }
+
+ public static synchronized ResourceMonitor getInstance()
+ {
+ //we do it in this complicated manner because of find bugs
+ //practically this cannot really happen except for shutdown were it is not important anymore
+ ServletContext context = _externalContext.get();
+ if (context != null)
+ {
+ return (ResourceMonitor) context.getAttribute(CONTEXT_KEY);
+ }
+ return null;
+ }
+
+ /**
+ * Central run method
+ * which performs the entire scanning process
+ */
+ public void run()
+ {
+
+ while (_running)
+ {
+ if (!_running) break;
+ //we run the full scan on the classes to bring our data structures up to the task
+ performMonitoringTask();
+ sleep();
+ }
+
+ if (_log.isLoggable(Level.INFO))
+ {
+ _log.info("[EXT-SCRIPTING] Dynamic reloading watch daemon is shutting down");
+ }
+
+ }
+
+ public void performMonitoringTask()
+ {
+ synchronized(WeavingContext.getInstance().recompileLock) {
+ WeavingContext context = WeavingContext.getInstance();
+ context.initialFullScan();
+
+ //we compile wherever needed, taints are now in place due to our scan already being performed
+ if (context.compile())
+ {
+ //we now have to perform a full dependency scan to bring our dependency map to the latest state
+ context.scanDependencies();
+ //we next retaint all classes according to our dependency graph
+ context.markTaintedDependends();
+ }
+ }
+ //context.annotationScan();
+
+ }
+
+ private void sleep()
+ {
+ try
+ {
+ Thread.sleep(ScriptingConst.TAINT_INTERVAL);
+ }
+ catch (InterruptedException e)
+ {
+ //if the server shuts down while we are in sleep we get an error
+ //which we better should swallow
+ }
+ }
+
+ public void setRunning(boolean running)
+ {
+ this._running = running;
+ }
+
+
+}
+
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/monitor/WatchedResource.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/monitor/WatchedResource.java
new file mode 100644
index 0000000..b051936
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/monitor/WatchedResource.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.monitor;
+
+import java.io.File;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * An abstraction on our class watcher
+ * we now deal with generic resources to simplify the access
+ */
+
+public abstract class WatchedResource implements Cloneable {
+
+ /**
+ * tainted override for dependend classes
+ */
+ volatile boolean tainted = false;
+
+ //volatile long _lastLoaded = -1L;
+
+ /**
+ * Unique identifier on the resource
+ *
+ * @return
+ */
+ public abstract String getIdentifier();
+
+ /**
+ * @return a file handle on the current resource
+ */
+ public abstract File getFile();
+
+ public WatchedResource getClone() {
+ try {
+ return (WatchedResource) super.clone();
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
+
+ public boolean isTainted()
+ {
+ return tainted;
+ }
+
+ public void setTainted(boolean tainted)
+ {
+ this.tainted = tainted;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/GlobalReloadingStrategy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/GlobalReloadingStrategy.java
new file mode 100644
index 0000000..284dbf7
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/GlobalReloadingStrategy.java
@@ -0,0 +1,151 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.reloading;
+
+import org.apache.myfaces.extensions.scripting.core.api.ReloadingStrategy;
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassUtils;
+import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static org.apache.myfaces.extensions.scripting.core.api.ScriptingConst.*;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * A reloading strategy chain of responsibility which switches
+ * depending on the artifact type to the correct
+ * strategy
+ * <p/>
+ * TODO make the reloading strategy pluggable from outside (1.1)!
+ * TODO groovy reloading strategy needs to be added at least the properties
+ * mapping for a groovy class
+ */
+
+public class GlobalReloadingStrategy implements ReloadingStrategy
+{
+
+ private static final String STRATEGY_PKG = "org.apache.myfaces.extensions.scripting.jsf.facelet";
+ final Logger _logger = Logger.getLogger(GlobalReloadingStrategy.class.getName());
+
+ protected ReloadingStrategy _beanStrategy;
+ protected ReloadingStrategy _noMappingStrategy;
+ protected ReloadingStrategy _allOthers;
+
+ /*loaded dynamically for myfaces 2+*/
+ protected ReloadingStrategy _componentHandlerStrategy;
+ protected ReloadingStrategy _validatorHandlerStrategy;
+ protected ReloadingStrategy _converterHandlerStrategy;
+ protected ReloadingStrategy _behaviorHandlerStrategy;
+
+ public GlobalReloadingStrategy()
+ {
+ _beanStrategy = new ManagedBeanReloadingStrategy();
+ _noMappingStrategy = new NoMappingReloadingStrategy();
+ _allOthers = new SimpleReloadingStrategy();
+
+ /*
+ * external handlers coming from various submodules
+ */
+ _componentHandlerStrategy = dynaload(STRATEGY_PKG +".ComponentHandlerReloadingStrategy");
+ _validatorHandlerStrategy = dynaload(STRATEGY_PKG +".ValidatorHandlerReloadingStrategy");
+ _converterHandlerStrategy = dynaload(STRATEGY_PKG +".ConverterHandlerReloadingStrategy");
+ _behaviorHandlerStrategy = dynaload(STRATEGY_PKG +".BehaviorHandlerReloadingStrategy");
+ }
+
+ /**
+ * the strategy callback which switches between various strategies
+ * we have in our system
+ *
+ * @param toReload the object which has to be reloaded
+ * @param artifactType the artifact type for which the reloading strategy has to be applied to
+ * @return either the same or a reloading object depending on the current state of the object
+ */
+ public Object reload(Object toReload, int artifactType)
+ {
+
+ switch (artifactType)
+ {
+ case ARTIFACT_TYPE_MANAGEDBEAN:
+ return _beanStrategy.reload(toReload, artifactType);
+
+ case ARTIFACT_TYPE_RENDERER:
+ return _noMappingStrategy.reload(toReload, artifactType);
+ case ARTIFACT_TYPE_BEHAVIOR:
+ return _noMappingStrategy.reload(toReload, artifactType);
+ case ARTIFACT_TYPE_CLIENTBEHAVIORRENDERER:
+ return _noMappingStrategy.reload(toReload, artifactType);
+ case ARTIFACT_TYPE_COMPONENT:
+ return _noMappingStrategy.reload(toReload, artifactType);
+ case ARTIFACT_TYPE_VALIDATOR:
+ return _noMappingStrategy.reload(toReload, artifactType);
+
+ case ARTIFACT_TYPE_COMPONENT_HANDLER:
+ return dynaReload(toReload, _componentHandlerStrategy, artifactType);
+ case ARTIFACT_TYPE_CONVERTER_HANDLER:
+ return dynaReload(toReload, _converterHandlerStrategy, artifactType);
+ case ARTIFACT_TYPE_VALIDATOR_HANDLER:
+ return dynaReload(toReload, _validatorHandlerStrategy, artifactType);
+ case ARTIFACT_TYPE_BEHAVIOR_HANDLER:
+ return dynaReload(toReload, _behaviorHandlerStrategy, artifactType);
+
+ default:
+ return _allOthers.reload(toReload, artifactType);
+ }
+ }
+
+ public Object dynaReload(Object toReload, ReloadingStrategy strategy, int artifactType)
+ {
+ if (strategy == null)
+ {
+ //no strategy no reload
+ return toReload;
+ } else
+ {
+ return strategy.reload(toReload, artifactType);
+ }
+ }
+
+ /**
+ * load dynamically the given strategy class
+ *
+ * @param strategyClass the strategy class which has to be loaded and instantiated
+ * @return an instance of the strategy class if found otherwise null
+ */
+ private ReloadingStrategy dynaload(String strategyClass)
+ {
+ try
+ {
+ Class theClass = ClassUtils.forName(strategyClass);
+ return (ReloadingStrategy) ReflectUtil.instantiate(theClass);
+ }
+ catch (RuntimeException ex)
+ {
+ //in this case swallowing the exception is expected
+ if (_logger.isLoggable(Level.FINEST))
+ {
+ _logger.log(Level.FINEST, "Expected Exception: ", ex);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/ManagedBeanReloadingStrategy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/ManagedBeanReloadingStrategy.java
new file mode 100644
index 0000000..e7c5990
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/ManagedBeanReloadingStrategy.java
@@ -0,0 +1,59 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.reloading;
+
+import org.apache.myfaces.extensions.scripting.core.api.ReloadingStrategy;
+
+/**
+ * The managed beans have a different reloading
+ * strategy. The dependencies of a managed bean
+ * are managed by the IOC container and
+ * not transferred over the reloading strategy
+ * like for all other artifacts.
+ * Hence the bean handler removes the bean and its
+ * referring backward dependencies, and the runtime system
+ * rebuilds the tree anew.
+ *
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ManagedBeanReloadingStrategy implements ReloadingStrategy
+{
+
+
+
+ public ManagedBeanReloadingStrategy() {
+ }
+
+ /**
+ * In our case the dropping already has happened at request time
+ * no need for another reloading here
+ *
+ * @param scriptingInstance the instance which has to be reloaded
+ * @param artifactType the type of artifact
+ * @return does nothing in this case and returns only the original instance, the reloading is handled
+ * for managed beans on another level
+ */
+ public Object reload(Object scriptingInstance, int artifactType) {
+ return scriptingInstance;
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/MethodLevelReloadingHandler.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/MethodLevelReloadingHandler.java
new file mode 100644
index 0000000..247405f
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/MethodLevelReloadingHandler.java
@@ -0,0 +1,131 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.reloading;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Generic artifact invocation handler
+ * which should be able to cover
+ * all interfaceable artifacts which
+ * only have reloading logic
+ * and can cope with reloading on method level
+ * <p/>
+ * Note this works only for a minority of the artifacts
+ * the reason, most artifacts do not rely on interfaces but
+ * on base classes
+ *
+ * @author Werner Punz
+ */
+@SuppressWarnings("unused")
+public class MethodLevelReloadingHandler extends ReloadingInvocationHandler implements Serializable {
+
+ private static final long serialVersionUID = -3034995032644947216L;
+
+ int _artifactType;
+
+ public MethodLevelReloadingHandler(Object rootObject, int artifactType) {
+ _loadedClass = rootObject.getClass();
+ _delegate = rootObject;
+ _artifactType = artifactType;
+ }
+
+ /**
+ * outside interface to the invoke method
+ * which gets called every time a method
+ * is called
+ *
+ * @param object the object holding the method
+ * @param method the method
+ * @param paramHolders the param holders
+ * @return the return value of the operation
+ * @throws Throwable in case of an error
+ */
+ public Object invoke(Object object, Method method, Object[] paramHolders) throws Throwable {
+ return reloadInvoke(method, paramHolders);
+ }
+
+ /**
+ * invoke handler which is triggered
+ * by every method call which takes care of the reload
+ *
+ * @param method the method to call
+ * @param paramHolders the params
+ * @return the return value of the operation
+ * @throws InstantiationException standard throw caused by reflection
+ * @throws IllegalAccessException standard throw caused by reflection
+ * @throws java.lang.reflect.InvocationTargetException standard throw caused by reflection
+ */
+
+ protected Object reloadInvoke(Method method, Object[] paramHolders) throws InstantiationException, IllegalAccessException, InvocationTargetException {
+
+
+ if (_delegate == null) {
+ //stateless or lost state due to a lifecycle iteration we trigger anew
+ _delegate = (WeavingContext.getInstance().reload(_loadedClass)).newInstance();
+ } else {
+ //if we are stateful only a tainted artifact is reloaded
+ _delegate = WeavingContext.getInstance().reload(_delegate, _artifactType);
+
+ //we work our way through all proxies and fetch the class for further reference
+ Object delegate = WeavingContext.getDelegateFromProxy(_delegate);
+ _loadedClass = delegate.getClass();
+ }
+ //check for proxies and unproxy them before calling the methods
+ //to avoid unnecessary cast problems
+ //this is slow on long param lists but it is better
+ //to be slow than to have casts an calls in the code
+ //for production we can compile the classes anyway and avoid
+ //this
+ unmapProxies(paramHolders);
+ return method.invoke(_delegate, paramHolders);
+ }
+
+ /**
+ * unmap proxied objects
+ *
+ * @param objects the objects to be unmapped
+ */
+ private void unmapProxies(Object[] objects) {
+ if (objects == null) return;
+ for (int cnt = 0; cnt < objects.length; cnt++) {
+ objects[cnt] = WeavingContext.getDelegateFromProxy(objects[cnt]);
+ }
+ }
+
+ public int getArtifactType() {
+ return _artifactType;
+ }
+
+ public void setArtifactType(int artifactType) {
+ _artifactType = artifactType;
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/NoMappingReloadingStrategy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/NoMappingReloadingStrategy.java
new file mode 100644
index 0000000..d6ed611
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/NoMappingReloadingStrategy.java
@@ -0,0 +1,54 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.reloading;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * <p/>
+ * The renderer is a stateless flyweight pattern the reloading strategy is
+ * to do nothing, this should give optimal results
+ * <p/>
+ * <p/>
+ * The components are a similar case they are not flyweight
+ * but the properties usually are preserved by the lifecycle if possible
+ * or assigned by the tag handlers
+ * <p/>
+ * <p/>
+ * The same also applies to other flyweight patterned classes
+ * like converters or validators
+ * <p/>
+ * <p/>
+ * The only ones which need to keep some state are the ones
+ * which keep delegates, like the NavHandler
+ */
+public class NoMappingReloadingStrategy extends SimpleReloadingStrategy
+{
+
+ public NoMappingReloadingStrategy()
+ {
+ super();
+ }
+
+ @Override
+ protected void mapProperties(Object target, Object src)
+ {
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/ReloadingInvocationHandler.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/ReloadingInvocationHandler.java
new file mode 100644
index 0000000..80ffbcc
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/ReloadingInvocationHandler.java
@@ -0,0 +1,71 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.reloading;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
+
+import java.lang.reflect.InvocationHandler;
+
+/**
+ * <p/>
+ * We set our own invocation handler
+ * here to allow reflection utils directly targeting our
+ * _delegate.
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+@SuppressWarnings("unused")
+public abstract class ReloadingInvocationHandler implements InvocationHandler, Decorated
+{
+ Class _loadedClass = null;
+ Object _delegate = null;
+
+ /**
+ * simplified invoke for more dynamic upon invocation
+ * on our reloading objects
+ *
+ * @param object the object to be invoked on
+ * @param method the method to be invoked
+ * @param arguments the arguments passed down
+ * @return the return value of the operation
+ */
+ public Object invoke(Object object, String method, Object... arguments) {
+ return ReflectUtil.executeMethod(object, method, arguments);
+ }
+
+ public Class getLoadedClass() {
+ return _loadedClass;
+ }
+
+ public Object getDelegate() {
+ return _delegate;
+ }
+
+ public void setDelegate(Object delegate) {
+ _delegate = delegate;
+ }
+
+ public void setLoadedClassName(Class loadedClass) {
+ this._loadedClass = loadedClass;
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/SimpleReloadingStrategy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/SimpleReloadingStrategy.java
new file mode 100644
index 0000000..360a24c
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/reloading/SimpleReloadingStrategy.java
@@ -0,0 +1,115 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.reloading;
+
+import org.apache.commons.beanutils.BeanUtils;
+import org.apache.myfaces.extensions.scripting.core.api.ReloadingStrategy;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A simple implementation of our reloading strategy
+ * pattern this is the most basic implementation
+ * covering our reloading.
+ * <p/>
+ * Applicable for most artifacts except for now managed beans
+ * <p/> *
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class SimpleReloadingStrategy implements ReloadingStrategy
+{
+
+
+
+ public SimpleReloadingStrategy() {
+
+ }
+
+ /**
+ * <p>
+ * the central callback for our strategy here
+ * it has to handle the reload of the scriptingInstance
+ * if possible, otherwise it has to return the
+ * original object if no reload was necessary or possible
+ * </p>
+ *
+ * @param scriptingInstance the instance to be reloaded by the system
+ * @return either the same object or a new instance utilizing the changed code
+ */
+ public Object reload(Object scriptingInstance, int artifactType) {
+ //reload the class to get new static content if needed
+ Class aclass = WeavingContext.getInstance().reload(scriptingInstance.getClass());
+
+ if (aclass == null || aclass.hashCode() == scriptingInstance.getClass().hashCode()) {
+ //class of this object has not changed although
+ // reload is enabled we can skip the rest now
+ return scriptingInstance;
+ }
+ getLog().info("[EXT-SCRIPTING] possible reload for " + scriptingInstance.getClass().getName());
+ /*only recreation of empty constructor classes is possible*/
+ try {
+ //reload the object by instantiating a new class and
+ // assigning the attributes properly
+ Object newObject = aclass.newInstance();
+
+ /*now we shuffle the properties between the objects*/
+ mapProperties(newObject, scriptingInstance);
+
+ return newObject;
+ } catch (Exception e) {
+ getLog().log(Level.SEVERE, "reload ", e);
+ }
+ return null;
+
+ }
+
+ /**
+ * helper to map the properties wherever possible
+ * <p/>
+ * This is the simplest solution for now,
+ * we apply only a copy properties here, which should be enough
+ * for all artifacts except the managed beans and the ones
+ * which have to preserve some kind of delegate before instantiation.
+ *
+ * @param target the target which has to receive the properties
+ * @param src the source which has the original properties
+ */
+ protected void mapProperties(Object target, Object src) {
+ try {
+ BeanUtils.copyProperties(target, src);
+ } catch (IllegalAccessException e) {
+ getLog().log(Level.FINEST, e.toString());
+ //this is wanted
+ } catch (InvocationTargetException e) {
+ getLog().log(Level.FINEST, e.toString());
+ //this is wanted
+ }
+ }
+
+ protected Logger getLog() {
+ return Logger.getLogger(this.getClass().getName());
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/servlet/ScriptingServletFilter.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/servlet/ScriptingServletFilter.java
new file mode 100644
index 0000000..1c5ccbb
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/core/servlet/ScriptingServletFilter.java
@@ -0,0 +1,58 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.core.servlet;
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.servlet.*;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+/**
+ * This filter is just a blocking filter
+ * refreshes are not allowed while the system
+ * recompiles, incoming requests are put on hold
+ *
+ * @author Werner Punz
+ */
+public class ScriptingServletFilter implements Filter
+{
+
+ ServletContext _context;
+ Logger logger = Logger.getLogger(this.getClass().getName());
+
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ _context = filterConfig.getServletContext();
+ }
+
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException
+ {
+ synchronized (WeavingContext.getInstance().recompileLock) {
+ logger.fine("request");
+ }
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+
+ @Override
+ public void destroy()
+ {
+ }
+
+}
\ No newline at end of file
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/adapters/CustomChainLoader.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/adapters/CustomChainLoader.java
new file mode 100644
index 0000000..c74793c
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/adapters/CustomChainLoader.java
@@ -0,0 +1,143 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.adapters;
+
+import org.apache.myfaces.shared.util.ClassLoaderExtension;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassUtils;
+import org.apache.myfaces.extensions.scripting.core.loader.ThrowAwayClassloader;
+import org.apache.myfaces.extensions.scripting.core.monitor.ClassResource;
+
+import javax.servlet.ServletContext;
+import java.io.File;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * The chainloader docks onto the forName handler
+ * in the myfaces classutils which load the
+ * artifacting classes, we dock onto this extension point
+ * neutrally by indirection over the MyFacesSPI
+ */
+
+public class CustomChainLoader extends ClassLoaderExtension
+{
+
+ ThrowAwayClassloader _loader = null;
+
+ static class _Action implements PrivilegedExceptionAction<ThrowAwayClassloader>
+ {
+ ClassLoader _parent;
+
+ _Action(ClassLoader parent)
+ {
+ this._parent = parent;
+ }
+
+ public ThrowAwayClassloader run()
+ {
+ return new ThrowAwayClassloader(_parent);
+ }
+ }
+
+ public CustomChainLoader(ServletContext context)
+ {
+ try
+ {
+ _loader = AccessController.doPrivileged(
+ new _Action(ClassUtils.getContextClassLoader())
+ );
+ }
+ catch (PrivilegedActionException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ }
+
+ public Class forName(String name)
+ {
+ if (name.endsWith(";"))
+ {
+ name = name.substring(1, name.length() - 1);
+ }
+
+ if (name.startsWith("java.")) /*the entire java namespace is reserved so no use to do a specific classloading check here*/
+ return null;
+ if (name.startsWith("javax.")) /*the entire java namespace is reserved so no use to do a specific classloading check here*/
+ return null;
+ else if (name.startsWith("com.sun")) /*internal java specific namespace*/
+ return null;
+ else if (name.startsWith("sun.")) /*internal java specific namespace*/
+ return null;
+ else if (name.startsWith("org.apache") && !name.startsWith("org.apache.myfaces"))
+ {
+ return null;
+ } else if (name.startsWith("org.apache") && name.startsWith("org.apache.myfaces.config"))
+ {
+ return null;
+ } else if (name.startsWith("org.apache") && name.startsWith("org.apache.myfaces.spi"))
+ {
+ return null;
+ } else if (name.startsWith("org.apache") && name.startsWith("org.apache.myfaces.application"))
+ {
+ return null;
+ }
+ //
+ try
+ {
+ return loadClass(name);
+ }
+ catch (ClassNotFoundException e)
+ {
+ return null;
+ }
+ }
+
+ private Class loadClass(String name) throws ClassNotFoundException
+ {
+ File targetDirectory = WeavingContext.getInstance().getConfiguration().getCompileTarget();
+ File target = ClassUtils.classNameToFile(targetDirectory.getAbsolutePath(), name);
+ if (!target.exists()) return null;
+ //otherwise check if tainted and if not simply return the class stored
+ if (name.contains("TestNavigationHandler"))
+ {
+ System.out.println("debugpoint found");
+ }
+ ClassResource resource = (ClassResource) WeavingContext.getInstance().getResource(name);
+ if (resource == null)
+ {
+ return null;
+ }
+ if (resource.isTainted() || resource.getAClass() == null)
+ {
+ //load the class via the throw away classloader
+ return _loader.loadClass(name);
+ } else
+ {
+ return resource.getAClass();
+ }
+
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/adapters/MyFacesSPI.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/adapters/MyFacesSPI.java
new file mode 100644
index 0000000..ad2291a
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/adapters/MyFacesSPI.java
@@ -0,0 +1,66 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.adapters;
+
+import org.apache.myfaces.extensions.scripting.core.api.ImplementationSPI;
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassUtils;
+import org.apache.myfaces.extensions.scripting.jsf.adapters.handlers.MyFacesBeanHandler;
+
+import javax.servlet.ServletContext;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ *
+ * SPI for various myfaces related tasks
+ */
+public class MyFacesSPI implements ImplementationSPI
+{
+ CustomChainLoader _loader = null;
+ MyFacesBeanHandler beanHandler = new MyFacesBeanHandler();
+
+ public void registerClassloadingExtension(ServletContext context) {
+ CustomChainLoader loader = new CustomChainLoader(context); //ReflectUtil.instantiate("extras.org.apache.myfaces.extensions
+ // .scripting.servlet" +
+ //".CustomChainLoader",
+ //new Cast(ServletContext.class, context));
+ ClassUtils.addClassLoadingExtension(loader, true);
+ _loader = loader;
+ }
+
+ public void refreshManagedBeans() {
+ beanHandler.refreshAllManagedBeans();
+ }
+
+ public Class forName(String clazz) {
+ return _loader.forName(clazz);
+ }
+
+
+ private static MyFacesSPI ourInstance = new MyFacesSPI();
+
+ public static MyFacesSPI getInstance()
+ {
+ return ourInstance;
+ }
+
+ private MyFacesSPI()
+ {
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/adapters/handlers/MyFacesBeanHandler.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/adapters/handlers/MyFacesBeanHandler.java
new file mode 100644
index 0000000..c9136c2
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/adapters/handlers/MyFacesBeanHandler.java
@@ -0,0 +1,281 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.adapters.handlers;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ *
+ * TODO refactor this out
+ */
+
+import org.apache.myfaces.config.RuntimeConfig;
+import org.apache.myfaces.config.annotation.LifecycleProvider;
+import org.apache.myfaces.config.annotation.LifecycleProviderFactory;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
+import org.apache.myfaces.extensions.scripting.core.monitor.ClassResource;
+
+import javax.faces.context.FacesContext;
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Bean handler implementation
+ * which encapsulates the myfaces specific parts
+ * of the bean processing
+ */
+public class MyFacesBeanHandler
+{
+
+ static final Logger _logger = Logger.getLogger(MyFacesBeanHandler.class.getName());
+
+ /**
+ * constructor
+ */
+ public MyFacesBeanHandler()
+ {
+
+ }
+
+ /**
+ * Refreshes all managed beans
+ * session, and personal scoped ones
+ * <p/>
+ * personal scoped beans are beans which
+ * have either
+ * <li> session scope </li>
+ * <li> page scope </li>
+ * <li> custom scope </li>
+ */
+ public void refreshAllManagedBeans()
+ {
+ if (FacesContext.getCurrentInstance() == null)
+ {
+ return;//no npe allowed
+ }
+
+ Collection<ClassResource> tainted = WeavingContext.getInstance().getTaintedClasses();
+ Set<String> taints = new HashSet<String>();
+ for (ClassResource taintedClass : tainted)
+ {
+ if(taintedClass.getAClass() != null) {
+ taints.add(taintedClass.getAClass().getName());
+ }
+ }
+
+ //scanElDependencies();
+
+ if (taints.size() > 0)
+ {
+ //We now have to check if the tainted classes belong to the managed beans
+ Set<String> managedBeanClasses = new HashSet<String>();
+
+ Map mbeans = RuntimeConfig.getCurrentInstance(FacesContext.getCurrentInstance().getExternalContext()).getManagedBeans();
+ Map mbeansSnapshotView;
+
+ //synchronized (RefreshContext.BEAN_SYNC_MONITOR) {
+ mbeansSnapshotView = makeSnapshot(mbeans);
+ //}
+
+ for (Object entry : mbeansSnapshotView.entrySet())
+ {
+ Object bean = (Object) ((Map.Entry) entry).getValue();
+
+ managedBeanClasses.add((String) ReflectUtil.executeMethod(bean, "getManagedBeanClassName"));//bean.getManagedBeanClassName());
+ }
+
+ boolean managedBeanTainted = isAnyManagedBeanTainted(taints, managedBeanClasses);
+ markPersonalScopeRefreshRecommended();
+ getLog().info("[EXT-SCRIPTING] Tainting all beans to avoid classcast exceptions");
+ if (managedBeanTainted)
+ {
+ globalManagedBeanRefresh(mbeansSnapshotView);
+ //personalScopeRefresh();
+ }
+ }
+ }
+
+
+ /**
+ * removes all bean references which have been tainted
+ * (note for now we remove all dynamic references until we
+ * get a more sophisticated handling of managed beans)
+ *
+ * @param workCopy the managed beam snapshot view
+ */
+ private void globalManagedBeanRefresh(Map workCopy)
+ {
+ Collection<ClassResource> tainted = WeavingContext.getInstance().getTaintedClasses();
+ Set<String> taints = new HashSet<String>();
+ for (ClassResource taintedClass : tainted)
+ {
+ if(taintedClass.getAClass() != null)
+ taints.add(taintedClass.getAClass().getName());
+ }
+
+ for (Object entry : workCopy.entrySet())
+ {
+ Object bean = ((Map.Entry) entry).getValue();
+ Class managedBeanClass = (Class) ReflectUtil.executeMethod(bean, "getManagedBeanClass");
+ if (hasToBeRefreshed(taints, managedBeanClass))
+ {
+ //managed bean class found we drop the class from our session
+ removeBeanReferences(bean);
+ }
+ }
+ }
+
+ /**
+ * determines whether any bean in our managed bean list
+ * is tainted or not
+ *
+ * @param tainted a list of classes which are tainted in this iteration
+ * @param managedBeanClasses a ist of classes which are our managed beans
+ * @return true if one of the beans is tainted
+ */
+ private boolean isAnyManagedBeanTainted(Set<String> tainted, Set<String> managedBeanClasses)
+ {
+ boolean managedBeanTainted = false;
+ for (String taintedClass : tainted)
+ {
+ if (managedBeanClasses.contains(taintedClass))
+ {
+ managedBeanTainted = true;
+ break;
+ }
+ }
+ return managedBeanTainted;
+ }
+
+ /**
+ * removes the references from out static scope
+ * for jsf2 we probably have some kind of notification mechanism
+ * which notifies custom scopes
+ *
+ * @param bean the managed bean which all references have to be removed from
+ */
+
+ private void removeBeanReferences(Object bean)
+ {
+ String managedBeanName = (String) ReflectUtil.executeMethod(bean, "getManagedBeanName");
+
+ if (getLog().isLoggable(Level.FINE))
+ {
+ getLog().log(Level.FINE, "[EXT-SCRIPTING] JavaScriptingWeaver.removeBeanReferences({0})", managedBeanName);
+ }
+
+ FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(managedBeanName);
+ FacesContext.getCurrentInstance().getExternalContext().getApplicationMap().remove(managedBeanName);
+ removeCustomScopedBean(bean);
+ }
+
+ /**
+ * @return the log for this class
+ */
+ protected Logger getLog()
+ {
+ return Logger.getLogger(this.getClass().getName());
+ }
+
+ /**
+ * jsf2 helper to remove custom scoped beans
+ *
+ * @param bean the managed bean which has to be removed from the custom scope from
+ */
+ private void removeCustomScopedBean(Object bean)
+ {
+ Object scopeImpl = FacesContext.getCurrentInstance().getExternalContext().getApplicationMap().get(ReflectUtil.executeMethod(bean, "getManagedBeanScope"));
+ if (scopeImpl == null) return; //scope not implemented
+ //we now have to revert to introspection here because scopes are a pure jsf2 construct
+ //so we use a messaging pattern here to cope with it
+
+ Object beanInstance = ReflectUtil.executeMethod(scopeImpl, "get", ReflectUtil.executeMethod(bean, "getManagedBeanName"));
+ LifecycleProvider lifecycleProvider =
+ LifecycleProviderFactory.getLifecycleProviderFactory().getLifecycleProvider(FacesContext.getCurrentInstance().getExternalContext());
+ try
+ {
+ lifecycleProvider.destroyInstance(beanInstance);
+ }
+ catch (IllegalAccessException e)
+ {
+ _logger.log(Level.WARNING, "removeCustomScopedBean():", e);
+ }
+ catch (InvocationTargetException e)
+ {
+ _logger.log(Level.WARNING, "removeCustomScopedBean():", e);
+ }
+ }
+
+ /**
+ * MyFaces 2.0 keeps an immutable map over the session
+ * and request scoped beans
+ * if we alter that during our loop we get a concurrent modification exception
+ * taking a snapshot in time fixes that
+ *
+ * @param mbeans the internal managed bean map which has to be investigated
+ * @return a map with the class name as key and the managed bean info
+ * as value of the current state of the internal runtime config bean map
+ */
+ private Map makeSnapshot(Map mbeans)
+ {
+ Map workCopy;
+
+ workCopy = new HashMap(mbeans.size());
+ for (Object elem : mbeans.entrySet())
+ {
+ Map.Entry entry = (Map.Entry) elem;
+ workCopy.put(entry.getKey(), entry.getValue());
+ }
+
+ return workCopy;
+ }
+
+ /**
+ * sets the internal timer for other processes
+ * to update their beans as well
+ */
+ private void markPersonalScopeRefreshRecommended()
+ {
+ long sessionRefreshTime = System.currentTimeMillis();
+ // WeavingContext.getRefreshContext().setPersonalScopedBeanRefresh(sessionRefreshTime);
+ FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(ScriptingConst.SESS_BEAN_REFRESH_TIMER, sessionRefreshTime);
+ }
+
+ /**
+ * important, this method determines whether a managed bean class
+ * has to be refreshed or not
+ *
+ * @param tainted set of tainted classes
+ * @param managedBeanClass the class to be checked for refresh criteria
+ * @return true if the current bean class fulfills our refresh criteria
+ */
+ protected boolean hasToBeRefreshed(Set<String> tainted, Class managedBeanClass)
+ {
+
+ return WeavingContext.getInstance().isDynamic(managedBeanClass) && tainted.contains(managedBeanClass.getName());
+ }
+
+
+}
+
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/BaseAnnotationScanListener.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/BaseAnnotationScanListener.java
new file mode 100644
index 0000000..9981f62
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/BaseAnnotationScanListener.java
@@ -0,0 +1,66 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation;
+
+import org.apache.myfaces.config.RuntimeConfig;
+
+import javax.faces.application.Application;
+import javax.faces.context.FacesContext;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+
+/**
+ * Base Class for the JSF2 annotation scanning
+ * (note we do not rely on the impl
+ * for annotation scanning because in the long
+ * run we want to support Mojarra)
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class BaseAnnotationScanListener {
+ Logger _log = Logger.getLogger(this.getClass().getName());
+ static Map<String, Object> _alreadyRegistered = new ConcurrentHashMap<String, Object>(8, 0.75f, 1);
+
+ protected RuntimeConfig getRuntimeConfig() {
+ final FacesContext facesContext = FacesContext.getCurrentInstance();
+ //runtime config not started
+ if (facesContext == null) {
+ return null;
+ }
+ return RuntimeConfig.getCurrentInstance(facesContext.getExternalContext());
+ }
+
+ protected Application getApplication() {
+ return FacesContext.getCurrentInstance().getApplication();
+ }
+
+ /**
+ * unregisters this class in the central registry
+ * is triggered if the class itself has been registered previously
+ *
+ * @param className
+ * @return
+ */
+ public void purge(String className) {
+
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/BeanImplementationListener.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/BeanImplementationListener.java
new file mode 100644
index 0000000..0f2d99f
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/BeanImplementationListener.java
@@ -0,0 +1,216 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation;
+
+import org.apache.myfaces.config.RuntimeConfig;
+import org.apache.myfaces.config.element.NavigationRule;
+import org.apache.myfaces.config.impl.digester.elements.ManagedBean;
+import org.apache.myfaces.extensions.scripting.core.api.AnnotationScanListener;
+import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
+import org.apache.myfaces.extensions.scripting.core.common.util.StringUtils;
+
+import javax.faces.bean.*;
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.logging.Level;
+
+/**
+ * bean implementation listener which registers new java sources
+ * into the runtime config, note this class is not thread safe
+ * it is only allowed to be called from a single thread
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class BeanImplementationListener extends BaseAnnotationScanListener implements AnnotationScanListener
+{
+
+ public boolean supportsAnnotation(String annotation) {
+ return annotation.equals(javax.faces.bean.ManagedBean.class.getName());
+ }
+
+ public boolean supportsAnnotation(Class annotation) {
+ return annotation.equals(javax.faces.bean.ManagedBean.class);
+ }
+
+ public void register(Class clazz, java.lang.annotation.Annotation ann) {
+
+ RuntimeConfig config = getRuntimeConfig();
+
+ javax.faces.bean.ManagedBean annCasted = (javax.faces.bean.ManagedBean) ann;
+
+ String beanName = annCasted.name();
+ if (StringUtils.isBlank(beanName)) {
+ beanName = normalizeName(clazz.getName());
+ }
+
+ beanName = beanName.replaceAll("\"", "");
+ //we need to reregister for every bean due to possible managed prop
+ //and scope changes
+ ManagedBean mbean;
+ if (!hasToReregister(beanName, clazz)) {
+ mbean = (ManagedBean) _alreadyRegistered.get(beanName);
+ //return;
+ } else {
+ mbean = new ManagedBean();
+ }
+
+ mbean.setBeanClass(clazz.getName());
+ mbean.setName(beanName);
+ handleManagedpropertiesCompiled(mbean, fields(clazz));
+ resolveScope(clazz, mbean);
+
+ _alreadyRegistered.put(beanName, mbean);
+ config.addManagedBean(beanName, mbean);
+
+ }
+
+ private void resolveScope(Class clazz, ManagedBean mbean) {
+ //now lets resolve the scope
+ String scope = "none";
+ if (clazz.isAnnotationPresent(RequestScoped.class)) {
+ scope = "request";
+ } else if (clazz.isAnnotationPresent(SessionScoped.class)) {
+ scope = "session";
+ } else if (clazz.isAnnotationPresent(ApplicationScoped.class)) {
+ scope = "application";
+ } else if (clazz.isAnnotationPresent(ViewScoped.class)) {
+ scope = "view";
+ } else if (clazz.isAnnotationPresent(NoneScoped.class)) {
+ scope = "none";
+ } else if (clazz.isAnnotationPresent(CustomScoped.class)) {
+ CustomScoped customScoped = (CustomScoped) clazz.getAnnotation(CustomScoped.class);
+ scope = (customScoped != null) ? customScoped.value() : "custom";
+ }
+ mbean.setScope(scope);
+ }
+
+ private void handleManagedpropertiesCompiled(ManagedBean mbean, Field[] fields) {
+ /*since we reprocess the managed properties we can handle them here by clearing them first*/
+ mbean.getManagedProperties().clear();
+ for (Field field : fields) {
+ if (_log.isLoggable(Level.FINEST)) {
+ _log.log(Level.FINEST, " Scanning field '" + field.getName() + "'");
+ }
+ ManagedProperty property = field
+ .getAnnotation(ManagedProperty.class);
+ if (property != null) {
+ if (_log.isLoggable(Level.FINE)) {
+ _log.log(Level.FINE, " Field '" + field.getName()
+ + "' has a @ManagedProperty annotation");
+ }
+
+ org.apache.myfaces.config.impl.digester.elements.ManagedProperty mpc =
+ new org.apache.myfaces.config.impl.digester.elements.ManagedProperty();
+ String name = property.name();
+ if ((name == null) || "".equals(name)) {
+ name = field.getName();
+ }
+ mpc.setPropertyName(name);
+ mpc.setPropertyClass(field.getType().getName()); // FIXME - primitives, arrays, etc.
+ mpc.setValue(property.value());
+
+ ReflectUtil.executeMethod(mbean, "addProperty", mpc);
+ }
+ }
+ }
+
+ /**
+ * <p>Return an array of all <code>Field</code>s reflecting declared
+ * fields in this class, or in any superclass other than
+ * <code>java.lang.Object</code>.</p>
+ *
+ * @param clazz Class to be analyzed
+ * @return the array of fields
+ */
+ private Field[] fields(Class clazz) {
+
+ Map<String, Field> fields = new HashMap<String, Field>();
+ do {
+ for (Field field : clazz.getDeclaredFields()) {
+ if (!fields.containsKey(field.getName())) {
+ fields.put(field.getName(), field);
+ }
+ }
+ } while ((clazz = clazz.getSuperclass()) != Object.class);
+ return fields.values().toArray(new Field[fields.size()]);
+ }
+
+ protected boolean hasToReregister(String name, Class clazz) {
+ ManagedBean mbean = (ManagedBean) _alreadyRegistered.get(name);
+ return mbean == null || !mbean.getManagedBeanClassName().equals(clazz.getName());
+ }
+
+ /**
+ * name normalizer for automated name mapping
+ * (aka if no name attribute is given in the annotation)
+ *
+ * @param className the classname to be mapped (can be with package=
+ * @return the normalized jsf bean name
+ */
+ private String normalizeName(String className) {
+ String name = className.substring(className.lastIndexOf(".") + 1);
+
+ return name.substring(0, 1).toLowerCase() + name.substring(1);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void purge(String className) {
+ RuntimeConfig config = getRuntimeConfig();
+ //We have to purge and readd our managed beans, unfortunatly the myfaces impl enforces
+ //us to do the same for the nav rules after purge
+ //we cannot purge the managed beans and nav rules separately
+ Collection<NavigationRule> navigationRules = new ArrayList<NavigationRule>();
+ Map managedBeans = new HashMap<String, org.apache.myfaces.config.element.ManagedBean>();
+
+ navigationRules.addAll(config.getNavigationRules());
+ managedBeans.putAll(config.getManagedBeans());
+
+ config.purge();
+
+ for (NavigationRule navRule : navigationRules) {
+ config.addNavigationRule(navRule);
+ }
+
+ //We refresh the managed beans, dead references still can cause
+ //runtime errors but in this case we cannot do anything
+ org.apache.myfaces.config.element.ManagedBean mbeanFound = null;
+ List<String> mbeanKey = new LinkedList<String>();
+
+ for (Object entry : managedBeans.entrySet()) {
+ Map.Entry mbean = (Map.Entry) entry;
+
+ Object bean = mbean.getValue();
+
+
+ if (!((Class)ReflectUtil.executeMethod( bean, "getManagedBeanClass")).getName().equals(className)) {
+ config.addManagedBean((String) mbean.getKey(), (org.apache.myfaces.config.element.ManagedBean) mbean.getValue());
+ } else {
+ Object mbeanf = mbean.getValue();
+ mbeanKey.add((String)ReflectUtil.executeMethod(mbeanf, "getManagedBeanName"));
+ }
+ }
+ if (mbeanFound != null) {
+ for (String toRemove : mbeanKey) {
+ _alreadyRegistered.remove(toRemove);
+ }
+ }
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/BehaviorImplementationListener.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/BehaviorImplementationListener.java
new file mode 100644
index 0000000..e482f64
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/BehaviorImplementationListener.java
@@ -0,0 +1,71 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.AnnotationScanListener;
+import org.apache.myfaces.extensions.scripting.jsf.annotation.purged.PurgedBehavior;
+
+import javax.faces.component.behavior.FacesBehavior;
+import java.util.logging.Level;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class BehaviorImplementationListener extends SingleEntityAnnotationListener implements AnnotationScanListener
+{
+
+ public BehaviorImplementationListener() {
+ super();
+ _entityParamValue = "value";
+ }
+
+ public boolean supportsAnnotation(String annotation) {
+ return annotation.equals(FacesBehavior.class.getName());
+ }
+
+ public boolean supportsAnnotation(Class annotation) {
+ return annotation.equals(FacesBehavior.class);
+ }
+
+
+ protected void addEntity(Class clazz, String val) {
+ if (_log.isLoggable(Level.FINEST)) {
+ _log.log(Level.FINEST, "addBehavior(" + val + ","
+ + clazz.getName() + ")");
+ }
+ getApplication().addBehavior(val, clazz.getName());
+ }
+
+ @Override
+ public void purge(String className) {
+ super.purge(className);
+ if (!_alreadyRegistered.containsKey(className)) {
+ return;
+ }
+
+ String val = (String) _alreadyRegistered.remove(className);
+ if (val != null) {
+ getApplication().addBehavior(val, PurgedBehavior.class.getName());
+ }
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/BehaviorRendererImplementationListener.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/BehaviorRendererImplementationListener.java
new file mode 100644
index 0000000..ae88ea1
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/BehaviorRendererImplementationListener.java
@@ -0,0 +1,175 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation;
+
+
+import org.apache.myfaces.extensions.scripting.jsf.annotation.purged.PurgedClientBehaviorRenderer;
+
+import javax.faces.FactoryFinder;
+import javax.faces.context.FacesContext;
+import javax.faces.render.FacesBehaviorRenderer;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitFactory;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * Implementation listener for the FacesBehaviorRenderer annotation
+ */
+
+public class BehaviorRendererImplementationListener extends MapEntityAnnotationScanner {
+
+ private static final String PAR_RENDERERTYPE = "rendererType";
+ private static final String PAR_RENDERKITID = "renderKitId";
+
+ Map<AnnotationEntry, String> _inverseIndex = new HashMap<AnnotationEntry, String>();
+
+ class AnnotationEntry {
+ String rendererType;
+ String renderKitId;
+
+ AnnotationEntry(String rendererType, String renderKitId) {
+ this.rendererType = rendererType;
+ this.renderKitId = renderKitId;
+ }
+
+ public boolean equals(Object incoming) {
+ if (!(incoming instanceof AnnotationEntry)) {
+ return false;
+ }
+ AnnotationEntry toCompare = (AnnotationEntry) incoming;
+
+ if (incoming == null) {
+ return false;
+ }
+
+ boolean firstEquals = compareValuePair(rendererType, toCompare.getRendererType());
+ boolean secondEquals = compareValuePair(renderKitId, toCompare.getRenderKitId());
+
+ return firstEquals && secondEquals;
+ }
+
+ @Override
+ public int hashCode() {
+ return (checkForNull(rendererType) + "_" + checkForNull(renderKitId)).hashCode(); //To change body of overridden methods use File | Settings | File Templates.
+ }
+
+ private String checkForNull(String in) {
+ return (in == null) ? "" : in;
+ }
+
+ protected boolean compareValuePair(Object val1, Object val2) {
+ boolean retVal = false;
+ if (val1 == null) {
+ if (val2 != null) retVal = false;
+ if (val2 == null) {
+ retVal = true;
+ }
+ } else {
+ retVal = val1.equals(val2);
+ }
+ return retVal;
+ }
+
+ public String getRendererType() {
+ return rendererType;
+ }
+
+ public String getRenderKitId() {
+ return renderKitId;
+ }
+ }
+
+ public BehaviorRendererImplementationListener() {
+ super();
+ }
+
+ @Override
+ protected void addEntity(Class clazz, Map<String, Object> params) {
+ String value = (String) params.get(PAR_RENDERERTYPE);
+ String renderKitId = (String) params.get(PAR_RENDERKITID);
+
+ AnnotationEntry entry = new AnnotationEntry(value, renderKitId);
+ _alreadyRegistered.put(clazz.getName(), entry);
+ _inverseIndex.put(entry, clazz.getName());
+
+ getApplication().addConverter(entry.getRendererType(), clazz.getName());
+ }
+
+ @Override
+ protected boolean hasToReregister(Map params, Class clazz) {
+ String value = (String) params.get(PAR_RENDERERTYPE);
+ String renderKitId = (String) params.get(PAR_RENDERKITID);
+
+ AnnotationEntry entry = new AnnotationEntry(value, renderKitId);
+
+ AnnotationEntry alreadyRegistered = (AnnotationEntry) _alreadyRegistered.get(clazz.getName());
+ if (alreadyRegistered == null) {
+ return true;
+ }
+
+ return alreadyRegistered.equals(entry);
+ }
+
+ public boolean supportsAnnotation(String annotation) {
+ return annotation.equals(FacesBehaviorRenderer.class.getName());
+ }
+
+ public boolean supportsAnnotation(Class annotation) {
+ return annotation.equals(FacesBehaviorRenderer.class);
+ }
+
+
+ private RenderKitFactory getRenderKitFactory() {
+ return (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+ }
+
+ private RenderKit getRenderkit(String renderKitId) {
+ RenderKitFactory factory = getRenderKitFactory();
+ RenderKit renderKit = factory.getRenderKit(FacesContext.getCurrentInstance(), renderKitId);
+ return renderKit;
+ }
+
+ @Override
+ public void purge(String className) {
+ super.purge(className);
+ AnnotationEntry entry = (AnnotationEntry) _alreadyRegistered.remove(className);
+ if (entry == null) {
+ return;
+ }
+
+ RenderKit renderKit = getRenderkit(entry.getRenderKitId());
+ try {
+ String rendererClass = _inverseIndex.get(entry);
+ if (rendererClass != null && rendererClass.equals(className)) {
+ _inverseIndex.put(entry, PurgedClientBehaviorRenderer.class.getName());
+ renderKit.addClientBehaviorRenderer(entry.getRendererType(), PurgedClientBehaviorRenderer.class.newInstance());
+ }
+ } catch (InstantiationException e) {
+ _log.log(Level.SEVERE, "", e);
+ } catch (IllegalAccessException e) {
+ _log.log(Level.SEVERE, "", e);
+ }
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/ComponentImplementationListener.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/ComponentImplementationListener.java
new file mode 100644
index 0000000..38593ae
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/ComponentImplementationListener.java
@@ -0,0 +1,73 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.AnnotationScanListener;
+import org.apache.myfaces.extensions.scripting.jsf.annotation.purged.PurgedComponent;
+
+import javax.faces.component.FacesComponent;
+import java.util.logging.Level;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ComponentImplementationListener extends SingleEntityAnnotationListener implements AnnotationScanListener
+{
+
+ public ComponentImplementationListener() {
+ super();
+ _entityParamValue = "value";
+ }
+
+ public boolean supportsAnnotation(String annotation) {
+ return annotation.equals(FacesComponent.class.getName());
+ }
+
+ public boolean supportsAnnotation(Class annotation) {
+ return annotation.equals(FacesComponent.class);
+ }
+
+
+ protected void addEntity(Class clazz, String val) {
+ if (_log.isLoggable(Level.FINEST)) {
+ _log.log(Level.FINEST, "addComponent(" + val + "," + clazz.getName() + ")");
+ }
+ getApplication().addComponent(val, clazz.getName());
+ //register the renderer if not registered
+
+ _alreadyRegistered.put(clazz.getName(), val);
+ }
+
+ @Override
+ public void purge(String className) {
+ super.purge(className);
+ //no purge needed we already have a different class
+ //registered
+ if (!_alreadyRegistered.containsKey(className)) {
+ return;
+ }
+ String val = (String) _alreadyRegistered.remove(className);
+ if (val != null) {
+ getApplication().addComponent(val, PurgedComponent.class.getName());
+ }
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/ConverterImplementationListener.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/ConverterImplementationListener.java
new file mode 100644
index 0000000..fe45e81
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/ConverterImplementationListener.java
@@ -0,0 +1,154 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.AnnotationScanListener;
+import org.apache.myfaces.extensions.scripting.jsf.annotation.purged.PurgedConverter;
+
+import javax.faces.application.Application;
+import javax.faces.convert.FacesConverter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ConverterImplementationListener extends MapEntityAnnotationScanner implements AnnotationScanListener
+{
+
+ private static final String PAR_VALUE = "value";
+ private static final String PAR_DEFAULT = "forClass";
+
+ Map<AnnotationEntry, String> _inverseIndex = new HashMap<AnnotationEntry, String>();
+
+ class AnnotationEntry {
+ String value;
+ Class forClass;
+
+ AnnotationEntry(String value, Class forClass) {
+
+ this.value = value;
+ this.forClass = forClass;
+ }
+
+ public boolean equals(Object incoming) {
+ if (incoming == null) {
+ return false;
+ }
+
+ if (!(incoming instanceof AnnotationEntry)) {
+ return false;
+ }
+ AnnotationEntry toCompare = (AnnotationEntry) incoming;
+
+ boolean firstEquals = compareValuePair(value, toCompare.getValue());
+ boolean secondEquals = compareValuePair(forClass, toCompare.getForClass());
+
+ return firstEquals && secondEquals;
+ }
+
+ @Override
+ public int hashCode() {
+ String retVal = checkForNull(value) + "_";
+ retVal += ((forClass != null) ? forClass.getName() : "");
+ return retVal.hashCode();
+ }
+
+ private String checkForNull(String in) {
+ return (in == null) ? "" : in;
+ }
+
+ protected boolean compareValuePair(Object val1, Object val2) {
+ boolean retVal = false;
+ if (val1 == null) {
+ if (val2 != null) retVal = false;
+ if (val2 == null) {
+ retVal = true;
+ }
+ } else {
+ retVal = val1.equals(val2);
+ }
+ return retVal;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public Class getForClass() {
+ return forClass;
+ }
+ }
+
+ public ConverterImplementationListener() {
+ super(PAR_VALUE, PAR_DEFAULT);
+ }
+
+ @Override
+ protected void addEntity(Class clazz, Map<String, Object> params) {
+ String value = (String) params.get(PAR_VALUE);
+ Class forClass = (Class) params.get(PAR_DEFAULT);
+
+ AnnotationEntry entry = new AnnotationEntry(value, forClass);
+ _alreadyRegistered.put(clazz.getName(), entry);
+ _inverseIndex.put(entry, clazz.getName());
+
+ getApplication().addConverter(entry.getValue(), clazz.getName());
+ }
+
+ @Override
+ protected boolean hasToReregister(Map params, Class clazz) {
+ String value = (String) params.get(PAR_VALUE);
+ Class forClass = (Class) params.get(PAR_DEFAULT);
+
+ AnnotationEntry entry = new AnnotationEntry(value, forClass);
+
+ AnnotationEntry alreadyRegistered = (AnnotationEntry) _alreadyRegistered.get(clazz.getName());
+
+ return (alreadyRegistered == null) || alreadyRegistered.equals(entry);
+ }
+
+ public boolean supportsAnnotation(String annotation) {
+ return annotation.equals(FacesConverter.class.getName());
+ }
+
+ public boolean supportsAnnotation(Class annotation) {
+ return annotation.equals(FacesConverter.class);
+ }
+
+
+ @Override
+ public void purge(String className) {
+ super.purge(className);
+ AnnotationEntry entry = (AnnotationEntry) _alreadyRegistered.remove(className);
+ if (entry == null) {
+ return;
+ }
+ String _oldConverterClass = _inverseIndex.get(entry);
+ if (_oldConverterClass.equals(className)) {
+ Application application = getApplication();
+ application.addConverter(entry.getValue(), PurgedConverter.class.getName());
+ _inverseIndex.put(entry, className);
+ }
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/GenericAnnotationScanner.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/GenericAnnotationScanner.java
new file mode 100644
index 0000000..25d8619
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/GenericAnnotationScanner.java
@@ -0,0 +1,205 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation;
+
+import org.apache.myfaces.extensions.scripting.core.api.AnnotationScanListener;
+import org.apache.myfaces.extensions.scripting.core.api.ClassScanListener;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.engine.api.ClassScanner;
+import org.apache.myfaces.extensions.scripting.core.engine.dependencyScan.loaders.ScannerClassloader;
+
+import javax.faces.context.FacesContext;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * Source path annotation scanner for java it scans all sources in the specified source paths
+ * recursively for additional information
+ * and then adds the id/name -> class binding information to the correct factory locations,
+ * wherever possible
+ */
+@SuppressWarnings("unused")
+public class GenericAnnotationScanner extends BaseAnnotationScanListener implements ClassScanner
+{
+ //eventing system not yet fully implemented
+ List<ClassScanListener> _listeners = new LinkedList<ClassScanListener>();
+
+ //this registry is needed to keep track of added and moved annotations
+ Map<String, String> _registeredAnnotations = new HashMap<String, String>();
+
+ LinkedList<String> _sourcePaths = new LinkedList<String>();
+
+ WeavingContext _weaver = null;
+
+ public GenericAnnotationScanner() {
+ _weaver = WeavingContext.getInstance();
+ initDefaultListeners();
+ }
+
+ public void addScanPath(String sourcePath) {
+ _sourcePaths.addFirst(sourcePath);
+ }
+
+ Collection<java.lang.annotation.Annotation> filterAnnotations(java.lang.annotation.Annotation[] annotations) {
+ List<java.lang.annotation.Annotation> retVal = new ArrayList<java.lang.annotation.Annotation>(annotations.length);
+
+ for (java.lang.annotation.Annotation annotation : annotations) {
+ if (annotation.annotationType().getName().startsWith(ScriptingConst.JAVAX_FACES)) {
+ retVal.add(annotation);
+ }
+
+ }
+ return retVal;
+ }
+
+ public void scanClass(Class clazz) {
+ java.lang.annotation.Annotation[] annotations = clazz.getAnnotations();
+
+ Collection<java.lang.annotation.Annotation> annCol = filterAnnotations(annotations);
+ if (!annCol.isEmpty()) {
+ addOrMoveAnnotations(clazz);
+ } else {
+ removeAnnotations(clazz);
+ }
+ }
+
+ private void initDefaultListeners() {
+ _listeners.add(new BeanImplementationListener());
+ _listeners.add(new BehaviorImplementationListener());
+ _listeners.add(new ComponentImplementationListener());
+ _listeners.add(new ConverterImplementationListener());
+ _listeners.add(new RendererImplementationListener());
+ _listeners.add(new ValidatorImplementationListener());
+ }
+
+ /**
+ * builds up the parsing chain and then notifies its observers
+ * on the found data
+ */
+ public void scanPaths() {
+ //https://issues.apache.org/jira/browse/EXTSCRIPT-33
+
+ //check if the faces config is already available otherwise we cannot scan yet
+ final FacesContext facesContext = FacesContext.getCurrentInstance();
+ //runtime config not started
+ //for now we only can reach the runtime config in the referenced BaseAnnotatonScanListener
+ //if we have a facesContext reachable.
+ if (facesContext == null) {
+ //TODO (1.1) decouple the scan in the BaseAnnotationScanListener from the facesConfig
+ //to get the runtime config
+ return;
+ }
+ if(!_weaver.isPostInit() || _weaver.getLastAnnotationScan() >= _weaver.getLastTaint()) return;
+ _weaver.markLastAnnotationScan();
+
+
+ for (String className : _weaver.loadPossibleDynamicClasses()) {
+ try {
+ if(!_weaver.isTainted(className)) continue;
+
+ ScannerClassloader loader = new ScannerClassloader(Thread.currentThread().getContextClassLoader(),
+ -1, null, _weaver.getConfiguration().getCompileTarget());
+
+ Class clazz;
+ //in case the class does not exist we have to load it from our weavingcontext
+ //otherwise we do just a scan on the class to avoid side behavior
+ //if (WeavingContext.getFileChangedDaemon().getClassMap().get(className) == null) {
+ // clazz = _weaver.loadScriptingClassFromName(className);
+ //} else {
+ clazz = loader.loadClass(className);
+ //}
+
+ if (clazz != null) {
+ java.lang.annotation.Annotation[] anns = clazz.getAnnotations();
+ if (anns != null && anns.length > 0) {
+ addOrMoveAnnotations(clazz);
+ } else {
+ removeAnnotations(clazz);
+ }
+ }
+ } catch (ClassNotFoundException e) {
+ Logger _logger = Logger.getLogger(this.getClass().getName());
+ _logger.log(Level.WARNING, "", e);
+ }
+ }
+
+ }
+
+ /**
+ * add or moves a class level annotation
+ * to a new place
+ *
+ * @param clazz the class to have the annotation moved or added
+ */
+ private void addOrMoveAnnotations(Class clazz) {
+ java.lang.annotation.Annotation[] anns = clazz.getAnnotations();
+ for (java.lang.annotation.Annotation ann : anns) {
+ for (ClassScanListener cListener : _listeners) {
+ AnnotationScanListener listener = (AnnotationScanListener) cListener;
+ if (listener.supportsAnnotation(ann.annotationType())) {
+ listener.register(clazz, ann);
+
+ _registeredAnnotations.put(clazz.getName(), ann.annotationType().getName());
+ //TODO check if we still need this
+ //ClassResource metaData = WeavingContext.getInstance().getWatchedResource(clazz.getName());
+
+ }
+ }
+ }
+ }
+
+ /**
+ * use case annotation removed
+ * we have to entirely remove the annotation
+ * from our internal registry and the myfaces registry
+ *
+ * @param clazz the class to have the annotation removed
+ */
+ private void removeAnnotations(Class clazz) {
+ String registeredAnnotation = _registeredAnnotations.get(clazz.getName());
+ if (registeredAnnotation != null) {
+ for (ClassScanListener cListener : _listeners) {
+ AnnotationScanListener listener = (AnnotationScanListener) cListener;
+ if (listener.supportsAnnotation(registeredAnnotation)) {
+ listener.purge(clazz.getName());
+ _registeredAnnotations.remove(clazz.getName());
+ //WeavingContext.getFileChangedDaemon().getClassMap().remove(clazz.getName());
+ }
+ }
+ }
+ }
+
+ public void clearListeners() {
+ _listeners.clear();
+ }
+
+ public void addListener(ClassScanListener listener) {
+ _listeners.add(listener);
+ }
+
+
+ public void scanAndMarkChange() {
+ //do nothing here
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/ListenerForAnnotationHandler.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/ListenerForAnnotationHandler.java
new file mode 100644
index 0000000..2b0c483
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/ListenerForAnnotationHandler.java
@@ -0,0 +1,31 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation;
+
+/**
+ * A generic system event listener which makes the system event
+ * annotations dynamic
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ListenerForAnnotationHandler {
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/MapEntityAnnotationScanner.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/MapEntityAnnotationScanner.java
new file mode 100644
index 0000000..97ef8a7
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/MapEntityAnnotationScanner.java
@@ -0,0 +1,63 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.AnnotationScanListener;
+import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
+
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Annotation scanner which scans generically
+ * an annotation with more than one entry values.
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public abstract class MapEntityAnnotationScanner extends BaseAnnotationScanListener implements AnnotationScanListener
+{
+
+ String[] _annotationParms = null;
+
+ public MapEntityAnnotationScanner(String... annotationParms) {
+ _annotationParms = annotationParms;
+ }
+
+ public void register(Class clazz, Annotation annotation) {
+
+ Map<String, Object> parms = new HashMap<String, Object>(_annotationParms.length);
+
+ for (String accessor : _annotationParms) {
+ parms.put(accessor, ReflectUtil.fastExecuteMethod(annotation, accessor, new Object[0]));
+ }
+
+ if (hasToReregister(parms, clazz)) {
+ addEntity(clazz, parms);
+ }
+ }
+
+ protected abstract void addEntity(Class clazz, Map<String, Object> params);
+
+ protected abstract boolean hasToReregister(Map params, Class clazz);
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/RendererImplementationListener.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/RendererImplementationListener.java
new file mode 100644
index 0000000..1bebe29
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/RendererImplementationListener.java
@@ -0,0 +1,202 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.AnnotationScanListener;
+import org.apache.myfaces.extensions.scripting.jsf.annotation.purged.PurgedRenderer;
+
+import javax.faces.FactoryFinder;
+import javax.faces.context.FacesContext;
+import javax.faces.render.FacesRenderer;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitFactory;
+import javax.faces.render.Renderer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class RendererImplementationListener extends MapEntityAnnotationScanner implements AnnotationScanListener
+{
+ private static final String PAR_FAMILY = "componentFamily";
+ private static final String PAR_RENDERERTYPE = "rendererType";
+ private static final String PAR_RENDERKITID = "renderKitId";
+
+ Map<AnnotationEntry, String> _inverseIndex = new HashMap<AnnotationEntry, String>();
+
+ public RendererImplementationListener() {
+ super(PAR_FAMILY, PAR_RENDERERTYPE, PAR_RENDERKITID);
+ }
+
+ class AnnotationEntry {
+ String componentFamily;
+ String rendererType;
+ String renderKitId;
+
+ AnnotationEntry(String componentFamily, String rendererType, String renderKitId) {
+ this.componentFamily = componentFamily;
+ this.rendererType = rendererType;
+ this.renderKitId = renderKitId;
+ }
+
+ public boolean equals(Object incoming) {
+ if (!(incoming instanceof AnnotationEntry)) {
+ return false;
+ }
+ AnnotationEntry toCompare = (AnnotationEntry) incoming;
+ //handle null cases
+ if ((componentFamily == null && toCompare.getComponentFamily() != null) ||
+ (componentFamily != null && toCompare.getComponentFamily() == null) ||
+ (rendererType == null && toCompare.getRendererType() != null) ||
+ (rendererType != null && toCompare.getRendererType() == null) ||
+ (renderKitId == null && toCompare.getRenderKitId() != null) ||
+ (renderKitId != null && toCompare.getRenderKitId() == null)) {
+
+ return false;
+ } else if (componentFamily == null && toCompare.getComponentFamily() == null &&
+ rendererType == null && toCompare.getRendererType() == null &&
+ renderKitId == null && toCompare.getRenderKitId() == null) {
+ return true;
+ }
+
+ return componentFamily.equals(toCompare.getComponentFamily()) &&
+ rendererType.equals(toCompare.getRendererType()) &&
+ renderKitId.equals(toCompare.getRenderKitId());
+ }
+
+ @Override
+ public int hashCode() {
+ /*we calculate the hashcoide to avoid double entries*/
+ return (((componentFamily != null) ? componentFamily : "")
+ + "_" +
+ ((rendererType != null) ? rendererType : "")
+ + "_" +
+ ((renderKitId != null) ? renderKitId : "")
+
+ ).hashCode();
+ }
+
+ public String getComponentFamily() {
+ return componentFamily;
+ }
+
+ public String getRendererType() {
+ return rendererType;
+ }
+
+ public String getRenderKitId() {
+ return renderKitId;
+ }
+ }
+
+ public boolean supportsAnnotation(String annotation) {
+ return annotation.equals(FacesRenderer.class.getName());
+ }
+
+ public boolean supportsAnnotation(Class annotation) {
+ return annotation.equals(FacesRenderer.class);
+ }
+
+ @Override
+ protected void addEntity(Class clazz, Map<String, Object> params) {
+ String value = (String) params.get(PAR_FAMILY);
+ String theDefault = (String) params.get(PAR_RENDERERTYPE);
+
+ String renderKitId = getRenderKitId(params);
+ RenderKit renderKit = getRenderkit(renderKitId);
+
+ AnnotationEntry entry = new AnnotationEntry(value, theDefault, renderKitId);
+ _inverseIndex.put(entry, clazz.getName());
+ _alreadyRegistered.put(clazz.getName(), entry);
+
+ if (_log.isLoggable(Level.FINEST)) {
+ _log.log(Level.FINEST, "addRenderer(" + renderKitId + ", "
+ + entry.getComponentFamily() + ", " + entry.getRendererType()
+ + ", " + clazz.getName() + ")");
+ }
+
+ try {
+ renderKit.addRenderer(entry.getComponentFamily(), entry.getRendererType(), (Renderer) clazz.newInstance());
+ } catch (InstantiationException e) {
+ _log.log(Level.SEVERE, "", e);
+ } catch (IllegalAccessException e) {
+ _log.log(Level.SEVERE, "", e);
+ }
+ }
+
+ private RenderKitFactory getRenderKitFactory() {
+ return (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+ }
+
+ @Override
+ protected boolean hasToReregister(Map params, Class clazz) {
+ String value = (String) params.get(PAR_FAMILY);
+ String theDefault = (String) params.get(PAR_RENDERERTYPE);
+ String renderKitId = (String) params.get(PAR_RENDERKITID);
+
+ AnnotationEntry entry = new AnnotationEntry(value, theDefault, renderKitId);
+
+ AnnotationEntry alreadyRegistered = (AnnotationEntry) _alreadyRegistered.get(clazz.getName());
+ if (alreadyRegistered == null) {
+ return true;
+ }
+ //here the check if the new class is the same as the old one
+ return alreadyRegistered.equals(entry);
+ }
+
+ private String getRenderKitId(Map<String, Object> params) {
+ String renderKitId = (String) params.get(PAR_RENDERKITID);
+ renderKitId = (renderKitId == null) ? getApplication().getDefaultRenderKitId() : renderKitId;
+ return renderKitId;
+ }
+
+ private RenderKit getRenderkit(String renderKitId) {
+ RenderKitFactory factory = getRenderKitFactory();
+ RenderKit renderKit = factory.getRenderKit(FacesContext.getCurrentInstance(), renderKitId);
+ return renderKit;
+ }
+
+ @Override
+ public void purge(String className) {
+ super.purge(className);
+ AnnotationEntry entry = (AnnotationEntry) _alreadyRegistered.remove(className);
+ if (entry == null) {
+ return;
+ }
+
+ RenderKit renderKit = getRenderkit(entry.getRenderKitId());
+ try {
+ //by fetching the changed renderer we save a full rescan
+ String rendererClass = _inverseIndex.get(entry);
+ if (rendererClass != null && rendererClass.equals(className)) {
+ _inverseIndex.put(entry, PurgedRenderer.class.getName());
+ renderKit.addRenderer(entry.getComponentFamily(), entry.getRendererType(), PurgedRenderer.class.newInstance());
+ }
+ } catch (InstantiationException e) {
+ _log.log(Level.SEVERE, "", e);
+ } catch (IllegalAccessException e) {
+ _log.log(Level.SEVERE, "", e);
+ }
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/SingleEntityAnnotationListener.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/SingleEntityAnnotationListener.java
new file mode 100644
index 0000000..614b9cf
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/SingleEntityAnnotationListener.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation;
+
+import org.apache.myfaces.extensions.scripting.core.api.AnnotationScanListener;
+import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * annotation scanner which generalized
+ * scans annotations with one value entry
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public abstract class SingleEntityAnnotationListener extends BaseAnnotationScanListener implements AnnotationScanListener
+{
+ String _entityParamValue = null;
+
+ public void register(Class clazz, Annotation annotation) {
+
+ String val = (String) ReflectUtil.executeMethod(annotation, _entityParamValue);
+ if (hasToReregister(val, clazz)) {
+ addEntity(clazz, val);
+ }
+ }
+
+ protected abstract void addEntity(Class clazz, String val);
+
+ protected boolean hasToReregister(String name, Class clazz) {
+ String componentClass = (String) _alreadyRegistered.get(name);
+ return componentClass == null || !componentClass.equals(clazz.getName());
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/ValidatorImplementationListener.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/ValidatorImplementationListener.java
new file mode 100644
index 0000000..80bc504
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/ValidatorImplementationListener.java
@@ -0,0 +1,158 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation;
+
+import org.apache.myfaces.extensions.scripting.core.api.AnnotationScanListener;
+import org.apache.myfaces.extensions.scripting.jsf.annotation.purged.PurgedValidator;
+
+import javax.faces.validator.FacesValidator;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ValidatorImplementationListener extends MapEntityAnnotationScanner implements AnnotationScanListener
+{
+
+ private static final String PAR_VALUE = "value";
+ private static final String PAR_DEFAULT = "isDefault";
+
+ Map<AnnotationEntry, String> _inverseIndex = new HashMap<AnnotationEntry, String>();
+
+ public ValidatorImplementationListener() {
+ /*supported annotation parameters rendererType and default*/
+ super(PAR_VALUE, PAR_DEFAULT);
+ }
+
+ class AnnotationEntry {
+ String value;
+ Boolean theDefault;
+
+ AnnotationEntry(String value, Boolean theDefault) {
+ this.value = value;
+ this.theDefault = theDefault;
+ }
+
+ public boolean equals(Object incoming) {
+ if (!(incoming instanceof AnnotationEntry)) {
+ return false;
+ }
+ AnnotationEntry toCompare = (AnnotationEntry) incoming;
+
+ if (incoming == null) {
+ return false;
+ }
+
+ boolean firstEquals = compareValuePair(value, toCompare.getValue());
+ boolean secondEquals = compareValuePair(theDefault, toCompare.getTheDefault());
+
+ return firstEquals && secondEquals;
+ }
+
+ @Override
+ public int hashCode() {
+ String retVal = checkForNull(value) + "_" + checkForNull(theDefault);
+ return retVal.hashCode();
+ }
+
+ private String checkForNull(String in) {
+ return (in == null) ? "" : in;
+ }
+
+ private String checkForNull(Boolean in) {
+ return (in == null) ? "" : String.valueOf(in.booleanValue());
+ }
+
+ protected boolean compareValuePair(Object val1, Object val2) {
+ boolean retVal = false;
+ if (val1 == null) {
+ if (val2 != null) retVal = false;
+ if (val2 == null) {
+ retVal = true;
+ }
+ } else {
+ retVal = val1.equals(val2);
+ }
+ return retVal;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public Boolean getTheDefault() {
+ return theDefault;
+ }
+ }
+
+ public boolean supportsAnnotation(String annotation) {
+ return annotation.equals(FacesValidator.class.getName());
+ }
+
+ public boolean supportsAnnotation(Class annotation) {
+ return annotation.equals(FacesValidator.class);
+ }
+
+
+ @Override
+ protected void addEntity(Class clazz, Map<String, Object> params) {
+ String value = (String) params.get(PAR_VALUE);
+ Boolean theDefault = (Boolean) params.get(PAR_DEFAULT);
+
+ AnnotationEntry entry = new AnnotationEntry(value, theDefault);
+ _alreadyRegistered.put(clazz.getName(), entry);
+ _inverseIndex.put(entry, clazz.getName());
+
+ getApplication().addValidator(entry.getValue(), clazz.getName());
+ }
+
+ @Override
+ protected boolean hasToReregister(Map params, Class clazz) {
+ String value = (String) params.get(PAR_VALUE);
+ Boolean theDefault = (Boolean) params.get(PAR_DEFAULT);
+
+ AnnotationEntry entry = new AnnotationEntry(value, theDefault);
+
+ AnnotationEntry alreadyRegistered = (AnnotationEntry) _alreadyRegistered.get(clazz.getName());
+ if (alreadyRegistered == null) {
+ return true;
+ }
+
+ return alreadyRegistered.equals(entry);
+ }
+
+ @Override
+ public void purge(String className) {
+ super.purge(className);
+ AnnotationEntry entry = (AnnotationEntry) _alreadyRegistered.get(className);
+ if (entry == null) {
+ return;
+ }
+
+ String oldValidator = _inverseIndex.get(entry);
+ if (oldValidator.equals(className)) {
+ _alreadyRegistered.remove(className);
+ getApplication().addValidator(entry.getValue(), PurgedValidator.class.getName());
+ _inverseIndex.put(entry, PurgedValidator.class.getName());
+ }
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedBehavior.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedBehavior.java
new file mode 100644
index 0000000..2bb37e4
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedBehavior.java
@@ -0,0 +1,33 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation.purged;
+
+import javax.faces.component.behavior.Behavior;
+import javax.faces.event.BehaviorEvent;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class PurgedBehavior implements Behavior {
+ public void broadcast(BehaviorEvent event) {
+ throw new RuntimeException("Behavior does not exist");
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedClientBehaviorRenderer.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedClientBehaviorRenderer.java
new file mode 100644
index 0000000..8555381
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedClientBehaviorRenderer.java
@@ -0,0 +1,30 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation.purged;
+
+import javax.faces.render.ClientBehaviorRenderer;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class PurgedClientBehaviorRenderer extends ClientBehaviorRenderer {
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedComponent.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedComponent.java
new file mode 100644
index 0000000..27ee379
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedComponent.java
@@ -0,0 +1,37 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation.purged;
+
+import javax.faces.component.UIOutput;
+
+/**
+ * We override the component from a real family so that
+ * so that myfaces can handle it in a decent way
+ * directly from UIComponent it would fail
+ * unless we implement everything family etc... all by our own
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class PurgedComponent extends UIOutput {
+ public PurgedComponent() {
+ super();
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedConverter.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedConverter.java
new file mode 100644
index 0000000..643e2a7
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedConverter.java
@@ -0,0 +1,41 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation.purged;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.convert.Converter;
+import javax.faces.convert.ConverterException;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class PurgedConverter implements Converter {
+ private static final String DOES_NOT_EXIST = "Converter does not exist";
+
+ public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedELResolver.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedELResolver.java
new file mode 100644
index 0000000..2fda69a
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedELResolver.java
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation.purged;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+import java.util.Iterator;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class PurgedELResolver extends ELResolver implements Decorated
+{
+
+ private final String DOES_NOT_EXIST = "EL Resolver does not exist";
+
+ ELResolver _delegate;
+
+ public PurgedELResolver(ELResolver delegate) {
+ _delegate = delegate;
+ }
+
+ @Override
+ public Object getValue(ELContext elContext, Object o, Object o1) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public Class getType(ELContext elContext, Object o, Object o1) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public void setValue(ELContext elContext, Object o, Object o1, Object o2) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public boolean isReadOnly(ELContext elContext, Object o, Object o1) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public Iterator getFeatureDescriptors(ELContext elContext, Object o) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public Class getCommonPropertyType(ELContext elContext, Object o) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ public ELResolver getDelegate() {
+ return _delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedLifecycle.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedLifecycle.java
new file mode 100644
index 0000000..c0cbb2c
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedLifecycle.java
@@ -0,0 +1,74 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation.purged;
+
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+
+import javax.faces.FacesException;
+import javax.faces.context.FacesContext;
+import javax.faces.event.PhaseListener;
+import javax.faces.lifecycle.Lifecycle;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class PurgedLifecycle extends Lifecycle implements Decorated
+{
+
+ private static final String DOES_NOT_EXIST = "Lifecycle does not exist";
+
+ Lifecycle _delegate;
+
+ public PurgedLifecycle(Lifecycle delegate) {
+ _delegate = delegate;
+ }
+
+ @Override
+ public void addPhaseListener(PhaseListener listener) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public void execute(FacesContext context) throws FacesException {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public PhaseListener[] getPhaseListeners() {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public void removePhaseListener(PhaseListener listener) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public void render(FacesContext context) throws FacesException {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ public Object getDelegate() {
+ return _delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedNavigationHandler.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedNavigationHandler.java
new file mode 100644
index 0000000..f728e7a
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedNavigationHandler.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation.purged;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+
+import javax.faces.application.NavigationHandler;
+import javax.faces.context.FacesContext;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class PurgedNavigationHandler extends NavigationHandler implements Decorated
+{
+
+ NavigationHandler _delegate;
+
+ public PurgedNavigationHandler(NavigationHandler delegate) {
+ _delegate = delegate;
+ }
+
+ @Override
+ public void handleNavigation(FacesContext context, String fromAction, String outcome) {
+ throw new RuntimeException("Navigation handler does not exist");
+ }
+
+ public NavigationHandler getDelegate() {
+ return _delegate;
+ }
+
+ public void setDelegate(NavigationHandler delegate) {
+ _delegate = delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedRenderer.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedRenderer.java
new file mode 100644
index 0000000..e875dc8
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedRenderer.java
@@ -0,0 +1,73 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation.purged;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.convert.ConverterException;
+import javax.faces.render.Renderer;
+import java.io.IOException;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class PurgedRenderer extends Renderer {
+ private static final String DOES_NOT_EXIST = "Renderer does not exist";
+
+ public PurgedRenderer() {
+ super();
+ }
+
+ @Override
+ public void decode(FacesContext context, UIComponent component) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public String convertClientId(FacesContext context, String clientId) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public boolean getRendersChildren() {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedRenderkit.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedRenderkit.java
new file mode 100644
index 0000000..cb9de2a
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedRenderkit.java
@@ -0,0 +1,80 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation.purged;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+
+import javax.faces.context.ResponseStream;
+import javax.faces.context.ResponseWriter;
+import javax.faces.render.RenderKit;
+import javax.faces.render.Renderer;
+import javax.faces.render.ResponseStateManager;
+import java.io.OutputStream;
+import java.io.Writer;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class PurgedRenderkit extends RenderKit implements Decorated
+{
+
+ private static final String DOES_NOT_EXIST = "Renderkit does not exist";
+
+ RenderKit _delegate;
+
+ public PurgedRenderkit(RenderKit delegate) {
+ _delegate = delegate;
+ }
+
+ @Override
+ public void addRenderer(String family, String rendererType, Renderer renderer) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public ResponseStream createResponseStream(OutputStream out) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public ResponseWriter createResponseWriter(Writer writer, String contentTypeList, String characterEncoding) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public Renderer getRenderer(String family, String rendererType) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public ResponseStateManager getResponseStateManager() {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ public RenderKit getDelegate() {
+ return _delegate;
+ }
+
+ public void setDelegate(RenderKit delegate) {
+ _delegate = delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedResourceHandler.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedResourceHandler.java
new file mode 100644
index 0000000..791edcf
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedResourceHandler.java
@@ -0,0 +1,86 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation.purged;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+
+import javax.faces.application.Resource;
+import javax.faces.application.ResourceHandler;
+import javax.faces.context.FacesContext;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class PurgedResourceHandler extends ResourceHandler implements Decorated
+{
+
+ private static final String DOES_NOT_EXIST = "Resource Handler does not exist";
+
+ ResourceHandler _delegate;
+
+ public PurgedResourceHandler(ResourceHandler delegate) {
+ _delegate = delegate;
+ }
+
+ @Override
+ public Resource createResource(String resourceName) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public Resource createResource(String resourceName, String libraryName) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public Resource createResource(String resourceName, String libraryName, String contentType) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public String getRendererTypeForResourceName(String resourceName) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public void handleResourceRequest(FacesContext context) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public boolean isResourceRequest(FacesContext context) {
+ throw new RuntimeException(DOES_NOT_EXIST);
+ }
+
+ @Override
+ public boolean libraryExists(String libraryName) {
+ return false;
+ }
+
+ public ResourceHandler getDelegate() {
+ return _delegate;
+ }
+
+ public void setDelegate(ResourceHandler delegate) {
+ _delegate = delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedValidator.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedValidator.java
new file mode 100644
index 0000000..61fd531
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/annotation/purged/PurgedValidator.java
@@ -0,0 +1,41 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.annotation.purged;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.validator.Validator;
+import javax.faces.validator.ValidatorException;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * Purged validator class to enable validator
+ * purging despite the fact
+ * that the original code does not allow it
+ */
+
+public class PurgedValidator implements Validator {
+
+ public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
+ throw new RuntimeException("Validator does not exist");
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/components/CompilerComponent.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/components/CompilerComponent.java
new file mode 100644
index 0000000..542f037
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/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 org.apache.myfaces.extensions.scripting.jsf.components;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import 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/org/apache/myfaces/extensions/scripting/jsf/components/CompilerComponentRenderer.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/components/CompilerComponentRenderer.java
new file mode 100644
index 0000000..523b947
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/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 org.apache.myfaces.extensions.scripting.jsf.components;
+
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.StringUtils;
+import 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/org/apache/myfaces/extensions/scripting/jsf/components/RendererConst.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/components/RendererConst.java
new file mode 100644
index 0000000..a25883b
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/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 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/org/apache/myfaces/extensions/scripting/jsf/components/TaintHistory.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/components/TaintHistory.java
new file mode 100644
index 0000000..10d9049
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/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 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/org/apache/myfaces/extensions/scripting/jsf/components/TaintHistoryRenderer.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/components/TaintHistoryRenderer.java
new file mode 100644
index 0000000..fcfcbfe
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/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 org.apache.myfaces.extensions.scripting.jsf.components;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import 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/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/factories/ScriptingApplicationFactory.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/factories/ScriptingApplicationFactory.java
new file mode 100644
index 0000000..8b271d1
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/factories/ScriptingApplicationFactory.java
@@ -0,0 +1,78 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.factories;
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations.ApplicationProxy;
+
+import javax.faces.application.Application;
+import javax.faces.application.ApplicationFactory;
+
+/**
+ * Application factory which introduces
+ * scripting proxies for their artefacts
+ * <p/>
+ * We use a mix of AOP and helper constructs
+ * to reach the goal to be dynamic.
+ * For most artefacts we just need to
+ * check if the object is a Groovy object
+ * and then reload at their connection interfaces
+ * <p/>
+ * Some artefacts have a longer lifespan and/or are stateless
+ * for those we have to work with reloading AOP
+ *
+ * @author Werner Punz
+ */
+public class ScriptingApplicationFactory extends ApplicationFactory implements Decorated
+{
+
+ ApplicationFactory _delegate;
+
+
+ public ScriptingApplicationFactory(ApplicationFactory delegate) {
+ _delegate = delegate;
+
+ }
+
+ public Application getApplication() {
+ Application retVal = _delegate.getApplication();
+
+ if (WeavingContext.getInstance().isScriptingEnabled() && !(retVal instanceof ApplicationProxy))
+ retVal = new ApplicationProxy(retVal);
+
+ return retVal;
+ }
+
+ public void setApplication(Application application) {
+ if (WeavingContext.getInstance().isScriptingEnabled() && !(application instanceof ApplicationProxy))
+ application = new ApplicationProxy(application);
+
+ _delegate.setApplication(application);
+ }
+
+ @Override
+ public ApplicationFactory getWrapped() {
+ return _delegate.getWrapped();
+ }
+
+ public Object getDelegate() {
+ return _delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/factories/ScriptingFacesContextFactory.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/factories/ScriptingFacesContextFactory.java
new file mode 100644
index 0000000..09e058e
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/factories/ScriptingFacesContextFactory.java
@@ -0,0 +1,66 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.factories;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations.FacesContextProxy;
+
+import javax.faces.FacesException;
+import javax.faces.context.FacesContext;
+import javax.faces.context.FacesContextFactory;
+import javax.faces.lifecycle.Lifecycle;
+
+/**
+ * Faces context weaver which builds
+ * our reloading proxy around the current faces context
+ *
+ * @author Werner Punz
+ */
+public class ScriptingFacesContextFactory extends FacesContextFactory implements Decorated
+{
+
+ public FacesContextFactory _delegate;
+
+ public ScriptingFacesContextFactory(FacesContextFactory delegate) {
+ _delegate = delegate;
+ }
+
+ public void setDelegate(FacesContextFactory delegate) {
+ _delegate = delegate;
+ }
+
+ public FacesContext getFacesContext(Object o, Object o1, Object o2, Lifecycle lifecycle) throws FacesException {
+ FacesContext retVal = _delegate.getFacesContext(o, o1, o2, lifecycle);
+
+ if (WeavingContext.getInstance().isScriptingEnabled() && !(retVal instanceof FacesContextProxy))
+ return new FacesContextProxy(retVal);
+ return retVal;
+ }
+
+ @Override
+ public FacesContextFactory getWrapped() {
+ return _delegate.getWrapped();
+ }
+
+ public Object getDelegate() {
+ return _delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/factories/ScriptingLifecycleFactory.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/factories/ScriptingLifecycleFactory.java
new file mode 100644
index 0000000..ccdeeb5
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/factories/ScriptingLifecycleFactory.java
@@ -0,0 +1,75 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.factories;
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations.LifefcycleProxy;
+
+import javax.faces.lifecycle.Lifecycle;
+import javax.faces.lifecycle.LifecycleFactory;
+import java.util.Iterator;
+
+/**
+ * Lifecyclefactory which introduces scripting proxies
+ * for their artefacts
+ *
+ * @author Werner Punz
+ */
+public class ScriptingLifecycleFactory extends LifecycleFactory implements Decorated
+{
+
+ LifecycleFactory _delegate;
+
+
+ public ScriptingLifecycleFactory(LifecycleFactory delegate) {
+ _delegate = delegate;
+ }
+
+ public void addLifecycle(String s, Lifecycle lifecycle) {
+ if (WeavingContext.getInstance().isScriptingEnabled() && !(lifecycle instanceof LifefcycleProxy))
+ lifecycle = new LifefcycleProxy(lifecycle);
+ _delegate.addLifecycle(s, lifecycle);
+ }
+
+ public Lifecycle getLifecycle(String s) {
+ Lifecycle retVal = _delegate.getLifecycle(s);
+ if (WeavingContext.getInstance().isScriptingEnabled() && !(retVal instanceof LifefcycleProxy))
+ retVal = new LifefcycleProxy(retVal);
+
+ return retVal;
+ }
+
+ public Iterator getLifecycleIds() {
+ return _delegate.getLifecycleIds();
+ }
+
+ public void setDelegate(LifecycleFactory delegate) {
+ this._delegate = delegate;
+ }
+
+ @Override
+ public LifecycleFactory getWrapped() {
+ return _delegate.getWrapped();
+ }
+
+ public Object getDelegate() {
+ return _delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/factories/ScriptingRenderkitFactory.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/factories/ScriptingRenderkitFactory.java
new file mode 100644
index 0000000..9421fed
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/factories/ScriptingRenderkitFactory.java
@@ -0,0 +1,76 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.factories;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations.RenderkitProxy;
+
+import javax.faces.context.FacesContext;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitFactory;
+import java.util.Iterator;
+
+/**
+ * Scripting enabled renderkit factory
+ *
+ * @author Werner Punz
+ */
+public class ScriptingRenderkitFactory extends RenderKitFactory implements Decorated
+{
+
+
+ public ScriptingRenderkitFactory(RenderKitFactory delegate) {
+ _delegate = delegate;
+ }
+
+ public void addRenderKit(String s, RenderKit renderKit) {
+ if (WeavingContext.getInstance().isScriptingEnabled() && renderKit != null && !(renderKit instanceof RenderkitProxy))
+ renderKit = new RenderkitProxy(renderKit);
+
+ _delegate.addRenderKit(s, renderKit);
+ }
+
+ public RenderKit getRenderKit(FacesContext facesContext, String s) {
+ RenderKit retVal = _delegate.getRenderKit(facesContext, s);
+ if (WeavingContext.getInstance().isScriptingEnabled() && retVal != null && !(retVal instanceof RenderkitProxy))
+ retVal = new RenderkitProxy(retVal);
+ return retVal;
+ }
+
+ public Iterator getRenderKitIds() {
+ return _delegate.getRenderKitIds();
+ }
+
+ public void setDelegate(RenderKitFactory delegate) {
+ _delegate = delegate;
+ }
+
+ @Override
+ public RenderKitFactory getWrapped() {
+ return _delegate.getWrapped();
+ }
+
+ RenderKitFactory _delegate = null;
+
+ public Object getDelegate() {
+ return _delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/ApplicationProxy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/ApplicationProxy.java
new file mode 100644
index 0000000..643c05c
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/ApplicationProxy.java
@@ -0,0 +1,957 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations;
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.el.*;
+import javax.faces.FacesException;
+import javax.faces.application.*;
+import javax.faces.component.UIComponent;
+import javax.faces.component.behavior.Behavior;
+import javax.faces.context.FacesContext;
+import javax.faces.convert.Converter;
+import javax.faces.el.*;
+import javax.faces.event.ActionListener;
+import javax.faces.event.SystemEvent;
+import javax.faces.event.SystemEventListener;
+import javax.faces.validator.Validator;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.apache.myfaces.extensions.scripting.core.api.ScriptingConst.*;
+
+/**
+ * @author Werner Punz
+ * <p/>
+ * our decorating applicstion
+ * which should resolve our bean issues within a central
+ * bean processing interceptor
+ */
+public class ApplicationProxy extends Application implements Decorated
+{
+
+ Application _delegate = null;
+
+ /*
+ * separate map needed for the behavior ids, because
+ * the original is immutable
+ * we have to do a double bookkeeping
+ * here
+ */
+ Map<String, String> _behaviors = new ConcurrentHashMap<String, String>();
+
+ /**
+ * special data structure to save our
+ * object -> proxy references
+ */
+ class EventHandlerProxyEntry
+ {
+ Class event;
+ Decorated proxy;
+
+ EventHandlerProxyEntry(Class event, Decorated proxy)
+ {
+ this.event = event;
+ this.proxy = proxy;
+ }
+
+ @SuppressWarnings("unused")
+ public Class getEvent()
+ {
+ return event;
+ }
+
+ @SuppressWarnings("unused")
+ public void setEvent(Class event)
+ {
+ this.event = event;
+ }
+
+ public Decorated getProxy()
+ {
+ return proxy;
+ }
+
+ @SuppressWarnings("unused")
+ public void setProxy(Decorated proxy)
+ {
+ this.proxy = proxy;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ EventHandlerProxyEntry that = (EventHandlerProxyEntry) o;
+
+ return !(event != null ? !event.equals(that.event) : that.event != null) && !(proxy != null ? !proxy.getDelegate().getClass().getName().equals(that.proxy.getDelegate().getClass().getName()) : that.proxy != null);
+
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = event.hashCode();
+ result = 31 * result + proxy.getDelegate().getClass().getName().hashCode();
+ return result;
+ }
+ }
+
+ /**
+ * now at the first look this looks like a weird construct
+ * but the standard java set imposes this limit since
+ * we have to iterate over the entire set to reach the correct element
+ * the trick is to save the same object in as both key and value
+ * and now if we generate a new key on an object
+ * we can fetch our proxy which might already contain
+ * the same object in a refreshed state from the value
+ * part of the set, in our case
+ * using hash maps should speed things up
+ * <p/>
+ * since we only have few write operations but access
+ * the map multithreaded we use concurrentHashMap here
+ */
+ Map<EventHandlerProxyEntry, EventHandlerProxyEntry> _eventHandlerIdx = new ConcurrentHashMap<EventHandlerProxyEntry, EventHandlerProxyEntry>();
+
+ volatile static boolean varResolverAdded = false;
+
+ ELResolverProxy finalResolver = null;
+
+ public ApplicationProxy(Application delegate)
+ {
+ _delegate = delegate;
+ }
+
+ public void addELResolver(ELResolver elResolver)
+ {
+ weaveDelegate();
+ //we do not need a proxy here anymore because
+ //we drop the beans directly
+ _delegate.addELResolver(elResolver);
+ }
+
+ private void weaveDelegate()
+ {
+ if (_delegate != null)
+ {
+ _delegate = (Application) WeavingContext.getInstance().reload(_delegate,
+ ARTIFACT_TYPE_APPLICATION);
+ }
+ }
+
+ public ELResolver getELResolver()
+ {
+ weaveDelegate();
+
+ ELResolver retVal = _delegate.getELResolver();
+ return retVal;
+ }
+
+ //TOD add a weaving for resource bundles
+
+ public ResourceBundle getResourceBundle(FacesContext facesContext, String s) throws FacesException, NullPointerException
+ {
+ weaveDelegate();
+ return _delegate.getResourceBundle(facesContext, s);
+ }
+
+ public UIComponent createComponent(ValueExpression valueExpression, FacesContext facesContext, String componentType) throws FacesException, NullPointerException
+ {
+ weaveDelegate();
+ UIComponent component = _delegate.createComponent(valueExpression, facesContext, componentType);
+ UIComponent oldComponent = component;
+ //We can replace annotated components on the fly via
+ //ApplicationImpl.addComponent(final String componentType, final String componentClassName)
+
+ /*we are reweaving on the fly because we cannot be sure if
+ * the class is not recycled all the time in the creation
+ * code, in the renderer we do it on method base
+ * due to the fact that our renderers are recycled via
+ * a flyweight pattern
+ *
+ *
+ * Also we cannot proxy here because there is no UIComponent interface
+ * maybe in the long run we can make a decorator here instead
+ * but for now lets try it this way
+ */
+ component = (UIComponent) reloadInstance(component, ARTIFACT_TYPE_COMPONENT);
+
+ //we now have to check for an annotation change, but only in case a reload has happened
+ /*<> if (component.getClass().hashCode() != oldComponent.getClass().hashCode())
+ {
+ return handleAnnotationChange(component, valueExpression, facesContext, componentType);
+ } */
+
+ return component;
+
+ }
+
+ public ExpressionFactory getExpressionFactory()
+ {
+ weaveDelegate();
+ return _delegate.getExpressionFactory();
+ }
+
+ public void addELContextListener(ELContextListener elContextListener)
+ {
+ weaveDelegate();
+ if (WeavingContext.getInstance().isDynamic(elContextListener.getClass()))
+ elContextListener = (ELContextListener) WeavingContext.getInstance().createMethodReloadingProxyFromObject
+ (elContextListener, ELContextListener.class, ARTIFACT_TYPE_ELCONTEXTLISTENER);
+ _delegate.addELContextListener(elContextListener);
+ }
+
+ public void removeELContextListener(ELContextListener elContextListener)
+ {
+ weaveDelegate();
+ _delegate.removeELContextListener(elContextListener);
+ }
+
+ public ELContextListener[] getELContextListeners()
+ {
+ weaveDelegate();
+ return _delegate.getELContextListeners();
+ }
+
+ public ActionListener getActionListener()
+ {
+ weaveDelegate();
+ ActionListener retVal = _delegate.getActionListener();
+ if (WeavingContext.getInstance().isDynamic(retVal.getClass()))
+ retVal = (ActionListener) WeavingContext.getInstance().createMethodReloadingProxyFromObject(retVal,
+ ActionListener.class, ARTIFACT_TYPE_ACTIONLISTENER);
+ return retVal;
+ }
+
+ public void setActionListener(ActionListener actionListener)
+ {
+ weaveDelegate();
+ if (WeavingContext.getInstance().isDynamic(actionListener.getClass()))
+ actionListener = (ActionListener) WeavingContext.getInstance().createMethodReloadingProxyFromObject(actionListener,
+ ActionListener.class, ARTIFACT_TYPE_ACTIONLISTENER);
+ _delegate.setActionListener(actionListener);
+ }
+
+ public Locale getDefaultLocale()
+ {
+ weaveDelegate();
+ return _delegate.getDefaultLocale();
+ }
+
+ public void setDefaultLocale(Locale locale)
+ {
+ weaveDelegate();
+ _delegate.setDefaultLocale(locale);
+ }
+
+ public String getDefaultRenderKitId()
+ {
+ weaveDelegate();
+ return _delegate.getDefaultRenderKitId();
+ }
+
+ public void setDefaultRenderKitId(String s)
+ {
+ weaveDelegate();
+ _delegate.setDefaultRenderKitId(s);
+ }
+
+ public String getMessageBundle()
+ {
+ weaveDelegate();
+ return _delegate.getMessageBundle();
+ }
+
+ public void setMessageBundle(String s)
+ {
+ weaveDelegate();
+ _delegate.setMessageBundle(s);
+ }
+
+ public NavigationHandler getNavigationHandler()
+ {
+ weaveDelegate();
+ //defined in the setter to speed things up a little
+ NavigationHandler retVal = _delegate.getNavigationHandler();
+
+ //if (retVal != null && WeavingContext.isDynamic(retVal.getClass()))
+ // retVal = new NavigationHandlerProxy(retVal);
+ return retVal;
+ }
+
+ public void setNavigationHandler(NavigationHandler navigationHandler)
+ {
+ weaveDelegate();
+
+ if (navigationHandler != null && WeavingContext.getInstance().isDynamic(navigationHandler.getClass()))
+ navigationHandler = new NavigationHandlerProxy(navigationHandler);
+ _delegate.setNavigationHandler(navigationHandler);
+ }
+
+ @SuppressWarnings("deprecation")
+ public PropertyResolver getPropertyResolver()
+ {
+ weaveDelegate();
+ return _delegate.getPropertyResolver();
+ }
+
+ @SuppressWarnings("deprecation")
+ public void setPropertyResolver(PropertyResolver propertyResolver)
+ {
+ weaveDelegate();
+ _delegate.setPropertyResolver(propertyResolver);
+ }
+
+ @SuppressWarnings("deprecation")
+ public VariableResolver getVariableResolver()
+ {
+ weaveDelegate();
+ return _delegate.getVariableResolver();
+ }
+
+ @SuppressWarnings("deprecation")
+ public void setVariableResolver(VariableResolver variableResolver)
+ {
+ weaveDelegate();
+ if (!varResolverAdded)
+ {
+ variableResolver = new VariableResolverProxy(variableResolver);
+ varResolverAdded = true;
+ }
+ _delegate.setVariableResolver(variableResolver);
+ }
+
+ public ViewHandler getViewHandler()
+ {
+ weaveDelegate();
+ ViewHandler handler = _delegate.getViewHandler();
+
+ /*
+ We proxy here to enable dynamic reloading for
+ methods in the long run, as soon as we hit
+ java all our groovy reloading code is lost
+ hence we have to work with proxies here
+ */
+ if (WeavingContext.getInstance().isDynamic(handler.getClass()))
+ handler = new ViewHandlerProxy(handler);
+ return handler;
+ }
+
+ public void setViewHandler(ViewHandler viewHandler)
+ {
+ weaveDelegate();
+ /*make sure you have the delegates as well in properties*/
+ if (WeavingContext.getInstance().isDynamic(viewHandler.getClass()))
+ viewHandler = new ViewHandlerProxy(viewHandler);
+
+ _delegate.setViewHandler(viewHandler);
+ }
+
+ public StateManager getStateManager()
+ {
+ weaveDelegate();
+ return _delegate.getStateManager();
+ }
+
+ public void setStateManager(StateManager stateManager)
+ {
+ weaveDelegate();
+ _delegate.setStateManager(stateManager);
+ }
+
+ public void addComponent(String componentType, String componentClass)
+ {
+ weaveDelegate();
+ _delegate.addComponent(componentType, componentClass);
+ }
+
+ public UIComponent createComponent(String componentType) throws FacesException
+ {
+ weaveDelegate();
+ //the components are generated anew very often
+ //we cannot do an on object weaving here
+ UIComponent oldComponent = _delegate.createComponent(componentType);
+
+ /*we are reweaving on the fly because we cannot be sure if
+ * the class is not recycled all the time in the creation
+ * code, in the renderer we do it on method base
+ * due to the fact that our renderers are recycled via
+ * a flyweight pattern*/
+ UIComponent component = (UIComponent) reloadInstance(oldComponent, ARTIFACT_TYPE_COMPONENT);
+
+ //we now have to check for an annotation change, but only in case a reload has happened
+ /*<> if (component.getClass().hashCode() != oldComponent.getClass().hashCode())
+ {
+ return handleAnnotationChange(component, componentType);
+ } */
+
+ return component;
+
+ }
+
+ @SuppressWarnings("deprecation")
+ public UIComponent createComponent(ValueBinding valueBinding, FacesContext facesContext, String componentType) throws FacesException
+ {
+ weaveDelegate();
+ UIComponent oldComponent = _delegate.createComponent(valueBinding, facesContext, componentType);
+
+ /*we are reweaving on the fly because we cannot be sure if
+ * the class is not recycled all the time in the creation
+ * code, in the renderer we do it on method base
+ * due to the fact that our renderers are recycled via
+ * a flyweight pattern*/
+ UIComponent component = (UIComponent) reloadInstance(oldComponent, ARTIFACT_TYPE_COMPONENT);
+
+ //we now have to check for an annotation change, but only in case a reload has happened
+ /*<>if (component.getClass().hashCode() != oldComponent.getClass().hashCode())
+ {
+ return handleAnnotationChange(component, valueBinding, facesContext, componentType);
+ }*/
+
+ return component;
+ }
+
+ public Iterator<String> getComponentTypes()
+ {
+ weaveDelegate();
+ return _delegate.getComponentTypes();
+ }
+
+ public void addConverter(String converterId, String converterClass)
+ {
+ weaveDelegate();
+ /* if (converterClass.equals(PurgedConverter.class.getName())) {
+ //purged case we do a full rescan
+ WeavingContext.getWeaver().fullClassScan();
+ Converter componentToChange = _delegate.createConverter(converterId);
+ if (componentToChange instanceof PurgedConverter) {
+ //Null not allowed here, but we set a purted converter to make
+ //sure that we get errors on the proper level
+ _delegate.addConverter(converterId, PurgedConverter.class.getName());
+ }
+ return;
+ }*/
+
+ _delegate.addConverter(converterId, converterClass);
+ }
+
+ public void addConverter(Class targetClass, String converterClass)
+ {
+ weaveDelegate();
+ _delegate.addConverter(targetClass, converterClass);
+ }
+
+ public Converter createConverter(String converterId)
+ {
+ weaveDelegate();
+ Converter retVal = _delegate.createConverter(converterId);
+
+ /**
+ * since createConverter is called only once
+ * we have to work with method reloading proxies
+ * we cannot use this technique extensively for speed reasons
+ * most of the time it is fine just to work with
+ *
+ * reloading objects at their interception points
+ */
+ Converter newRetVal = (Converter) reloadInstance(retVal, ARTIFACT_TYPE_CONVERTER);
+ if (newRetVal != retVal)
+ {
+ return _delegate.createConverter(converterId);
+ }
+
+ return retVal;
+ }
+
+ public Converter createConverter(Class aClass)
+ {
+ weaveDelegate();
+ Converter retVal = _delegate.createConverter(aClass);
+ Converter newRetVal = (Converter) reloadInstance(retVal, ARTIFACT_TYPE_CONVERTER);
+ if (newRetVal != retVal)
+ {
+ return _delegate.createConverter(aClass);
+ }
+
+ return retVal;
+ }
+
+ public Iterator<String> getConverterIds()
+ {
+ weaveDelegate();
+ return _delegate.getConverterIds();
+ }
+
+ public Iterator<Class<?>> getConverterTypes()
+ {
+ weaveDelegate();
+ return _delegate.getConverterTypes();
+ }
+
+ @SuppressWarnings("deprecation")
+ public MethodBinding createMethodBinding(String s, Class[] classes) throws ReferenceSyntaxException
+ {
+ weaveDelegate();
+ return _delegate.createMethodBinding(s, classes);
+ }
+
+ public Iterator<Locale> getSupportedLocales()
+ {
+ weaveDelegate();
+ return _delegate.getSupportedLocales();
+ }
+
+ public void setSupportedLocales(Collection<Locale> locales)
+ {
+ weaveDelegate();
+ _delegate.setSupportedLocales(locales);
+ }
+
+ public void addValidator(String validatorId, String validatorClass)
+ {
+ weaveDelegate();
+/* if (validatorClass.equals(PurgedValidator.class.getName())) {
+ //purged case we do a full rescane
+ WeavingContext.getWeaver().fullClassScan();
+ Validator componentToChange = _delegate.createValidator(validatorId);
+ if (componentToChange instanceof PurgedValidator) {
+ //Null not allowed here, but we set a purted validator to make
+ //sure that we get errors on the proper level
+ _delegate.addValidator(validatorId, PurgedValidator.class.getName());
+
+ }
+ return;
+ } */
+ _delegate.addValidator(validatorId, validatorClass);
+ }
+
+ public Validator createValidator(String validatorId) throws FacesException
+ {
+ weaveDelegate();
+
+ Validator retVal = _delegate.createValidator(validatorId);
+
+ //the validators are recreated every request we do not have to deal with them on method level
+ Validator newRetVal = (Validator) reloadInstance(retVal, ARTIFACT_TYPE_VALIDATOR);
+ if (newRetVal != retVal)
+ {
+ _delegate.createValidator(validatorId);
+ }
+ return retVal;
+ }
+
+ public Iterator<String> getValidatorIds()
+ {
+ weaveDelegate();
+ return _delegate.getValidatorIds();
+ }
+
+ @SuppressWarnings("deprecation")
+ public ValueBinding createValueBinding(String s) throws ReferenceSyntaxException
+ {
+ weaveDelegate();
+ return _delegate.createValueBinding(s);
+ }
+
+
+ /*<> @Override public void addBehavior(String behaviorId, String behaviorClass)
+ {
+ weaveDelegate();
+
+ if (behaviorClass.equals(PurgedValidator.class.getName()))
+ {
+ //purged case we do a full rescan
+ WeavingContext.getInstance().getWeaver().fullClassScan();
+ Behavior behavior = _delegate.createBehavior(behaviorId);
+ _behaviors.put(behaviorId, behaviorClass);
+ if (behavior instanceof PurgedBehavior)
+ {
+ //Null not allowed here, but we set a purged validator to make
+ //sure that we get errors on the proper level
+ _delegate.addBehavior(behaviorId, PurgedBehavior.class.getName());
+ _behaviors.remove(behaviorId);
+
+ }
+ return;
+ }
+
+ _delegate.addBehavior(behaviorId, behaviorClass);
+ } */
+
+ @Override
+ public void addDefaultValidatorId(String validatorId)
+ {
+ weaveDelegate();
+ _delegate.addDefaultValidatorId(validatorId);
+ }
+
+ @Override
+ public Behavior createBehavior(String behaviorId) throws FacesException
+ {
+ weaveDelegate();
+ Behavior retVal = _delegate.createBehavior(behaviorId);
+
+ //we might have casts here against one of the parents
+ //of this object
+ Behavior newBehavior = (Behavior) reloadInstance(retVal, ARTIFACT_TYPE_BEHAVIOR);
+ if (newBehavior != retVal)
+ {
+ return _delegate.createBehavior(behaviorId);
+ }
+
+ return retVal;
+ }
+
+ @Override
+ public UIComponent createComponent(FacesContext facesContext, Resource resource)
+ {
+ weaveDelegate();
+
+ UIComponent oldComponent = _delegate.createComponent(facesContext, resource);
+
+ /*we are reweaving on the fly because we cannot be sure if
+ * the class is not recycled all the time in the creation
+ * code, in the renderer we do it on method base
+ * due to the fact that our renderers are recycled via
+ * a flyweight pattern*/
+ UIComponent component = (UIComponent) reloadInstance(oldComponent, ARTIFACT_TYPE_COMPONENT);
+
+ //we now have to check for an annotation change, but only in case a reload has happened
+ /*<>if (component.getClass().hashCode() != oldComponent.getClass().hashCode())
+ {
+ return handleAnnotationChange(component, facesContext, resource);
+ }*/
+
+ return component;
+
+ }
+
+ @Override
+ public UIComponent createComponent(FacesContext facesContext, String componentType, String rendererType)
+ {
+ weaveDelegate();
+ UIComponent oldComponent = _delegate.createComponent(facesContext, componentType, rendererType);
+
+ /*we are reweaving on the fly because we cannot be sure if
+ * the class is not recycled all the time in the creation
+ * code, in the renderer we do it on method base
+ * due to the fact that our renderers are recycled via
+ * a flyweight pattern*/
+ UIComponent component = (UIComponent) reloadInstance(oldComponent, ARTIFACT_TYPE_COMPONENT);
+
+ //we now have to check for an annotation change, but only in case a reload has happened
+ /*<>if (component.getClass().hashCode() != oldComponent.getClass().hashCode())
+ {
+ return handleAnnotationChange(component, facesContext, componentType, rendererType);
+ } */
+
+ return component;
+ }
+
+ @Override
+ public UIComponent createComponent(ValueExpression valueExpression, FacesContext facesContext, String s, String s1)
+ {
+ weaveDelegate();
+ UIComponent oldComponent = _delegate.createComponent(valueExpression, facesContext, s, s1);
+
+ /*we are reweaving on the fly because we cannot be sure if
+ * the class is not recycled all the time in the creation
+ * code, in the renderer we do it on method base
+ * due to the fact that our renderers are recycled via
+ * a flyweight pattern*/
+ UIComponent component = (UIComponent) reloadInstance(oldComponent, ARTIFACT_TYPE_COMPONENT);
+
+ //we now have to check for an annotation change, but only in case a reload has happened
+ /*if (component.getClass().hashCode() != oldComponent.getClass().hashCode())
+ {
+ return handleAnnotationChange(component, valueExpression, facesContext, s, s1);
+ } <>*/
+
+ return component;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T evaluateExpressionGet(FacesContext facesContext, String s, Class<? extends T> aClass) throws ELException
+ {
+ weaveDelegate();
+ //good place for a dynamic reloading check as well
+ T retVal = _delegate.evaluateExpressionGet(facesContext, s, aClass);
+ if (WeavingContext.getInstance().isDynamic(retVal.getClass()))
+ retVal = (T) WeavingContext.getInstance().reload(retVal, ARTIFACT_TYPE_MANAGEDBEAN);
+ return retVal;
+ }
+
+ @Override
+ public Iterator<String> getBehaviorIds()
+ {
+ weaveDelegate();
+ return _behaviors.keySet().iterator();
+ //return _delegate.getBehaviorIds();
+ }
+
+ @Override
+ public Map<String, String> getDefaultValidatorInfo()
+ {
+ weaveDelegate();
+ return _delegate.getDefaultValidatorInfo();
+ }
+
+ @Override
+ public ProjectStage getProjectStage()
+ {
+ weaveDelegate();
+ return _delegate.getProjectStage();
+ }
+
+ @Override
+ public ResourceHandler getResourceHandler()
+ {
+ weaveDelegate();
+ ResourceHandler retVal = _delegate.getResourceHandler();
+
+ /*if (WeavingContext.isDynamic(retVal.getClass())) {
+ ResourceHandler newHandler = (ResourceHandler) reloadInstance(retVal, ScriptingConst.ARTIFACT_TYPE_RESOURCEHANDLER);
+ if (newHandler != retVal) {
+ _delegate.setResourceHandler(newHandler);
+ return newHandler;
+ }
+ } */
+ return retVal;
+ }
+
+ @Override
+ public void publishEvent(FacesContext facesContext, Class<? extends SystemEvent> eventClass, Class<?> sourceBaseTye, Object source)
+ {
+ weaveDelegate();
+ _delegate.publishEvent(facesContext, eventClass, sourceBaseTye, source);
+ }
+
+ @Override
+ public void publishEvent(FacesContext facesContext, Class<? extends SystemEvent> eventClass, Object source)
+ {
+ weaveDelegate();
+ _delegate.publishEvent(facesContext, eventClass, source);
+ }
+
+ @Override
+ public void setResourceHandler(ResourceHandler resourceHandler)
+ {
+ weaveDelegate();
+ if (WeavingContext.getInstance().isDynamic(resourceHandler.getClass()))
+ {
+ ResourceHandler proxy = new ResourceHandlerProxy(resourceHandler);
+ resourceHandler = proxy;
+ }
+
+ _delegate.setResourceHandler(resourceHandler);
+ //ResourceHandler handler = _delegate.getResourceHandler();
+ //if (handler instanceof PurgedResourceHandler) {
+ // WeavingContext.getWeaver().fullClassScan();
+ //}
+ }
+
+ @Override
+ public void subscribeToEvent(Class<? extends SystemEvent> eventClass, Class<?> aClass, SystemEventListener systemEventListener)
+ {
+ weaveDelegate();
+ systemEventListener = makeEventProxy(eventClass, systemEventListener);
+ _delegate.subscribeToEvent(eventClass, aClass, systemEventListener);
+ }
+
+ private SystemEventListener makeEventProxy(Class<? extends SystemEvent> eventClass, SystemEventListener systemEventListener)
+ {
+ if (WeavingContext.getInstance().isDynamic(systemEventListener.getClass()))
+ {
+ systemEventListener = new SystemEventListenerProxy(systemEventListener);
+ EventHandlerProxyEntry entry = new EventHandlerProxyEntry(eventClass, (Decorated) systemEventListener);
+ _eventHandlerIdx.put(entry, entry);
+ }
+ return systemEventListener;
+ }
+
+ @Override
+ public void subscribeToEvent(Class<? extends SystemEvent> eventClass, SystemEventListener systemEventListener)
+ {
+ weaveDelegate();
+ systemEventListener = makeEventProxy(eventClass, systemEventListener);
+ _delegate.subscribeToEvent(eventClass, systemEventListener);
+ }
+
+ @Override
+ public void unsubscribeFromEvent(Class<? extends SystemEvent> eventClass, Class<?> aClass, SystemEventListener systemEventListener)
+ {
+ weaveDelegate();
+ systemEventListener = resolveEventProxy(eventClass, systemEventListener);
+ _delegate.unsubscribeFromEvent(eventClass, aClass, systemEventListener);
+ }
+
+ private SystemEventListener resolveEventProxy(Class<? extends SystemEvent> eventClass, SystemEventListener systemEventListener)
+ {
+ if (WeavingContext.getInstance().isDynamic(systemEventListener.getClass()))
+ {
+ systemEventListener = new SystemEventListenerProxy(systemEventListener);
+ EventHandlerProxyEntry entry = new EventHandlerProxyEntry(eventClass, (Decorated) systemEventListener);
+ entry = _eventHandlerIdx.remove(entry);
+ if (entry != null)
+ {
+ systemEventListener = (SystemEventListener) entry.getProxy().getDelegate();
+ }
+ }
+ return systemEventListener;
+ }
+
+ @Override
+ public void unsubscribeFromEvent(Class<? extends SystemEvent> eventClass, SystemEventListener systemEventListener)
+ {
+ weaveDelegate();
+ systemEventListener = resolveEventProxy(eventClass, systemEventListener);
+ _delegate.unsubscribeFromEvent(eventClass, systemEventListener);
+ }
+
+ public Object getDelegate()
+ {
+ return _delegate;
+ }
+
+ private Object reloadInstance(Object instance, int artifactType)
+ {
+ if (instance == null)
+ {
+ return null;
+ }
+ if (WeavingContext.getInstance().isDynamic(instance.getClass()))
+ {
+ instance = WeavingContext.getInstance().reload(instance, artifactType);
+ }
+ return instance;
+ }
+
+ /* private boolean alreadyWovenInRequest(String clazz)
+ {
+ //portlets now can be enabled thanks to the jsf2 indirections regarding the external context
+ Map<String, Object> req = WeavingContext.getRequestMap();
+ if (req.get(SCRIPTING_REQUSINGLETON + clazz) == null)
+ {
+ req.put(SCRIPTING_REQUSINGLETON + clazz, "");
+ return false;
+ }
+ return true;
+ }*/
+
+ /*<> private UIComponent handleAnnotationChange(UIComponent oldComponent, ValueExpression valueExpression,
+ FacesContext facesContext, String componentType)
+ {
+ UIComponent componentToChange = _delegate.createComponent(valueExpression, facesContext, componentType);
+ if (componentToChange instanceof PurgedComponent)
+ {
+ WeavingContext.getWeaver().fullClassScan();
+ //via an additional create component we can check whether a purged component
+ //was registered after the reload because the annotation has been removed
+ componentToChange = _delegate.createComponent(valueExpression, facesContext, componentType);
+
+ return componentToChange;
+ }
+ return oldComponent;
+ } */
+
+ /*<> private UIComponent handleAnnotationChange(UIComponent oldComponent, String componentType)
+ {
+ UIComponent componentToChange = _delegate.createComponent(componentType);
+ if (componentToChange instanceof PurgedComponent)
+ {
+ WeavingContext.getWeaver().fullClassScan();
+ //via an additional create component we can check whether a purged component
+ //was registered after the reload because the annotation has been removed
+ componentToChange = _delegate.createComponent(componentType);
+
+ return componentToChange;
+ }
+ return oldComponent;
+ }*/
+
+
+ /*<>@SuppressWarnings("deprecation") private UIComponent handleAnnotationChange(UIComponent oldComponent, ValueBinding valueBinding,
+ FacesContext context, String componentType)
+ {
+ UIComponent componentToChange = _delegate.createComponent(valueBinding, context, componentType);
+ if (componentToChange instanceof PurgedComponent)
+ {
+ WeavingContext.getWeaver().fullClassScan();
+ //via an additional create component we can check whether a purged component
+ //was registered after the reload because the annotation has been removed
+ componentToChange = _delegate.createComponent(valueBinding, context, componentType);
+
+ return componentToChange;
+ }
+ return oldComponent;
+ } */
+
+ /*<> private UIComponent handleAnnotationChange(UIComponent oldComponent, FacesContext context, Resource resource)
+ {
+ UIComponent componentToChange = _delegate.createComponent(context, resource);
+ if (componentToChange instanceof PurgedComponent)
+ {
+ WeavingContext.getWeaver().fullClassScan();
+ //via an additional create component we can check whether a purged component
+ //was registered after the reload because the annotation has been removed
+ componentToChange = _delegate.createComponent(context, resource);
+
+ return componentToChange;
+ }
+ return oldComponent;
+ }
+
+ private UIComponent handleAnnotationChange(UIComponent oldComponent, FacesContext context, String componentType, String rendererType)
+ {
+ UIComponent componentToChange = _delegate.createComponent(context, componentType, rendererType);
+ if (componentToChange instanceof PurgedComponent)
+ {
+ WeavingContext.getWeaver().fullClassScan();
+ //via an additional create component we can check whether a purged component
+ //was registered after the reload because the annotation has been removed
+ componentToChange = _delegate.createComponent(context, componentType, rendererType);
+
+ return componentToChange;
+ }
+ return oldComponent;
+ }
+
+ private UIComponent handleAnnotationChange(UIComponent oldComponent, ValueExpression valueExpression, FacesContext facesContext, String s, String s1)
+ {
+ UIComponent componentToChange = _delegate.createComponent(valueExpression, facesContext, s, s1);
+ if (componentToChange instanceof PurgedComponent)
+ {
+ WeavingContext.getWeaver().fullClassScan();
+
+ //via an additional create component we can check whether a purged component
+ //was registered after the reload because the annotation has been removed
+
+ componentToChange = _delegate.createComponent(valueExpression, facesContext, s, s1);
+
+ return componentToChange;
+ }
+ return oldComponent;
+ } */
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/ELResolverProxy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/ELResolverProxy.java
new file mode 100644
index 0000000..398160d
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/ELResolverProxy.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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ELResolver;
+import javax.faces.context.FacesContext;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.Iterator;
+import java.util.logging.Logger;
+
+/**
+ * EL Resolver which is scripting enabled
+ *
+ * @author Werner Punz
+ *
+ * TODO not needed anymore because we drop the beans at request start directly which are tainted...
+ * they get reloaded because they are dropped entirely from the scope
+ *
+ * The compile and load happens on classloader level
+ * @deprecated
+ */
+public class ELResolverProxy extends ELResolver implements Decorated
+{
+
+ Logger log = Logger.getLogger(ELResolverProxy.class.getName());
+ ELResolver _delegate = null;
+
+ // static ThreadLocal<Boolean> _getValue = new ThreadLocal<Boolean>();
+
+ public Object getValue(ELContext elContext, final Object base, final Object property) throws NullPointerException, ELException {
+
+ Object retVal = _delegate.getValue(elContext, base, property);
+
+ /* Object newRetVal;
+ if (retVal != null && WeavingContext.getInstance().isDynamic(retVal.getClass())) {
+
+ newRetVal = WeavingContext.getInstance().getWeaver().reloadScriptingInstance(retVal, ScriptingConst.ARTIFACT_TYPE_MANAGEDBEAN);
+
+ if (newRetVal != retVal) {
+ setValue(elContext, base, property, newRetVal);
+ }
+
+ return newRetVal;
+
+ }*/
+
+ return retVal;
+ }
+
+
+
+
+ public Class<?> getType(ELContext elContext, Object o, Object o1) throws NullPointerException, ELException {
+ Class<?> retVal = _delegate.getType(elContext, o, o1);
+ if (retVal != null && WeavingContext.getInstance().isDynamic(retVal)) {
+ return WeavingContext.getInstance().reload(retVal);
+ }
+ return retVal;
+ }
+
+ public void setValue(ELContext elContext, Object base, Object property, Object value) throws NullPointerException, ELException {
+ //now to more complex relations...
+ //TODO add dependency
+ if (base != null && WeavingContext.getInstance().isDynamic(base.getClass()) && WeavingContext.getInstance().isDynamic(value.getClass())) {
+ WeavingContext.getInstance().addDependency(ScriptingConst.ENGINE_TYPE_JSF_ALL, base.getClass().getName(),
+ value.getClass().getName());
+ }
+ _delegate.setValue(elContext, base, property, value);
+ }
+
+ public boolean isReadOnly(ELContext elContext, Object o, Object o1) throws NullPointerException, ELException {
+ return _delegate.isReadOnly(elContext, o, o1);
+ }
+
+ public Iterator getFeatureDescriptors(ELContext elContext, Object o) {
+ return _delegate.getFeatureDescriptors(elContext, o);
+ }
+
+ public Class<?> getCommonPropertyType(ELContext elContext, Object o) {
+ return _delegate.getCommonPropertyType(elContext, o);
+ }
+
+
+ public ELResolverProxy() {
+ _delegate = FacesContext.getCurrentInstance().getELContext().getELResolver();
+ }
+
+ public ELResolverProxy(ELResolver delegate) {
+ _delegate = delegate;
+ }
+
+ public Object getDelegate() {
+ return _delegate;
+ }
+
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
+ {
+ // our "pseudo-constructor"
+ in.defaultReadObject();
+ log = Logger.getLogger(ELResolverProxy.class.getName());
+
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/FacesContextProxy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/FacesContextProxy.java
new file mode 100644
index 0000000..17fc2c8
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/FacesContextProxy.java
@@ -0,0 +1,221 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.el.ELContext;
+import javax.faces.application.Application;
+import javax.faces.application.FacesMessage;
+import javax.faces.application.ProjectStage;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.*;
+import javax.faces.event.PhaseId;
+import javax.faces.render.RenderKit;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A reloading, weaving faces context
+ * this is needed because groovy fails on
+ * the introspection of the standard java myfaces
+ * faces context due to pending references
+ * of the _impl into the portlet context
+ * not sure if this works in portlets
+ * though
+ *
+ * @author Werner Punz
+ */
+public class FacesContextProxy extends FacesContext implements Decorated
+{
+
+ public FacesContext _delegate = null;
+
+ private void weaveDelegate() {
+ //in case of a context destroyed the weaver might be accessed a last time
+ //but is already null due to having no weaver in the shutdown thread
+ if (_delegate != null && WeavingContext.getInstance().isScriptingEnabled())
+ _delegate = (FacesContext) WeavingContext.getInstance().reload(_delegate,
+ ScriptingConst.ARTIFACT_TYPE_FACESCONTEXT);
+ }
+
+ public ELContext getELContext() {
+ return _delegate.getELContext();
+ }
+
+ public Application getApplication() {
+ return _delegate.getApplication();
+ }
+
+ public Iterator<String> getClientIdsWithMessages() {
+ return _delegate.getClientIdsWithMessages();
+ }
+
+ public ExternalContext getExternalContext() {
+ return _delegate.getExternalContext();
+ }
+
+ public FacesMessage.Severity getMaximumSeverity() {
+ return _delegate.getMaximumSeverity();
+ }
+
+ public Iterator<FacesMessage> getMessages() {
+ return _delegate.getMessages();
+ }
+
+ public Iterator<FacesMessage> getMessages(String s) {
+ return _delegate.getMessages(s);
+ }
+
+ public RenderKit getRenderKit() {
+ return _delegate.getRenderKit();
+ }
+
+ public boolean getRenderResponse() {
+ return _delegate.getRenderResponse();
+ }
+
+ public boolean getResponseComplete() {
+ return _delegate.getResponseComplete();
+ }
+
+ public ResponseStream getResponseStream() {
+ return _delegate.getResponseStream();
+ }
+
+ public void setResponseStream(ResponseStream responseStream) {
+ _delegate.setResponseStream(responseStream);
+ }
+
+ public ResponseWriter getResponseWriter() {
+ return _delegate.getResponseWriter();
+ }
+
+ public void setResponseWriter(ResponseWriter responseWriter) {
+ _delegate.setResponseWriter(responseWriter);
+ }
+
+ public UIViewRoot getViewRoot() {
+ return _delegate.getViewRoot();
+ }
+
+ public void setViewRoot(UIViewRoot uiViewRoot) {
+ weaveDelegate();//perfect place no matter what the viewRoot is about once per request set
+ _delegate.setViewRoot(uiViewRoot);
+ }
+
+ public void addMessage(String s, FacesMessage facesMessage) {
+ _delegate.addMessage(s, facesMessage);
+ }
+
+ public void release() {
+ _delegate.release();
+ }
+
+ public void renderResponse() {
+ _delegate.renderResponse();
+ }
+
+ public void responseComplete() {
+ _delegate.responseComplete();
+ }
+
+ public FacesContextProxy(FacesContext delegate) {
+ _delegate = delegate;
+ weaveDelegate();
+ }
+
+ @Override
+ public Map<Object, Object> getAttributes() {
+ return _delegate.getAttributes();
+ }
+
+ @Override
+ public PhaseId getCurrentPhaseId() {
+ return _delegate.getCurrentPhaseId();
+ }
+
+ @Override
+ public ExceptionHandler getExceptionHandler() {
+ return _delegate.getExceptionHandler();
+ }
+
+ @Override
+ public List<FacesMessage> getMessageList() {
+ return _delegate.getMessageList();
+ }
+
+ @Override
+ public List<FacesMessage> getMessageList(String s) {
+ return _delegate.getMessageList(s);
+ }
+
+ @Override
+ public PartialViewContext getPartialViewContext() {
+ return _delegate.getPartialViewContext();
+ }
+
+ @Override
+ public boolean isValidationFailed() {
+ return _delegate.isValidationFailed();
+ }
+
+ @Override
+ public boolean isPostback() {
+ return _delegate.isPostback();
+ }
+
+ @Override
+ public boolean isProcessingEvents() {
+ return _delegate.isProcessingEvents();
+ }
+
+ @Override
+ public void setCurrentPhaseId(PhaseId phaseId) {
+ _delegate.setCurrentPhaseId(phaseId);
+ }
+
+ @Override
+ public void setExceptionHandler(ExceptionHandler exceptionHandler) {
+ _delegate.setExceptionHandler(exceptionHandler);
+ }
+
+ @Override
+ public void setProcessingEvents(boolean b) {
+ _delegate.setProcessingEvents(b);
+ }
+
+ @Override
+ public void validationFailed() {
+ _delegate.validationFailed();
+ }
+
+ @Override
+ public boolean isProjectStage(ProjectStage projectStage) {
+ return _delegate.isProjectStage(projectStage);
+ }
+
+ public Object getDelegate() {
+ return _delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/LifefcycleProxy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/LifefcycleProxy.java
new file mode 100644
index 0000000..fd08b89
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/LifefcycleProxy.java
@@ -0,0 +1,84 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.faces.FacesException;
+import javax.faces.context.FacesContext;
+import javax.faces.event.PhaseListener;
+import javax.faces.lifecycle.Lifecycle;
+
+/**
+ * Scripting enabled lifecycle
+ *
+ * @author Werner Punz
+ */
+public class LifefcycleProxy extends Lifecycle implements Decorated
+{
+
+ Lifecycle _delegate = null;
+
+ private void weaveDelegate() {
+ if (_delegate != null)
+ _delegate = (Lifecycle) WeavingContext.getInstance().reload(_delegate,
+ ScriptingConst.ARTIFACT_TYPE_LIFECYCLE);
+ }
+
+ public LifefcycleProxy(Lifecycle delegate) {
+ _delegate = delegate;
+ }
+
+ public void addPhaseListener(PhaseListener phaseListener) {
+ weaveDelegate();
+ /*we can put our object weaving code into the add here*/
+ if (WeavingContext.getInstance().isDynamic(phaseListener.getClass()))
+ phaseListener = (PhaseListener) WeavingContext.getInstance().createMethodReloadingProxyFromObject(phaseListener,
+ PhaseListener.class, ScriptingConst.ARTIFACT_TYPE_PHASELISTENER);
+
+ _delegate.addPhaseListener(phaseListener);
+ }
+
+ public void execute(FacesContext facesContext) throws FacesException {
+ weaveDelegate();
+ _delegate.execute(facesContext);
+ }
+
+ public PhaseListener[] getPhaseListeners() {
+ weaveDelegate();
+ return _delegate.getPhaseListeners();
+ }
+
+ public void removePhaseListener(PhaseListener phaseListener) {
+ weaveDelegate();
+ _delegate.removePhaseListener(phaseListener);
+ }
+
+ public void render(FacesContext facesContext) throws FacesException {
+ weaveDelegate();
+ _delegate.render(facesContext);
+ }
+
+ public Object getDelegate() {
+ return _delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/NavigationHandlerProxy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/NavigationHandlerProxy.java
new file mode 100644
index 0000000..6dcd020
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/NavigationHandlerProxy.java
@@ -0,0 +1,56 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations;
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.faces.application.NavigationHandler;
+import javax.faces.context.FacesContext;
+
+/**
+ * A reloading navigation handler
+ *
+ * @author Werner Punz
+ */
+public class NavigationHandlerProxy extends NavigationHandler implements Decorated
+{
+
+ NavigationHandler _delegate;
+
+ private void weaveDelegate() {
+ _delegate = (NavigationHandler) WeavingContext.getInstance().reload(_delegate,
+ ScriptingConst.ARTIFACT_TYPE_NAVIGATIONHANDLER);
+ }
+
+ public NavigationHandlerProxy(NavigationHandler delegate) {
+ super();
+ _delegate = delegate;
+ }
+
+ public void handleNavigation(FacesContext facesContext, String s, String s1) {
+ weaveDelegate();
+ _delegate.handleNavigation(facesContext, s, s1);
+ }
+
+ public Object getDelegate() {
+ return _delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/RenderkitProxy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/RenderkitProxy.java
new file mode 100644
index 0000000..5218ed4
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/RenderkitProxy.java
@@ -0,0 +1,177 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.faces.context.ResponseStream;
+import javax.faces.context.ResponseWriter;
+import javax.faces.render.ClientBehaviorRenderer;
+import javax.faces.render.RenderKit;
+import javax.faces.render.Renderer;
+import javax.faces.render.ResponseStateManager;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Iterator;
+
+/**
+ * Weaving renderkit which
+ * acts as a proxy factory for
+ * our internal reloading referers
+ *
+ * @author Werner Punz
+ */
+public class RenderkitProxy extends RenderKit implements Decorated
+{
+
+ RenderKit _delegate = null;
+
+ public RenderkitProxy(RenderKit delegate) {
+ _delegate = delegate;
+ }
+
+ public void addRenderer(String componentFamily, String rendererType, Renderer renderer) {
+ weaveDelegate();
+ //wo do it brute force here because we have sometimes casts and hence cannot rely on proxies
+ //renderers itself are flyweight patterns which means they are shared over objects
+
+ renderer = (Renderer) reloadInstance(renderer, ScriptingConst.ARTIFACT_TYPE_RENDERER);
+
+ _delegate.addRenderer(componentFamily, rendererType, renderer);
+ }
+
+ public Renderer getRenderer(String componentFamily, String rendererType) {
+ weaveDelegate();
+ Renderer rendr = _delegate.getRenderer(componentFamily, rendererType);
+ Renderer rendr2 = (Renderer) reloadInstance(rendr, ScriptingConst.ARTIFACT_TYPE_RENDERER);
+ if (rendr != rendr2) {
+ Renderer tempRenderer = _delegate.getRenderer(componentFamily, rendererType);
+ /**<></>if (tempRenderer instanceof PurgedRenderer) {
+ return handleAnnotationChange(componentFamily, rendererType);
+ } */
+
+ _delegate.addRenderer(componentFamily, rendererType, rendr2);
+ return rendr2;
+ }
+ return rendr;
+ }
+
+ private ClientBehaviorRenderer handleAnnotationChangeBehaviorRenderer(String s) {
+ ClientBehaviorRenderer rendr2;
+
+ rendr2 = _delegate.getClientBehaviorRenderer(s);
+ /*<>if (rendr2 instanceof PurgedClientBehaviorRenderer) {
+ throw new FacesException("Renderer not found");
+ } */
+ rendr2 = _delegate.getClientBehaviorRenderer(s);
+ return rendr2;
+ }
+
+ private Renderer handleAnnotationChange(String s, String s1) {
+ Renderer rendr2;
+
+ //WeavingContext.getWeaver().fullClassScan();
+ rendr2 = _delegate.getRenderer(s, s1);
+ /*<>if (rendr2 instanceof PurgedRenderer) {
+ throw new FacesException("Renderer not found");
+ }*/
+ rendr2 = _delegate.getRenderer(s, s1);
+ return rendr2;
+ }
+
+ public ResponseStateManager getResponseStateManager() {
+ weaveDelegate();
+ return _delegate.getResponseStateManager();
+ }
+
+ public ResponseWriter createResponseWriter(Writer writer, String s, String s1) {
+ weaveDelegate();
+ return (ResponseWriter) reloadInstance(_delegate.createResponseWriter(writer, s, s1), ScriptingConst.ARTIFACT_TYPE_RESPONSEWRITER);
+ }
+
+ public ResponseStream createResponseStream(OutputStream outputStream) {
+ weaveDelegate();
+ return (ResponseStream) reloadInstance(_delegate.createResponseStream(outputStream), ScriptingConst.ARTIFACT_TYPE_RESPONSESTREAM);
+ }
+
+ @Override
+ public void addClientBehaviorRenderer(String s, ClientBehaviorRenderer renderer) {
+
+ weaveDelegate();
+ renderer = (ClientBehaviorRenderer) reloadInstance(renderer, ScriptingConst.ARTIFACT_TYPE_CLIENTBEHAVIORRENDERER);
+ _delegate.addClientBehaviorRenderer(s, renderer);
+ }
+
+ @Override
+ public ClientBehaviorRenderer getClientBehaviorRenderer(String s) {
+ weaveDelegate();
+ ClientBehaviorRenderer rendr = _delegate.getClientBehaviorRenderer(s);
+ ClientBehaviorRenderer rendr2 = (ClientBehaviorRenderer) reloadInstance(rendr, ScriptingConst.ARTIFACT_TYPE_CLIENTBEHAVIORRENDERER);
+ if (rendr != rendr2) {
+
+ rendr2 = _delegate.getClientBehaviorRenderer(s);
+ /*<>if (rendr2 instanceof PurgedClientBehaviorRenderer) {
+ return handleAnnotationChangeBehaviorRenderer(s);
+ }*/
+ return rendr2;
+ }
+ return rendr;
+ }
+
+ @Override
+ public Iterator<String> getClientBehaviorRendererTypes() {
+ weaveDelegate();
+ return _delegate.getClientBehaviorRendererTypes();
+ }
+
+ @Override
+ public Iterator<String> getComponentFamilies() {
+ weaveDelegate();
+ return _delegate.getComponentFamilies();
+ }
+
+ @Override
+ public Iterator<String> getRendererTypes(String s) {
+ weaveDelegate();
+ return _delegate.getRendererTypes(s);
+ }
+
+ public Object getDelegate() {
+ return _delegate;
+ }
+
+ private void weaveDelegate() {
+ _delegate = (RenderKit) WeavingContext.getInstance().reload(_delegate, ScriptingConst.ARTIFACT_TYPE_RENDERKIT);
+ }
+
+ private Object reloadInstance(Object instance, int artefactType) {
+ if (instance == null) {
+ return null;
+ }
+ if (WeavingContext.getInstance().isDynamic(instance.getClass()) ) {
+ instance = WeavingContext.getInstance().reload(instance, artefactType);
+ //now the add should be done properly if possible
+ }
+ return instance;
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/ResourceHandlerProxy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/ResourceHandlerProxy.java
new file mode 100644
index 0000000..6be2982
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/ResourceHandlerProxy.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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.faces.application.Resource;
+import javax.faces.application.ResourceHandler;
+import javax.faces.context.FacesContext;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ *
+ * Problem the resource request is issued on servlet level before
+ * our compile triggers can trigger from the phase listener
+ * this is evil
+ *
+ * We probably have to reissue the compile from the resource handler
+ * directly upfront :-( or mark the resource handler as something like double tainted!
+ *
+ * This problem will resolve itself with async compile
+ *
+ */
+
+public class ResourceHandlerProxy extends ResourceHandler {
+ private ResourceHandler _delegate;
+
+ public ResourceHandlerProxy(ResourceHandler delegate) {
+ _delegate = delegate;
+ }
+
+ public Resource createResource(String resourceName) {
+ weaveDelegate();
+ return _delegate.createResource(resourceName);
+ }
+
+ public Resource createResource(String resourceName, String libraryName) {
+ weaveDelegate();
+ return _delegate.createResource(resourceName, libraryName);
+ }
+
+ public Resource createResource(String resourceName, String libraryName, String contentType) {
+ weaveDelegate();
+ return _delegate.createResource(resourceName, libraryName, contentType);
+ }
+
+ public String getRendererTypeForResourceName(String resourceName) {
+ weaveDelegate();
+ return _delegate.getRendererTypeForResourceName(resourceName);
+ }
+
+ public void handleResourceRequest(FacesContext context) throws java.io.IOException {
+ weaveDelegate();
+ _delegate.handleResourceRequest(context);
+ }
+
+ public boolean isResourceRequest(FacesContext context) {
+ weaveDelegate();
+ return _delegate.isResourceRequest(context);
+ }
+
+ public boolean libraryExists(String libraryName) {
+ weaveDelegate();
+ return _delegate.libraryExists(libraryName);
+ }
+
+ private final void weaveDelegate() {
+ _delegate = (ResourceHandler) WeavingContext.getInstance().reload(_delegate,
+ ScriptingConst.ARTIFACT_TYPE_RESOURCEHANDLER);
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/SystemEventListenerProxy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/SystemEventListenerProxy.java
new file mode 100644
index 0000000..66b5e33
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/SystemEventListenerProxy.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.faces.event.SystemEvent;
+import javax.faces.event.SystemEventListener;
+
+/**
+ * a method level reloading proxy class
+ * we do not use auto proxies here because
+ * this class needs special treatment
+ * over our decorated interface
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class SystemEventListenerProxy implements Decorated, SystemEventListener {
+
+ SystemEventListener _delegate;
+
+ public SystemEventListenerProxy(SystemEventListener delegate) {
+ _delegate = delegate;
+ }
+
+ public boolean isListenerForSource(Object source) {
+ weaveDelegate();
+ return _delegate.isListenerForSource(source);
+ }
+
+ public void processEvent(SystemEvent event) {
+ weaveDelegate();
+ _delegate.processEvent(event);
+ }
+
+ @Override
+ public Object getDelegate() {
+ return _delegate;
+ }
+
+ private void weaveDelegate() {
+ //TODO (1.1) add a speed optimization here by pushing something in the request map
+ if (_delegate != null) {
+ _delegate = (SystemEventListener) WeavingContext.getInstance().reload(_delegate,
+ ScriptingConst.ARTIFACT_TYPE_SYSTEMEVENTLISTENER);
+ }
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/VariableResolverProxy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/VariableResolverProxy.java
new file mode 100644
index 0000000..f7c6905
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/VariableResolverProxy.java
@@ -0,0 +1,55 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.faces.context.FacesContext;
+import javax.faces.el.EvaluationException;
+import javax.faces.el.VariableResolver;
+
+/**
+ * objects loaded must
+ * be checked if a reloading is needed
+ *
+ * @author Werner Punz
+ */
+@SuppressWarnings("deprecation") //we must suppress it here
+public class VariableResolverProxy extends VariableResolver implements Decorated
+{
+ VariableResolver _delegate;
+
+ public VariableResolverProxy(VariableResolver delegate) {
+ _delegate = delegate;
+ }
+
+ public Object resolveVariable(FacesContext facesContext, String s) throws EvaluationException {
+ Object variable = _delegate.resolveVariable(facesContext, s);
+ if (variable != null && WeavingContext.getInstance().isDynamic(variable.getClass()))
+ variable = WeavingContext.getInstance().reload(variable, ScriptingConst.ARTIFACT_TYPE_MANAGEDBEAN);
+ return variable;
+ }
+
+ public Object getDelegate() {
+ return _delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/ViewHandlerProxy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/ViewHandlerProxy.java
new file mode 100644
index 0000000..ae5826c
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/dynamicDecorators/implementations/ViewHandlerProxy.java
@@ -0,0 +1,134 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.dynamicdecorators.implementations;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.faces.FacesException;
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+import javax.faces.view.ViewDeclarationLanguage;
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Scripting enabled View Handler
+ *
+ * @author Werner Punz
+ */
+public class ViewHandlerProxy extends ViewHandler implements Decorated
+{
+
+ ViewHandler _delegate = null;
+
+ private void weaveDelegate() {
+ if (_delegate != null) {
+ _delegate = (ViewHandler) WeavingContext.getInstance().reload(_delegate,
+ ScriptingConst.ARTIFACT_TYPE_VIEWHANDLER);
+ }
+ }
+
+ public ViewHandlerProxy(ViewHandler delegate) {
+ _delegate = delegate;
+ }
+
+ public String calculateCharacterEncoding(FacesContext facesContext) {
+ weaveDelegate();
+ return _delegate.calculateCharacterEncoding(facesContext);
+ }
+
+ public Locale calculateLocale(FacesContext facesContext) {
+ weaveDelegate();
+ return _delegate.calculateLocale(facesContext);
+ }
+
+ public String calculateRenderKitId(FacesContext facesContext) {
+ weaveDelegate();
+ return _delegate.calculateRenderKitId(facesContext);
+ }
+
+ public UIViewRoot createView(FacesContext facesContext, String s) {
+ weaveDelegate();
+ return _delegate.createView(facesContext, s);
+ }
+
+ public String getActionURL(FacesContext facesContext, String s) {
+ weaveDelegate();
+ return _delegate.getActionURL(facesContext, s);
+ }
+
+ public String getResourceURL(FacesContext facesContext, String s) {
+ weaveDelegate();
+ return _delegate.getResourceURL(facesContext, s);
+ }
+
+ public void initView(FacesContext facesContext) throws FacesException {
+ weaveDelegate();
+ _delegate.initView(facesContext);
+ }
+
+ public void renderView(FacesContext facesContext, UIViewRoot uiViewRoot) throws IOException, FacesException {
+ weaveDelegate();
+ _delegate.renderView(facesContext, uiViewRoot);
+ }
+
+ public UIViewRoot restoreView(FacesContext facesContext, String s) {
+ weaveDelegate();
+ return _delegate.restoreView(facesContext, s);
+ }
+
+ public void writeState(FacesContext facesContext) throws IOException {
+ weaveDelegate();
+ _delegate.writeState(facesContext);
+ }
+
+
+ @Override
+ public String deriveViewId(FacesContext facesContext, String s) {
+ weaveDelegate();
+ return _delegate.deriveViewId(facesContext, s);
+ }
+
+ @Override
+ public String getBookmarkableURL(FacesContext facesContext, String s, Map<String, List<String>> stringListMap, boolean b) {
+ return super.getBookmarkableURL(facesContext, s, stringListMap, b);
+ }
+
+ @Override
+ public ViewDeclarationLanguage getViewDeclarationLanguage(FacesContext facesContext, String s) {
+ weaveDelegate();
+ return _delegate.getViewDeclarationLanguage(facesContext, s);
+ }
+
+ @Override
+ public String getRedirectURL(FacesContext facesContext, String s, Map<String, List<String>> stringListMap, boolean b) {
+ weaveDelegate();
+ return _delegate.getRedirectURL(facesContext, s, stringListMap, b);
+ }
+
+ public Object getDelegate() {
+ return _delegate;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/MyFacesReroutingResourceResolver.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/MyFacesReroutingResourceResolver.java
new file mode 100644
index 0000000..cc9428d
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/MyFacesReroutingResourceResolver.java
@@ -0,0 +1,66 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.facelet;
+
+
+import org.apache.myfaces.view.facelets.impl.DefaultResourceResolver;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * decorated Facelet resource resolver to reroute
+ * the resource requests to our source path if possible
+ */
+public class MyFacesReroutingResourceResolver extends DefaultResourceResolver
+{
+
+ DefaultResourceResolver _delegate = new DefaultResourceResolver();
+ volatile boolean _initiated = false;
+ List<String> _resourceDirs = null;
+
+ Logger log = Logger.getLogger(this.getClass().getName());
+
+ @Override
+ public URL resolveUrl(String path) {
+
+ if (!_initiated) {
+ _resourceDirs = WeavingContext.getInstance().getConfiguration().getResourceDirs();
+ _initiated = true;
+ }
+
+ if (_resourceDirs != null && !_resourceDirs.isEmpty()) {
+ for (String resourceDir : _resourceDirs) {
+ File resource = new File(resourceDir + path);
+ if (resource.exists()) try {
+ return resource.toURI().toURL();
+ } catch (MalformedURLException e) {
+ log.log(Level.SEVERE, "",e);
+ }
+ }
+ }
+
+ return _delegate.resolveUrl(path);
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/ReloadingBehaviorTagHandlerDelegate.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/ReloadingBehaviorTagHandlerDelegate.java
new file mode 100644
index 0000000..19c5c05
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/ReloadingBehaviorTagHandlerDelegate.java
@@ -0,0 +1,72 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.facelet;
+
+
+import org.apache.myfaces.view.facelets.tag.jsf.BehaviorTagHandlerDelegate;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.faces.component.UIComponent;
+import javax.faces.view.facelets.BehaviorHandler;
+import javax.faces.view.facelets.FaceletContext;
+import javax.faces.view.facelets.MetaRuleset;
+import javax.faces.view.facelets.TagHandlerDelegate;
+import java.io.IOException;
+
+/**
+ * Behavior Tag Handler which introduces reloading behavior
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ReloadingBehaviorTagHandlerDelegate extends TagHandlerDelegate {
+
+ BehaviorHandler _owner;
+ TagHandlerDelegate _delegate;
+
+ public ReloadingBehaviorTagHandlerDelegate(BehaviorHandler owner) {
+ applyOwner(owner);
+ }
+
+ private void applyOwner(BehaviorHandler owner) {
+ _owner = owner;
+ _delegate = new BehaviorTagHandlerDelegate(_owner);
+ }
+
+ @Override
+ public void apply(FaceletContext ctx, UIComponent comp) throws IOException {
+ if (WeavingContext.getInstance().isDynamic(_owner.getClass())) {
+ BehaviorHandler newOwner = (BehaviorHandler) WeavingContext.getInstance().reload(_owner,
+ ScriptingConst.ARTIFACT_TYPE_BEHAVIOR_HANDLER);
+ if (!newOwner.getClass().equals(_owner.getClass())) {
+ applyOwner(newOwner);
+ }
+ }
+ _owner.apply(ctx, comp);
+ }
+
+ @Override
+ public MetaRuleset createMetaRuleset(Class type) {
+ return _delegate.createMetaRuleset(type);
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/ReloadingComponentTagHandlerDelegate.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/ReloadingComponentTagHandlerDelegate.java
new file mode 100644
index 0000000..8abe891
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/ReloadingComponentTagHandlerDelegate.java
@@ -0,0 +1,110 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.facelet;
+
+import org.apache.myfaces.view.facelets.tag.jsf.ActionSourceRule;
+import org.apache.myfaces.view.facelets.tag.jsf.ComponentTagHandlerDelegate;
+import org.apache.myfaces.view.facelets.tag.jsf.EditableValueHolderRule;
+import org.apache.myfaces.view.facelets.tag.jsf.ValueHolderRule;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.jsf.facelet.support.ComponentRule;
+import org.apache.myfaces.extensions.scripting.jsf.facelet.support.SwitchingMetarulesetImpl;
+
+import javax.faces.component.ActionSource;
+import javax.faces.component.EditableValueHolder;
+import javax.faces.component.UIComponent;
+import javax.faces.component.ValueHolder;
+import javax.faces.view.facelets.ComponentHandler;
+import javax.faces.view.facelets.FaceletContext;
+import javax.faces.view.facelets.MetaRuleset;
+import javax.faces.view.facelets.TagHandlerDelegate;
+import java.io.IOException;
+
+/**
+ * we provide our own component tag handler factory impl
+ * so that we can deal with refreshing of components
+ * on Facelets level without running into
+ * nasty type exceptions
+ */
+public class ReloadingComponentTagHandlerDelegate extends TagHandlerDelegate {
+
+ ComponentHandler _owner;
+ TagHandlerDelegate _delegate;
+
+ public ReloadingComponentTagHandlerDelegate(ComponentHandler owner) {
+ applyOwner(owner);
+ }
+
+ private void applyOwner(ComponentHandler owner) {
+ _owner = owner;
+ _delegate = new ComponentTagHandlerDelegate(_owner);
+ }
+
+ @Override
+ public void apply(FaceletContext ctx, UIComponent comp) throws IOException {
+ if (WeavingContext.getInstance().isDynamic(_owner.getClass())) {
+ ComponentHandler newOwner = (ComponentHandler) WeavingContext.getInstance().reload(_owner,
+ ScriptingConst.ARTIFACT_TYPE_COMPONENT_HANDLER);
+ if (!newOwner.getClass().equals(_owner.getClass())) {
+ applyOwner(newOwner);
+ }
+ }
+ _delegate.apply(ctx, comp);
+ }
+
+ public MetaRuleset createMetaRuleset(Class type) {
+ //We have to create a different meta rule set for dynamic classes
+ //which have weaver instantiation criteria, the original meta rule set
+ //first applies the attributes and then calls BeanPropertyTagRule
+ //that one however caches the current method and does not take into consideration
+ //that classes can be changed on the fly
+
+ // if (WeavingContext.isDynamic(type)) {
+ MetaRuleset m = new SwitchingMetarulesetImpl(_owner.getTag(), type);
+ // ignore standard component attributes
+ m.ignore("binding").ignore("id");
+
+ // add auto wiring for attributes
+ m.addRule(ComponentRule.Instance);
+
+ // if it's an ActionSource
+ if (ActionSource.class.isAssignableFrom(type)) {
+ m.addRule(ActionSourceRule.INSTANCE);
+ }
+
+ // if it's a ValueHolder
+ if (ValueHolder.class.isAssignableFrom(type)) {
+ m.addRule(ValueHolderRule.INSTANCE);
+
+ // if it's an EditableValueHolder
+ if (EditableValueHolder.class.isAssignableFrom(type)) {
+ m.ignore("submittedValue");
+ m.ignore("valid");
+ m.addRule(EditableValueHolderRule.INSTANCE);
+ }
+ }
+
+ return m;
+ //}
+
+ //return _delegate.createMetaRuleset(type);
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/ReloadingConverterTagHandlerDelegate.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/ReloadingConverterTagHandlerDelegate.java
new file mode 100644
index 0000000..ad988ba
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/ReloadingConverterTagHandlerDelegate.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.facelet;
+
+
+import org.apache.myfaces.view.facelets.tag.jsf.ConverterTagHandlerDelegate;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.faces.component.UIComponent;
+import javax.faces.view.facelets.ConverterHandler;
+import javax.faces.view.facelets.FaceletContext;
+import javax.faces.view.facelets.MetaRuleset;
+import javax.faces.view.facelets.TagHandlerDelegate;
+import java.io.IOException;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ReloadingConverterTagHandlerDelegate extends TagHandlerDelegate {
+
+ ConverterHandler _owner;
+ TagHandlerDelegate _delegate;
+
+ public ReloadingConverterTagHandlerDelegate(ConverterHandler owner) {
+ applyOwner(owner);
+ }
+
+ private void applyOwner(ConverterHandler owner) {
+ _owner = owner;
+ _delegate = new ConverterTagHandlerDelegate(_owner);
+ }
+
+ @Override
+ public void apply(FaceletContext ctx, UIComponent comp) throws IOException {
+ if (WeavingContext.getInstance().isDynamic(_owner.getClass())) {
+ ConverterHandler newOwner = (ConverterHandler) WeavingContext.getInstance().reload(_owner,
+ ScriptingConst.ARTIFACT_TYPE_CONVERTER_HANDLER);
+ if(!newOwner.getClass().equals(_owner.getClass())) {
+ applyOwner(newOwner);
+ }
+ }
+ _delegate.apply(ctx, comp);
+ }
+
+ @Override
+ public MetaRuleset createMetaRuleset(Class type) {
+ return _delegate.createMetaRuleset(type);
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/ReloadingValidatorTagHandlerDelegate.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/ReloadingValidatorTagHandlerDelegate.java
new file mode 100644
index 0000000..142f7fd
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/ReloadingValidatorTagHandlerDelegate.java
@@ -0,0 +1,69 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.facelet;
+
+
+import org.apache.myfaces.view.facelets.tag.jsf.ValidatorTagHandlerDelegate;
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.faces.component.UIComponent;
+import javax.faces.view.facelets.FaceletContext;
+import javax.faces.view.facelets.MetaRuleset;
+import javax.faces.view.facelets.TagHandlerDelegate;
+import javax.faces.view.facelets.ValidatorHandler;
+import java.io.IOException;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ReloadingValidatorTagHandlerDelegate extends TagHandlerDelegate {
+
+ ValidatorHandler _owner;
+ TagHandlerDelegate _delegate;
+
+ public ReloadingValidatorTagHandlerDelegate(ValidatorHandler owner) {
+ applyOwner(owner);
+ }
+
+ private void applyOwner(ValidatorHandler owner) {
+ _owner = owner;
+ _delegate = new ValidatorTagHandlerDelegate(_owner);
+ }
+
+ @Override
+ public void apply(FaceletContext ctx, UIComponent comp) throws IOException {
+ if (WeavingContext.getInstance().isDynamic(_owner.getClass())) {
+ ValidatorHandler newOwner = (ValidatorHandler) WeavingContext.getInstance().reload(_owner,
+ ScriptingConst.ARTIFACT_TYPE_VALIDATOR_HANDLER);
+ if (!newOwner.getClass().equals(_owner.getClass())) {
+ applyOwner(newOwner);
+ }
+ }
+ _owner.apply(ctx, comp);
+ }
+
+ @Override
+ public MetaRuleset createMetaRuleset(Class type) {
+ return _delegate.createMetaRuleset(type);
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/TagHandlerDelegateFactoryImpl.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/TagHandlerDelegateFactoryImpl.java
new file mode 100644
index 0000000..3f554eb
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/TagHandlerDelegateFactoryImpl.java
@@ -0,0 +1,72 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.facelet;
+
+
+import org.apache.myfaces.view.facelets.tag.jsf.BehaviorTagHandlerDelegate;
+import org.apache.myfaces.view.facelets.tag.jsf.ConverterTagHandlerDelegate;
+import org.apache.myfaces.view.facelets.tag.jsf.ValidatorTagHandlerDelegate;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.faces.view.facelets.*;
+
+/**
+ * Tag handler delegate factory which injects reloading
+ * proxies for our facelet artifacts
+ */
+public class TagHandlerDelegateFactoryImpl extends TagHandlerDelegateFactory {
+
+ @Override
+ public TagHandlerDelegate createBehaviorHandlerDelegate(
+ BehaviorHandler owner) {
+ if (WeavingContext.getInstance().isDynamic(owner.getClass())) {
+ return new ReloadingBehaviorTagHandlerDelegate(owner);
+ } else {
+ return new BehaviorTagHandlerDelegate(owner);
+ }
+ }
+
+ @Override
+ public TagHandlerDelegate createComponentHandlerDelegate(
+ ComponentHandler owner) {
+ return new ReloadingComponentTagHandlerDelegate(owner);
+ }
+
+ @Override
+ public TagHandlerDelegate createConverterHandlerDelegate(
+ ConverterHandler owner) {
+ if (WeavingContext.getInstance().isDynamic(owner.getClass())) {
+ return new ReloadingConverterTagHandlerDelegate(owner);
+ } else {
+ return new ConverterTagHandlerDelegate(owner);
+ }
+ }
+
+ @Override
+ public TagHandlerDelegate createValidatorHandlerDelegate(
+ ValidatorHandler owner) {
+ if (WeavingContext.getInstance().isDynamic(owner.getClass())) {
+ return new ReloadingValidatorTagHandlerDelegate(owner);
+ } else {
+ return new ValidatorTagHandlerDelegate(owner);
+ }
+ }
+}
+
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/support/ComponentRule.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/support/ComponentRule.java
new file mode 100644
index 0000000..b3771a3
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/support/ComponentRule.java
@@ -0,0 +1,102 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.facelet.support;
+
+import javax.faces.component.UIComponent;
+import javax.faces.view.facelets.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * we have to re implement the component rule class here
+ * because it is declared private in the original
+ * implementation
+ */
+public final class ComponentRule extends MetaRule {
+
+ final class LiteralAttributeMetadata extends Metadata {
+ private final String _name;
+ private final String _value;
+
+ public LiteralAttributeMetadata(String name, String value) {
+ _name = name;
+ _value = value;
+ }
+
+ public void applyMetadata(FaceletContext ctx, Object instance) {
+ ((UIComponent) instance).getAttributes().put(_name, _value);
+ }
+ }
+
+ final static class ValueExpressionMetadata extends Metadata {
+ private final String _name;
+
+ private final TagAttribute _attr;
+
+ private final Class<?> _type;
+
+ public ValueExpressionMetadata(String name, Class<?> type, TagAttribute attr) {
+ _name = name;
+ _attr = attr;
+ _type = type;
+ }
+
+ public void applyMetadata(FaceletContext ctx, Object instance) {
+ ((UIComponent) instance).setValueExpression(_name, _attr.getValueExpression(ctx, _type));
+ }
+ }
+
+ //private final static Logger log = Logger.getLogger("facelets.tag.component");
+ private final static Logger log = Logger.getLogger(ComponentRule.class.getName());
+
+ public final static ComponentRule Instance = new ComponentRule();
+
+ public ComponentRule() {
+ super();
+ }
+
+ public Metadata applyRule(String name, TagAttribute attribute, MetadataTarget meta) {
+ if (meta.isTargetInstanceOf(UIComponent.class)) {
+ // if component and dynamic, then must set expression
+ if (!attribute.isLiteral()) {
+ Class<?> type = meta.getPropertyType(name);
+ if (type == null) {
+ type = Object.class;
+ }
+
+ return new ValueExpressionMetadata(name, type, attribute);
+ } else if (meta.getWriteMethod(name) == null) {
+
+ // this was an attribute literal, but not property
+ warnAttr(attribute, meta.getTargetClass(), name);
+
+ return new LiteralAttributeMetadata(name, attribute.getValue());
+ }
+ }
+ return null;
+ }
+
+ private static void warnAttr(TagAttribute attr, Class<?> type, String n) {
+ if (log.isLoggable(Level.FINER)) {
+ log.finer(attr + " Property '" + n + "' is not on type: " + type.getName());
+ }
+ }
+
+}
+
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/support/InvokeDynamicBeanPropertyTagRule.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/support/InvokeDynamicBeanPropertyTagRule.java
new file mode 100644
index 0000000..4fe7065
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/support/InvokeDynamicBeanPropertyTagRule.java
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.facelet.support;
+
+
+import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
+
+import javax.faces.view.facelets.FaceletContext;
+import javax.faces.view.facelets.Metadata;
+import javax.faces.view.facelets.MetadataTarget;
+import javax.faces.view.facelets.TagAttribute;
+import java.lang.reflect.Method;
+
+/**
+ * We have to introduce a BeanPropertyTagRule
+ * which calls the setter of a given component
+ * on a weaker base than the original facelets component
+ * property tag rule does.
+ * By not enforcing a strict per object/class policy on calling
+ * the setter we are able to reload the classes on the fly
+ * <p/>
+ * the original approach was to cache the classes, and then
+ * call the invoke method on the existing class
+ * if we now exchange the classes we have a problem...
+ * By making the invocation of the method independend from the underlying
+ * class (sort of calling an invokedynamic) we can bypass this problem
+ * on facelets level.
+ */
+public class InvokeDynamicBeanPropertyTagRule {
+ public final static InvokeDynamicBeanPropertyTagRule Instance = new InvokeDynamicBeanPropertyTagRule();
+
+ public Metadata applyRule(String name, TagAttribute attribute, MetadataTarget meta) {
+ Method m = meta.getWriteMethod(name);
+
+ // if the property is writable
+ if (m != null) {
+ if (attribute.isLiteral()) {
+ return new LiteralPropertyMetadata(m, attribute);
+ } else {
+ return new DynamicPropertyMetadata(m, attribute);
+ }
+ }
+
+ return null;
+ }
+
+ final static class LiteralPropertyMetadata extends Metadata {
+
+ private final Method method;
+
+ private final TagAttribute attribute;
+
+ private Object[] value;
+
+ public LiteralPropertyMetadata(Method method, TagAttribute attribute) {
+ this.method = method;
+ this.attribute = attribute;
+ }
+
+ public void applyMetadata(FaceletContext ctx, Object instance) {
+ if (value == null) {
+ String str = this.attribute.getValue();
+ value = new Object[]{ctx.getExpressionFactory().coerceToType(str, method.getParameterTypes()[0])};
+ }
+ //What we do here is simply to call an invoke dynamic on the method with the same name
+ //but on the new instance of, that way we can bypass class problems
+ //because the method reference has stored the old class in our case
+ ReflectUtil.executeMethod(instance, method.getName(), this.value);
+ }
+
+ }
+
+ final static class DynamicPropertyMetadata extends Metadata {
+
+ private final Method method;
+
+ private final TagAttribute attribute;
+
+ private final Class<?> type;
+
+ public DynamicPropertyMetadata(Method method, TagAttribute attribute) {
+ this.method = method;
+ this.type = method.getParameterTypes()[0];
+ this.attribute = attribute;
+ }
+
+ public void applyMetadata(FaceletContext ctx, Object instance) {
+ ReflectUtil.executeMethod(instance, method.getName(), new Object[]{attribute.getObject(ctx, type)});
+ }
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/support/SwitchingBeanPropertyTagRule.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/support/SwitchingBeanPropertyTagRule.java
new file mode 100644
index 0000000..ee43955
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/support/SwitchingBeanPropertyTagRule.java
@@ -0,0 +1,51 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.facelet.support;
+
+import org.apache.myfaces.view.facelets.tag.BeanPropertyTagRule;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.faces.view.facelets.MetaRule;
+import javax.faces.view.facelets.Metadata;
+import javax.faces.view.facelets.MetadataTarget;
+import javax.faces.view.facelets.TagAttribute;
+
+/**
+ * Bean property tag rule
+ * which switches between the fast static
+ * version and the slower invoke dynamic
+ * version depending on the class type of
+ * the incoming instance
+ */
+public class SwitchingBeanPropertyTagRule extends MetaRule {
+
+ InvokeDynamicBeanPropertyTagRule _invokeDynamic = InvokeDynamicBeanPropertyTagRule.Instance;
+ BeanPropertyTagRule _invokeStatic = BeanPropertyTagRule.INSTANCE;
+
+ public static volatile SwitchingBeanPropertyTagRule Instance = new SwitchingBeanPropertyTagRule();
+
+ @Override
+ public Metadata applyRule(String name, TagAttribute attribute, MetadataTarget meta) {
+ if (WeavingContext.getInstance().isDynamic(meta.getTargetClass())) {
+ return _invokeDynamic.applyRule(name, attribute, meta);
+ } else {
+ return _invokeStatic.applyRule(name, attribute, meta);
+ }
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/support/SwitchingMetarulesetImpl.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/support/SwitchingMetarulesetImpl.java
new file mode 100644
index 0000000..54dd343
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/facelet/support/SwitchingMetarulesetImpl.java
@@ -0,0 +1,179 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.facelet.support;
+
+import org.apache.myfaces.view.facelets.tag.MetaRulesetImpl;
+import org.apache.myfaces.view.facelets.tag.MetadataImpl;
+import org.apache.myfaces.view.facelets.tag.MetadataTargetImpl;
+import org.apache.myfaces.view.facelets.util.ParameterCheck;
+
+import javax.faces.view.facelets.*;
+import java.beans.IntrospectionException;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * we have to to do a full reimplementation of the rule set
+ * because otherwise we could not plant the switching bean reloading
+ * rule due to private props in the original code
+ */
+public class SwitchingMetarulesetImpl extends MetaRuleset {
+ private final static Metadata NONE = new NullMetadata();
+
+ //private final static Logger log = Logger.getLogger("facelets.tag.meta");
+ private final static Logger log = Logger.getLogger(MetaRulesetImpl.class.getName());
+
+ private final static WeakHashMap<String, MetadataTarget> _metadata = new WeakHashMap<String, MetadataTarget>();
+
+ private final Map<String, TagAttribute> _attributes;
+
+ private final List<Metadata> _mappers;
+
+ private final List<MetaRule> _rules;
+
+ private final Tag _tag;
+
+ private final Class<?> _type;
+
+ public SwitchingMetarulesetImpl(Tag tag, Class<?> type) {
+ _tag = tag;
+ _type = type;
+ _attributes = new HashMap<String, TagAttribute>();
+ _mappers = new ArrayList<Metadata>();
+ _rules = new ArrayList<MetaRule>();
+
+ // setup attributes
+ for (TagAttribute attribute : _tag.getAttributes().getAll()) {
+ _attributes.put(attribute.getLocalName(), attribute);
+ }
+
+ // add default rules
+ _rules.add(SwitchingBeanPropertyTagRule.Instance);
+ }
+
+ public MetaRuleset add(Metadata mapper) {
+ ParameterCheck.notNull("mapper", mapper);
+
+ if (!_mappers.contains(mapper)) {
+ _mappers.add(mapper);
+ }
+
+ return this;
+ }
+
+ public MetaRuleset addRule(MetaRule rule) {
+ ParameterCheck.notNull("rule", rule);
+
+ _rules.add(rule);
+
+ return this;
+ }
+
+ public MetaRuleset alias(String attribute, String property) {
+ ParameterCheck.notNull("attribute", attribute);
+ ParameterCheck.notNull("property", property);
+
+ TagAttribute attr = (TagAttribute) _attributes.remove(attribute);
+ if (attr != null) {
+ _attributes.put(property, attr);
+ }
+
+ return this;
+ }
+
+ public Metadata finish() {
+ assert !_rules.isEmpty();
+
+ if (!_attributes.isEmpty()) {
+ MetadataTarget target = this._getMetadataTarget();
+ int ruleEnd = _rules.size() - 1;
+
+ // now iterate over attributes
+ for (Map.Entry<String, TagAttribute> entry : _attributes.entrySet()) {
+ Metadata data = null;
+
+ int i = ruleEnd;
+
+ // First loop is always safe
+ do {
+ MetaRule rule = _rules.get(i);
+ data = rule.applyRule(entry.getKey(), entry.getValue(), target);
+ i--;
+ } while (data == null && i >= 0);
+
+ if (data == null) {
+ if (log.isLoggable(Level.SEVERE)) {
+ log.severe(entry.getValue() + " Unhandled by MetaTagHandler for type " + _type.getName());
+ }
+ } else {
+ _mappers.add(data);
+ }
+ }
+ }
+
+ if (_mappers.isEmpty()) {
+ return NONE;
+ } else {
+ return new MetadataImpl(_mappers.toArray(new Metadata[_mappers.size()]));
+ }
+ }
+
+ public MetaRuleset ignore(String attribute) {
+ ParameterCheck.notNull("attribute", attribute);
+
+ _attributes.remove(attribute);
+
+ return this;
+ }
+
+ public MetaRuleset ignoreAll() {
+ _attributes.clear();
+
+ return this;
+ }
+
+ private final MetadataTarget _getMetadataTarget() {
+ String key = _type.getName();
+
+ MetadataTarget meta = _metadata.get(key);
+ if (meta == null) {
+ try {
+ meta = new MetadataTargetImpl(_type);
+ }
+ catch (IntrospectionException e) {
+ throw new TagException(_tag, "Error Creating TargetMetadata", e);
+ }
+
+ _metadata.put(key, meta);
+ }
+
+ return meta;
+ }
+
+ private static class NullMetadata extends Metadata {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void applyMetadata(FaceletContext ctx, Object instance) {
+ // do nothing
+ }
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/reloading/BehaviorHandlerReloadingStrategy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/reloading/BehaviorHandlerReloadingStrategy.java
new file mode 100644
index 0000000..1916f0a
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/reloading/BehaviorHandlerReloadingStrategy.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.reloading;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.Cast;
+import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
+import org.apache.myfaces.extensions.scripting.core.reloading.SimpleReloadingStrategy;
+
+import javax.faces.view.facelets.BehaviorConfig;
+import javax.faces.view.facelets.BehaviorHandler;
+import javax.faces.view.facelets.ComponentHandler;
+
+/**
+ * The reloading strategy for our behavior tag handlers
+ * note since we do not have an official api we must
+ * enforce a getConverterConfig() method to allow
+ * the reloading of converter tag handlers
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class BehaviorHandlerReloadingStrategy extends SimpleReloadingStrategy
+{
+ public BehaviorHandlerReloadingStrategy() {
+ super();
+ }
+
+ @Override
+ public Object reload(Object scriptingInstance, int artifactType) {
+ if (!(scriptingInstance instanceof ComponentHandler)) return scriptingInstance;
+ Class aclass = WeavingContext.getInstance().reload(scriptingInstance.getClass());
+ if (aclass.hashCode() == scriptingInstance.getClass().hashCode()) {
+ //class of this object has not changed although
+ // reload is enabled we can skip the rest now
+ return scriptingInstance;
+ }
+ BehaviorHandler oldHandler = (BehaviorHandler) scriptingInstance;
+ /**
+ *
+ */
+ BehaviorConfig config = (BehaviorConfig) ReflectUtil.executeMethod(oldHandler, "getBehaviorConfig");
+ BehaviorHandler newHandler = (BehaviorHandler) ReflectUtil.instantiate(aclass, new Cast(BehaviorConfig.class, config));
+
+ //save all pending non config related properties wherever possible
+ super.mapProperties(newHandler, oldHandler);
+
+ return newHandler;
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/reloading/ComponentHandlerReloadingStrategy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/reloading/ComponentHandlerReloadingStrategy.java
new file mode 100644
index 0000000..6146691
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/reloading/ComponentHandlerReloadingStrategy.java
@@ -0,0 +1,63 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.reloading;
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.Cast;
+import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
+import org.apache.myfaces.extensions.scripting.core.reloading.SimpleReloadingStrategy;
+
+import javax.faces.view.facelets.ComponentConfig;
+import javax.faces.view.facelets.ComponentHandler;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ComponentHandlerReloadingStrategy extends SimpleReloadingStrategy
+{
+
+ public ComponentHandlerReloadingStrategy() {
+ super();
+ }
+
+ @Override
+ public Object reload(Object scriptingInstance, int artifactType) {
+ if (!(scriptingInstance instanceof ComponentHandler)) return scriptingInstance;
+ Class aclass = WeavingContext.getInstance().reload(scriptingInstance.getClass());
+ if (aclass.hashCode() == scriptingInstance.getClass().hashCode()) {
+ //class of this object has not changed although
+ // reload is enabled we can skip the rest now
+ return scriptingInstance;
+ }
+ ComponentHandler oldHandler = (ComponentHandler) scriptingInstance;
+ ComponentConfig config = oldHandler.getComponentConfig();
+ ComponentHandler newHandler = (ComponentHandler) ReflectUtil.instantiate(aclass, new Cast(ComponentConfig.class, config));
+
+ //save all pending non config related properties wherever possible
+
+
+ super.mapProperties(newHandler, oldHandler);
+
+ return newHandler;
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/reloading/ConverterHandlerReloadingStrategy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/reloading/ConverterHandlerReloadingStrategy.java
new file mode 100644
index 0000000..196d210
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/reloading/ConverterHandlerReloadingStrategy.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.reloading;
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.Cast;
+import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
+import org.apache.myfaces.extensions.scripting.core.reloading.SimpleReloadingStrategy;
+
+import javax.faces.view.facelets.ComponentHandler;
+import javax.faces.view.facelets.ConverterConfig;
+import javax.faces.view.facelets.ConverterHandler;
+
+/**
+ * The reloading strategy for our converter tag handlers
+ * note since we do not have an official api we must
+ * enforce a getConverterConfig() method to allow
+ * the reloading of converter tag handlers
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+@SuppressWarnings("unused")//used dynamically
+public class ConverterHandlerReloadingStrategy extends SimpleReloadingStrategy
+{
+
+ public ConverterHandlerReloadingStrategy() {
+ super();
+ }
+
+ @Override
+ public Object reload(Object scriptingInstance, int artifactType) {
+ if (!(scriptingInstance instanceof ComponentHandler)) return scriptingInstance;
+ Class aclass = WeavingContext.getInstance().reload(scriptingInstance.getClass());
+ if (aclass.hashCode() == scriptingInstance.getClass().hashCode()) {
+ //class of this object has not changed although
+ // reload is enabled we can skip the rest now
+ return scriptingInstance;
+ }
+ ConverterHandler oldHandler = (ConverterHandler) scriptingInstance;
+ /**
+ *
+ */
+ ConverterConfig config = (ConverterConfig) ReflectUtil.executeMethod(oldHandler, "getConverterConfig");
+ ConverterHandler newHandler = (ConverterHandler) ReflectUtil.instantiate(aclass, new Cast(ConverterConfig.class, config));
+
+ //save all pending non config related properties wherever possible
+ super.mapProperties(newHandler, oldHandler);
+
+ return newHandler;
+ }
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/reloading/ValidatorHandlerReloadingStrategy.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/reloading/ValidatorHandlerReloadingStrategy.java
new file mode 100644
index 0000000..012cede
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/reloading/ValidatorHandlerReloadingStrategy.java
@@ -0,0 +1,64 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.reloading;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.common.util.Cast;
+import org.apache.myfaces.extensions.scripting.core.common.util.ReflectUtil;
+import org.apache.myfaces.extensions.scripting.core.reloading.SimpleReloadingStrategy;
+
+import javax.faces.view.facelets.ComponentHandler;
+import javax.faces.view.facelets.ValidatorConfig;
+import javax.faces.view.facelets.ValidatorHandler;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ValidatorHandlerReloadingStrategy extends SimpleReloadingStrategy
+{
+
+ public ValidatorHandlerReloadingStrategy() {
+ super();
+ }
+
+ @Override
+ public Object reload(Object scriptingInstance, int artifactType) {
+ if (!(scriptingInstance instanceof ComponentHandler)) return scriptingInstance;
+ Class aclass = WeavingContext.getInstance().reload(scriptingInstance.getClass());
+ if (aclass.hashCode() == scriptingInstance.getClass().hashCode()) {
+ //class of this object has not changed although
+ // reload is enabled we can skip the rest now
+ return scriptingInstance;
+ }
+ ValidatorHandler oldHandler = (ValidatorHandler) scriptingInstance;
+ ValidatorConfig config = oldHandler.getValidatorConfig();
+ ValidatorHandler newHandler = (ValidatorHandler) ReflectUtil.instantiate(aclass, new Cast(ValidatorConfig.class, config));
+
+ //save all pending non config related properties wherever possible
+ super.mapProperties(newHandler, oldHandler);
+
+ return newHandler;
+ }
+
+}
+
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/AliasResourceMetaImpl.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/AliasResourceMetaImpl.java
new file mode 100644
index 0000000..950e5b6
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/AliasResourceMetaImpl.java
@@ -0,0 +1,96 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+/**
+ * Contains the metadata information to reference a resource
+ *
+ * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
+ * @version $Revision: 700544 $ $Date: 2008-09-30 13:44:02 -0500 (Mar, 30 Sep 2008) $
+ */
+public class AliasResourceMetaImpl extends ResourceMetaImpl
+{
+ private String _realResourceName;
+
+ private boolean _couldContainValueExpressions;
+
+ public AliasResourceMetaImpl(String prefix, String libraryName, String libraryVersion,
+ String resourceName, String resourceVersion, String realResourceName, boolean couldContainValueExpressions)
+ {
+ super(prefix, libraryName, libraryVersion,
+ resourceName, resourceVersion);
+ _realResourceName = realResourceName;
+ _couldContainValueExpressions = couldContainValueExpressions;
+ }
+
+ public String getRealResourceName()
+ {
+ return _realResourceName;
+ }
+
+ public void setRealResourceName(String realResourceName)
+ {
+ _realResourceName = realResourceName;
+ }
+
+ @Override
+ public String getResourceIdentifier()
+ {
+ StringBuilder builder = new StringBuilder();
+ boolean firstSlashAdded = false;
+ if (getLocalePrefix() != null && getLocalePrefix().length() > 0)
+ {
+ builder.append(getLocalePrefix());
+ firstSlashAdded = true;
+ }
+ if (getLibraryName() != null)
+ {
+ if (firstSlashAdded) builder.append('/');
+ builder.append(getLibraryName());
+ firstSlashAdded = true;
+ }
+ if (getLibraryVersion() != null)
+ {
+ if (firstSlashAdded) builder.append('/');
+ builder.append(getLibraryVersion());
+ firstSlashAdded = true;
+ }
+ if (getRealResourceName() != null)
+ {
+ if (firstSlashAdded) builder.append('/');
+ builder.append(getRealResourceName());
+ firstSlashAdded = true;
+ }
+ if (getResourceVersion() != null)
+ {
+ if (firstSlashAdded) builder.append('/');
+ builder.append(getResourceVersion());
+ builder.append(
+ getRealResourceName().substring(getRealResourceName().lastIndexOf('.')));
+ firstSlashAdded = true;
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public boolean couldResourceContainValueExpressions()
+ {
+ return _couldContainValueExpressions;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/BaseResourceHandlerSupport.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/BaseResourceHandlerSupport.java
new file mode 100644
index 0000000..a9c48c9
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/BaseResourceHandlerSupport.java
@@ -0,0 +1,236 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import java.util.Map;
+
+/**
+ * A ResourceHandlerSupport implementation for use with standard Java Servlet engines,
+ * ie an engine that supports javax.servlet, and uses a standard web.xml file.
+ *
+ * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
+ * @version $Revision: 946779 $ $Date: 2010-05-20 15:31:42 -0500 (Jue, 20 May 2010) $
+ */
+public class BaseResourceHandlerSupport extends ResourceHandlerSupport
+{
+
+ /**
+ * Set the max time in miliseconds set on the "Expires" header for a resource.
+ * (default to one week in miliseconds or 604800000)
+ */
+ public static final String RESOURCE_MAX_TIME_EXPIRES = "org.apache.myfaces.RESOURCE_MAX_TIME_EXPIRES";
+
+ /**
+ * Identifies the FacesServlet mapping in the current request map.
+ */
+ private static final String CACHED_SERVLET_MAPPING =
+ BaseResourceHandlerSupport.class.getName() + ".CACHED_SERVLET_MAPPING";
+
+ private Long _startupTime;
+
+ private Long _maxTimeExpires;
+
+ public BaseResourceHandlerSupport()
+ {
+ _startupTime = System.currentTimeMillis();
+ }
+
+ public ResourceLoader[] getResourceLoaders()
+ {
+ return null;
+ }
+
+ public String calculateResourceBasePath(FacesContext facesContext)
+ {
+ FacesServletMapping mapping = getFacesServletMapping(facesContext);
+ ExternalContext externalContext = facesContext.getExternalContext();
+
+ if (mapping != null)
+ {
+ String resourceBasePath = null;
+ if (mapping.isExtensionMapping())
+ {
+ // Mapping using a suffix. In this case we have to strip
+ // the suffix. If we have a url like:
+ // http://localhost:8080/testjsf20/javax.faces.resource/imagen.jpg.jsf?ln=dojo
+ //
+ // The servlet path is /javax.faces.resource/imagen.jpg.jsf
+ //
+ // For obtain the resource name we have to remove the .jsf suffix and
+ // the prefix ResourceHandler.RESOURCE_IDENTIFIER
+ resourceBasePath = externalContext.getRequestServletPath();
+ int stripPoint = resourceBasePath.lastIndexOf('.');
+ if (stripPoint > 0)
+ {
+ resourceBasePath = resourceBasePath.substring(0, stripPoint);
+ }
+ }
+ else
+ {
+ // Mapping using prefix. In this case we have to strip
+ // the prefix used for mapping. If we have a url like:
+ // http://localhost:8080/testjsf20/faces/javax.faces.resource/imagen.jpg?ln=dojo
+ //
+ // The servlet path is /faces
+ // and the path info is /javax.faces.resource/imagen.jpg
+ //
+ // For obtain the resource name we have to remove the /faces prefix and
+ // then the prefix ResourceHandler.RESOURCE_IDENTIFIER
+ resourceBasePath = externalContext.getRequestPathInfo();
+ }
+ return resourceBasePath;
+ }
+ else
+ {
+ //If no mapping is detected, just return the
+ //information follows the servlet path but before
+ //the query string
+ return externalContext.getRequestPathInfo();
+ }
+ }
+
+ public boolean isExtensionMapping()
+ {
+ FacesServletMapping mapping = getFacesServletMapping(
+ FacesContext.getCurrentInstance());
+ if (mapping != null)
+ {
+ if (mapping.isExtensionMapping())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String getMapping()
+ {
+ FacesServletMapping mapping = getFacesServletMapping(
+ FacesContext.getCurrentInstance());
+ if (mapping != null)
+ {
+ if (mapping.isExtensionMapping())
+ {
+ return mapping.getExtension();
+ }
+ else
+ {
+ return mapping.getPrefix();
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Read the web.xml file that is in the classpath and parse its internals to
+ * figure out how the FacesServlet is mapped for the current webapp.
+ */
+ protected FacesServletMapping getFacesServletMapping(FacesContext context)
+ {
+ Map<Object, Object> attributes = context.getAttributes();
+
+ // Has the mapping already been determined during this request?
+ FacesServletMapping mapping = (FacesServletMapping) attributes.get(CACHED_SERVLET_MAPPING);
+ if (mapping == null)
+ {
+ ExternalContext externalContext = context.getExternalContext();
+ mapping = calculateFacesServletMapping(externalContext.getRequestServletPath(),
+ externalContext.getRequestPathInfo());
+
+ attributes.put(CACHED_SERVLET_MAPPING, mapping);
+ }
+ return mapping;
+ }
+
+ /**
+ * Determines the mapping of the FacesServlet in the web.xml configuration
+ * file. However, there is no need to actually parse this configuration file
+ * as runtime information is sufficient.
+ *
+ * @param servletPath The servletPath of the current request
+ * @param pathInfo The pathInfo of the current request
+ * @return the mapping of the FacesServlet in the web.xml configuration file
+ */
+ protected static FacesServletMapping calculateFacesServletMapping(
+ String servletPath, String pathInfo)
+ {
+ if (pathInfo != null)
+ {
+ // If there is a "extra path", it's definitely no extension mapping.
+ // Now we just have to determine the path which has been specified
+ // in the url-pattern, but that's easy as it's the same as the
+ // current servletPath. It doesn't even matter if "/*" has been used
+ // as in this case the servletPath is just an empty string according
+ // to the Servlet Specification (SRV 4.4).
+ return FacesServletMapping.createPrefixMapping(servletPath);
+ }
+ else
+ {
+ // In the case of extension mapping, no "extra path" is available.
+ // Still it's possible that prefix-based mapping has been used.
+ // Actually, if there was an exact match no "extra path"
+ // is available (e.g. if the url-pattern is "/faces/*"
+ // and the request-uri is "/context/faces").
+ int slashPos = servletPath.lastIndexOf('/');
+ int extensionPos = servletPath.lastIndexOf('.');
+ if (extensionPos > -1 && extensionPos > slashPos)
+ {
+ String extension = servletPath.substring(extensionPos);
+ return FacesServletMapping.createExtensionMapping(extension);
+ }
+ else
+ {
+ // There is no extension in the given servletPath and therefore
+ // we assume that it's an exact match using prefix-based mapping.
+ return FacesServletMapping.createPrefixMapping(servletPath);
+ }
+ }
+ }
+
+ public long getStartupTime()
+ {
+ return _startupTime;
+ }
+
+ public long getMaxTimeExpires()
+ {
+ if (_maxTimeExpires == null)
+ {
+ String time = FacesContext.getCurrentInstance().getExternalContext().getInitParameter(RESOURCE_MAX_TIME_EXPIRES);
+ if (time != null && time.length() > 0)
+ {
+ try
+ {
+ _maxTimeExpires = Long.parseLong(time);
+ }
+ catch (NumberFormatException e)
+ {
+ _maxTimeExpires = 604800000L;
+ }
+ }
+ else
+ {
+ _maxTimeExpires = 604800000L;
+ }
+ }
+ return _maxTimeExpires;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ClassLoaderResourceLoader.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ClassLoaderResourceLoader.java
new file mode 100644
index 0000000..f1f266e
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ClassLoaderResourceLoader.java
@@ -0,0 +1,480 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassLoaderUtils;
+
+import javax.faces.application.ProjectStage;
+import javax.faces.context.FacesContext;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * A resource loader implementation which loads resources from the thread ClassLoader.
+ *
+ */
+public class ClassLoaderResourceLoader extends ResourceLoader
+{
+ /**
+ * It checks version like this: 1, 1_0, 1_0_0, 100_100
+ *
+ * Used on getLibraryVersion to filter resource directories
+ **/
+ //protected static Pattern VERSION_CHECKER = Pattern.compile("\\p{Digit}+(_\\p{Digit}*)*");
+
+ /**
+ * It checks version like this: /1.js, /1_0.js, /1_0_0.js, /100_100.js
+ *
+ * Used on getResourceVersion to filter resources
+ **/
+ //protected static Pattern RESOURCE_VERSION_CHECKER = Pattern.compile("/\\p{Digit}+(_\\p{Digit}*)*\\..*");
+
+ /*
+ private FileFilter _libraryFileFilter = new FileFilter()
+ {
+ public boolean accept(File pathname)
+ {
+ if (pathname.isDirectory() && VERSION_CHECKER.matcher(pathname.getName()).matches())
+ {
+ return true;
+ }
+ return false;
+ }
+ };*/
+
+ /*
+ private FileFilter _resourceFileFilter = new FileFilter()
+ {
+ public boolean accept(File pathname)
+ {
+ if (pathname.isDirectory() && RESOURCE_VERSION_CHECKER.matcher(pathname.getName()).matches())
+ {
+ return true;
+ }
+ return false;
+ }
+ };*/
+
+ private final boolean _developmentStage;
+
+ public ClassLoaderResourceLoader(String prefix)
+ {
+ super(prefix);
+ _developmentStage = FacesContext.getCurrentInstance().isProjectStage(ProjectStage.Development);
+ }
+
+ @Override
+ public String getLibraryVersion(String path)
+ {
+ return null;
+ /*
+ String libraryVersion = null;
+ if (getPrefix() != null)
+ path = getPrefix() + '/' + path;
+
+ URL url = getClassLoader().getResource(path);
+
+ if (url == null)
+ {
+ // This library does not exists for this
+ // ResourceLoader
+ return null;
+ }
+
+ // The problem here is how to scan the directory. When a ClassLoader
+ // is used two cases could occur
+ // 1. The files are unpacked so we can use Url.toURI and crawl
+ // the directory using the api for files.
+ // 2. The files are packed in a jar. This case is more tricky,
+ // because we only have a URL. Checking the jar api we can use
+ // JarURLConnection (Sounds strange, but the api of
+ // URL.openConnection says that for a jar connection a
+ // JarURLConnection is returned). From this point we can access
+ // to the jar api and solve the algoritm.
+ if (url.getProtocol().equals("file"))
+ {
+ try
+ {
+ File directory = new File(url.toURI());
+ if (directory.isDirectory())
+ {
+ File[] versions = directory.listFiles(_libraryFileFilter);
+ for (int i = 0; i < versions.length; i++)
+ {
+ String version = versions[i].getName();
+ if (VERSION_CHECKER.matcher(version).matches())
+ {
+ if (libraryVersion == null)
+ {
+ libraryVersion = version;
+ }
+ else if (getVersionComparator().compare(libraryVersion, version) < 0)
+ {
+ libraryVersion = version;
+ }
+ }
+ }
+ }
+ }
+ catch (URISyntaxException e)
+ {
+ // Just return null, because library version cannot be
+ // resolved.
+ Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName());
+ if (log.isLoggable(Level.WARNING))
+ {
+ log.log(Level.WARNING, "url "+url.toString()+" cannot be translated to uri: "+e.getMessage(), e);
+ }
+ }
+ }
+ else if (isJarResourceProtocol(url.getProtocol()))
+ {
+ try
+ {
+ url = getClassLoader().getResource(path + '/');
+
+ if (url != null)
+ {
+ JarURLConnection conn = (JarURLConnection)url.openConnection();
+ // See DIGESTER-29 for related problem
+ conn.setUseCaches(false);
+
+ try
+ {
+ if (conn.getJarEntry().isDirectory())
+ {
+ // Unfortunately, we have to scan all entry files
+ // because there is no proper api to scan it as a
+ // directory tree.
+ JarFile file = conn.getJarFile();
+ for (Enumeration<JarEntry> en = file.entries(); en.hasMoreElements();)
+ {
+ JarEntry entry = en.nextElement();
+ String entryName = entry.getName();
+
+ if (entryName.startsWith(path + '/'))
+ {
+ if (entryName.length() == path.length() + 1)
+ {
+ // the same string, just skip it
+ continue;
+ }
+
+ if (entryName.charAt(entryName.length() - 1) != '/')
+ {
+ // Skip files
+ continue;
+ }
+
+ entryName = entryName.substring(path.length() + 1, entryName.length() - 1);
+
+ if (entryName.indexOf('/') >= 0)
+ {
+ // Inner Directory
+ continue;
+ }
+
+ String version = entryName;
+ if (VERSION_CHECKER.matcher(version).matches())
+ {
+ if (libraryVersion == null)
+ {
+ libraryVersion = version;
+ }
+ else if (getVersionComparator().compare(libraryVersion, version) < 0)
+ {
+ libraryVersion = version;
+ }
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ //See TRINIDAD-73
+ //just close the input stream again if
+ //by inspecting the entries the stream
+ //was let open.
+ try
+ {
+ conn.getInputStream().close();
+ }
+ catch (Exception exception)
+ {
+ // Ignored
+ }
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ // Just return null, because library version cannot be
+ // resolved.
+ Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName());
+ if (log.isLoggable(Level.WARNING))
+ {
+ log.log(Level.WARNING, "IOException when scanning for resource in jar file:", e);
+ }
+ }
+ }
+ return libraryVersion;
+ */
+ }
+
+ @Override
+ public InputStream getResourceInputStream(ResourceMeta resourceMeta)
+ {
+ if (getPrefix() != null && !"".equals(getPrefix()))
+ {
+ return getClassLoader().getResourceAsStream(getPrefix() + '/' + resourceMeta.getResourceIdentifier());
+ }
+ else
+ {
+ return getClassLoader().getResourceAsStream(resourceMeta.getResourceIdentifier());
+ }
+ }
+
+ @Override
+ public URL getResourceURL(ResourceMeta resourceMeta)
+ {
+ if (getPrefix() != null && !"".equals(getPrefix()))
+ {
+ return getClassLoader().getResource(getPrefix() + '/' + resourceMeta.getResourceIdentifier());
+ }
+ else
+ {
+ return getClassLoader().getResource(resourceMeta.getResourceIdentifier());
+ }
+ }
+
+ @Override
+ public String getResourceVersion(String path)
+ {
+ return null;
+ /*
+ String resourceVersion = null;
+
+ if (getPrefix() != null)
+ path = getPrefix() + '/' + path;
+
+ URL url = getClassLoader().getResource(path);
+
+ if (url == null)
+ {
+ // This library does not exists for this
+ // ResourceLoader
+ return null;
+ }
+
+ if (url.getProtocol().equals("file"))
+ {
+ try
+ {
+ File directory = new File(url.toURI());
+ if (directory.isDirectory())
+ {
+ File[] versions = directory.listFiles(_resourceFileFilter);
+ for (int i = 0; i < versions.length; i++)
+ {
+ String version = versions[i].getName();
+ if (resourceVersion == null)
+ {
+ resourceVersion = version;
+ }
+ else if (getVersionComparator().compare(resourceVersion, version) < 0)
+ {
+ resourceVersion = version;
+ }
+ }
+ //Since it is a directory and no version found set resourceVersion as invalid
+ if (resourceVersion == null)
+ {
+ resourceVersion = VERSION_INVALID;
+ }
+ }
+ }
+ catch (URISyntaxException e)
+ {
+ Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName());
+ if (log.isLoggable(Level.WARNING))
+ {
+ log.log(Level.WARNING, "url "+url.toString()+" cannot be translated to uri: "+e.getMessage(), e);
+ }
+ }
+ }
+ else if (isJarResourceProtocol(url.getProtocol()))
+ {
+ try
+ {
+ url = getClassLoader().getResource(path + '/');
+
+ if (url != null)
+ {
+ JarURLConnection conn = (JarURLConnection)url.openConnection();
+ // See DIGESTER-29 for related problem
+ conn.setUseCaches(false);
+
+ try
+ {
+ if (conn.getJarEntry().isDirectory())
+ {
+ // Unfortunately, we have to scan all entry files
+ JarFile file = conn.getJarFile();
+ for (Enumeration<JarEntry> en = file.entries(); en.hasMoreElements();)
+ {
+ JarEntry entry = en.nextElement();
+ String entryName = entry.getName();
+
+ if (entryName.startsWith(path + '/'))
+ {
+ if (entryName.length() == path.length() + 1)
+ {
+ // the same string, just skip it
+ continue;
+ }
+
+ entryName = entryName.substring(path.length());
+ if (RESOURCE_VERSION_CHECKER.matcher(entryName).matches())
+ {
+ String version = entryName.substring(1, entryName.lastIndexOf('.'));
+ if (resourceVersion == null)
+ {
+ resourceVersion = version;
+ }
+ else if (getVersionComparator().compare(resourceVersion, version) < 0)
+ {
+ resourceVersion = version;
+ }
+ }
+ }
+ }
+ if (resourceVersion == null)
+ {
+ resourceVersion = VERSION_INVALID;
+ }
+ }
+ }
+ finally
+ {
+ //See TRINIDAD-73
+ //just close the input stream again if
+ //by inspecting the entries the stream
+ //was let open.
+ try
+ {
+ conn.getInputStream().close();
+ }
+ catch (Exception exception)
+ {
+ // Ignored
+ }
+ }
+
+ }
+ }
+ catch (IOException e)
+ {
+ // Just return null, because library version cannot be
+ // resolved.
+ Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName());
+ if (log.isLoggable(Level.WARNING))
+ {
+ log.log(Level.WARNING, "IOException when scanning for resource in jar file:", e);
+ }
+ }
+ }
+ return resourceVersion;
+ */
+ }
+
+ @Override
+ public ResourceMeta createResourceMeta(String prefix, String libraryName, String libraryVersion,
+ String resourceName, String resourceVersion)
+ {
+ if (_developmentStage && libraryName != null &&
+ ResourceLoaderUtils.JAVAX_FACES_LIBRARY_NAME.equals(libraryName) &&
+ ResourceLoaderUtils.JSF_JS_RESOURCE_NAME.equals(resourceName))
+ {
+ // InternalClassLoaderResourceLoader will serve it, so return null in this case.
+ return null;
+ } else if (_developmentStage && libraryName != null &&
+ ResourceLoaderUtils.MYFACES_LIBRARY_NAME.equals(libraryName) &&
+ ResourceLoaderUtils.MYFACES_JS_RESOURCE_NAME.equals(resourceName)) {
+ // InternalClassLoaderResourceLoader will serve it, so return null in this case.
+ return null;
+ } else
+ {
+ return new ResourceMetaImpl(prefix, libraryName, libraryVersion, resourceName, resourceVersion);
+ }
+ }
+
+ /**
+ * Returns the ClassLoader to use when looking up resources under the top level package. By default, this is the
+ * context class loader.
+ *
+ * @return the ClassLoader used to lookup resources
+ */
+ protected ClassLoader getClassLoader()
+ {
+ return ClassLoaderUtils.getDefaultClassLoader();
+ }
+
+ @Override
+ public boolean libraryExists(String libraryName)
+ {
+ if (getPrefix() != null && !"".equals(getPrefix()))
+ {
+ URL url = getClassLoader().getResource(getPrefix() + '/' + libraryName);
+ if (url != null)
+ {
+ return true;
+ }
+ }
+ else
+ {
+ URL url = getClassLoader().getResource(libraryName);
+ if (url != null)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * <p>Determines whether the given URL resource protocol refers to a JAR file. Note that
+ * BEA WebLogic and IBM WebSphere don't use the "jar://" protocol for some reason even
+ * though you can treat these resources just like normal JAR files, i.e. you can ignore
+ * the difference between these protocols after this method has returned.</p>
+ *
+ * @param protocol the URL resource protocol you want to check
+ *
+ * @return <code>true</code> if the given URL resource protocol refers to a JAR file,
+ * <code>false</code> otherwise
+ */
+ /*
+ private static boolean isJarResourceProtocol(String protocol)
+ {
+ // Websphere uses the protocol "wsjar://" and Weblogic uses the protocol "zip://".
+ return "jar".equals(protocol) || "wsjar".equals(protocol) || "zip".equals(protocol);
+ }*/
+
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ExternalContextResourceLoader.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ExternalContextResourceLoader.java
new file mode 100644
index 0000000..43f9782
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ExternalContextResourceLoader.java
@@ -0,0 +1,205 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+import javax.faces.context.FacesContext;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * A resource loader implementation which loads resources from the webapp root. It uses the methods on ExternalContext
+ * for handle resources.
+ *
+ */
+public class ExternalContextResourceLoader extends ResourceLoader
+{
+ /**
+ * It checks version like this: /1/, /1_0/, /1_0_0/, /100_100/
+ *
+ * Used on getLibraryVersion to filter resource directories
+ **/
+ protected static Pattern VERSION_CHECKER = Pattern.compile("/\\p{Digit}+(_\\p{Digit}*)*/");
+
+ /**
+ * It checks version like this: /1.js, /1_0.js, /1_0_0.js, /100_100.js
+ *
+ * Used on getResourceVersion to filter resources
+ **/
+ protected static Pattern RESOURCE_VERSION_CHECKER = Pattern.compile("/\\p{Digit}+(_\\p{Digit}*)*\\..*");
+
+ public ExternalContextResourceLoader(String prefix)
+ {
+ super(prefix);
+ }
+
+ protected Set<String> getResourcePaths(String path)
+ {
+ return FacesContext.getCurrentInstance().getExternalContext().getResourcePaths(getPrefix() + '/' + path);
+ }
+
+ @Override
+ public String getResourceVersion(String path)
+ {
+ String resourceVersion = null;
+ Set<String> resourcePaths = this.getResourcePaths(path);
+ if (getPrefix() != null)
+ path = getPrefix() + '/' + path;
+
+ if (null != resourcePaths && !resourcePaths.isEmpty())
+ {
+ // resourceVersion = // execute the comment
+ // Look in the resourcePaths for versioned resources.
+ // If one or more versioned resources are found, take
+ // the one with the "highest" version number as the value
+ // of resourceVersion. If no versioned libraries
+ // are found, let resourceVersion remain null.
+ for (String resourcePath : resourcePaths)
+ {
+ String version = resourcePath.substring(path.length());
+
+ if (RESOURCE_VERSION_CHECKER.matcher(version).matches())
+ {
+ version = version.substring(1, version.lastIndexOf('.'));
+ if (resourceVersion == null)
+ {
+ resourceVersion = version;
+ }
+ else if (getVersionComparator().compare(resourceVersion, version) < 0)
+ {
+ resourceVersion = version;
+ }
+ }
+ }
+ //Since it is a directory and no version was found, set as invalid
+ if (resourceVersion == null)
+ {
+ resourceVersion = VERSION_INVALID;
+ }
+ }
+ return resourceVersion;
+ }
+
+ @Override
+ public String getLibraryVersion(String path)
+ {
+ String libraryVersion = null;
+ Set<String> libraryPaths = this.getResourcePaths(path);
+ path = getPrefix() + '/' + path;
+ if (null != libraryPaths && !libraryPaths.isEmpty())
+ {
+ // Look in the libraryPaths for versioned libraries.
+ // If one or more versioned libraries are found, take
+ // the one with the "highest" version number as the value
+ // of libraryVersion. If no versioned libraries
+ // are found, let libraryVersion remain null.
+
+ for (Iterator<String> it = libraryPaths.iterator(); it.hasNext();)
+ {
+ String libraryPath = it.next();
+ String version = libraryPath.substring(path.length());
+
+ if (VERSION_CHECKER.matcher(version).matches())
+ {
+ version = version.substring(1, version.length() - 1);
+ if (libraryVersion == null)
+ {
+ libraryVersion = version;
+ }
+ else if (getVersionComparator().compare(libraryVersion, version) < 0)
+ {
+ libraryVersion = version;
+ }
+ }
+ }
+ }
+ return libraryVersion;
+ }
+
+ @Override
+ public URL getResourceURL(ResourceMeta resourceMeta)
+ {
+ try
+ {
+ return FacesContext.getCurrentInstance().getExternalContext().getResource(
+ getPrefix() + '/' + resourceMeta.getResourceIdentifier());
+ }
+ catch (MalformedURLException e)
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public InputStream getResourceInputStream(ResourceMeta resourceMeta)
+ {
+ return FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream(
+ getPrefix() + '/' + resourceMeta.getResourceIdentifier());
+ }
+
+ @Override
+ public ResourceMeta createResourceMeta(String prefix, String libraryName, String libraryVersion,
+ String resourceName, String resourceVersion)
+ {
+ return new ResourceMetaImpl(prefix, libraryName, libraryVersion, resourceName, resourceVersion);
+ }
+
+ @Override
+ public boolean libraryExists(String libraryName)
+ {
+ if (getPrefix() != null && !"".equals(getPrefix()))
+ {
+ try
+ {
+ URL url =
+ FacesContext.getCurrentInstance().getExternalContext().getResource(
+ getPrefix() + '/' + libraryName);
+ if (url != null)
+ {
+ return true;
+ }
+ }
+ catch (MalformedURLException e)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ try
+ {
+
+ URL url = FacesContext.getCurrentInstance().getExternalContext().getResource(libraryName);
+
+ if (url != null)
+ {
+ return true;
+ }
+ }
+ catch (MalformedURLException e)
+ {
+ return false;
+ }
+ }
+ return false;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/FacesServletMapping.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/FacesServletMapping.java
new file mode 100644
index 0000000..d6a1d72
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/FacesServletMapping.java
@@ -0,0 +1,159 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+/**
+ * Represents a mapping entry of the FacesServlet in the web.xml
+ * configuration file.
+ */
+public class FacesServletMapping
+{
+
+ /**
+ * The path ("/faces", for example) which has been specified in the
+ * url-pattern of the FacesServlet mapping.
+ */
+ private String prefix;
+
+ /**
+ * The extension (".jsf", for example) which has been specified in the
+ * url-pattern of the FacesServlet mapping.
+ */
+ private String extension;
+
+ /**
+ * Creates a new FacesServletMapping object using prefix mapping.
+ *
+ * @param path The path ("/faces", for example) which has been specified
+ * in the url-pattern of the FacesServlet mapping.
+ * @return a newly created FacesServletMapping
+ */
+ public static FacesServletMapping createPrefixMapping(String path)
+ {
+ FacesServletMapping mapping = new FacesServletMapping();
+ mapping.setPrefix(path);
+ return mapping;
+ }
+
+ /**
+ * Creates a new FacesServletMapping object using extension mapping.
+ *
+ * @param path The extension (".jsf", for example) which has been
+ * specified in the url-pattern of the FacesServlet mapping.
+ * @return a newly created FacesServletMapping
+ */
+ public static FacesServletMapping createExtensionMapping(
+ String extension)
+ {
+ FacesServletMapping mapping = new FacesServletMapping();
+ mapping.setExtension(extension);
+ return mapping;
+ }
+
+ /**
+ * Returns the path ("/faces", for example) which has been specified in
+ * the url-pattern of the FacesServlet mapping. If this mapping is based
+ * on an extension, <code>null</code> will be returned. Note that this
+ * path is not the same as the specified url-pattern as the trailing
+ * "/*" is omitted.
+ *
+ * @return the path which has been specified in the url-pattern
+ */
+ public String getPrefix()
+ {
+ return prefix;
+ }
+
+ /**
+ * Sets the path ("/faces/", for example) which has been specified in
+ * the url-pattern.
+ *
+ * @param path The path which has been specified in the url-pattern
+ */
+ public void setPrefix(String path)
+ {
+ this.prefix = path;
+ }
+
+ /**
+ * Returns the extension (".jsf", for example) which has been specified
+ * in the url-pattern of the FacesServlet mapping. If this mapping is
+ * not based on an extension, <code>null</code> will be returned.
+ *
+ * @return the extension which has been specified in the url-pattern
+ */
+ public String getExtension()
+ {
+ return extension;
+ }
+
+ /**
+ * Sets the extension (".jsf", for example) which has been specified in
+ * the url-pattern of the FacesServlet mapping.
+ *
+ * @param extension The extension which has been specified in the url-pattern
+ */
+ public void setExtension(String extension)
+ {
+ this.extension = extension;
+ }
+
+ /**
+ * Indicates whether this mapping is based on an extension (e.g.
+ * ".jsp").
+ *
+ * @return <code>true</code>, if this mapping is based is on an
+ * extension, <code>false</code> otherwise
+ */
+ public boolean isExtensionMapping()
+ {
+ return extension != null;
+ }
+
+ /**
+ * Indicates whether this mapping is based on a prefix (e.g.
+ * /faces/*").
+ *
+ * @return <code>true</code>, if this mapping is based is on a
+ * prefix, <code>false</code> otherwise
+ */
+ public boolean isPrefixMapping()
+ {
+ return prefix != null;
+ }
+
+ /**
+ * Returns the url-pattern entry for this servlet mapping.
+ *
+ * @return the url-pattern entry for this servlet mapping
+ */
+ public String getUrlPattern()
+ {
+ if (isExtensionMapping())
+ {
+ return "*" + extension;
+ }
+ else
+ {
+ return prefix + "/*";
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerCache.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerCache.java
new file mode 100644
index 0000000..c8326bf
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerCache.java
@@ -0,0 +1,241 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+import javax.faces.application.ProjectStage;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class ResourceHandlerCache
+{
+ private static final Logger log = Logger
+ .getLogger(ResourceHandlerCache.class.getName());
+
+ private Boolean _resourceCacheEnabled = null;
+ private Map<ResourceKey, ResourceValue> _resourceCacheMap = null;
+
+ private static final String RESOURCE_HANDLER_CACHE_SIZE_ATTRIBUTE = "org.apache.myfaces.RESOURCE_HANDLER_CACHE_SIZE";
+ private static final int RESOURCE_HANDLER_CACHE_DEFAULT_SIZE = 500;
+
+ private static final String RESOURCE_HANDLER_CACHE_ENABLED_ATTRIBUTE = "org.apache.myfaces.RESOURCE_HANDLER_CACHE_ENABLED";
+ private static final boolean RESOURCE_HANDLER_CACHE_ENABLED_DEFAULT = true;
+
+ public ResourceValue getResource(String resourceName, String libraryName,
+ String contentType)
+ {
+ if (!isResourceCachingEnabled() || _resourceCacheMap == null)
+ return null;
+
+ if (log.isLoggable(Level.FINE))
+ log.log(Level.FINE, "Attemping to get resource from cache for "
+ + resourceName);
+
+ ResourceKey key = new ResourceKey(resourceName, libraryName,
+ contentType);
+
+ return _resourceCacheMap.get(key);
+ }
+
+ public boolean containsResource(String resourceName, String libraryName,
+ String contentType)
+ {
+ if (!isResourceCachingEnabled() || _resourceCacheMap == null)
+ return false;
+ ResourceKey key = new ResourceKey(resourceName, libraryName,
+ contentType);
+ return _resourceCacheMap.containsKey(key);
+ }
+
+ public void putResource(String resourceName, String libraryName,
+ String contentType, ResourceMeta resource, ResourceLoader loader)
+ {
+ if (!isResourceCachingEnabled())
+ return;
+
+ if (log.isLoggable(Level.FINE))
+ log.log(Level.FINE, "Attemping to put resource to cache for "
+ + resourceName);
+
+ if (_resourceCacheMap == null)
+ {
+ if (log.isLoggable(Level.FINE))
+ log.log(Level.FINE, "Initializing resource cache map");
+ _resourceCacheMap = Collections
+ .synchronizedMap(new _ResourceMap<ResourceKey, ResourceValue>(
+ getMaxSize()));
+ }
+
+ _resourceCacheMap.put(new ResourceKey(resourceName, libraryName,
+ contentType), new ResourceValue(resource, loader));
+ }
+
+ private boolean isResourceCachingEnabled()
+ {
+ if (_resourceCacheEnabled == null)
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+
+ //first, check to make sure that ProjectStage is production, if not, skip caching
+ if (!facesContext.isProjectStage(ProjectStage.Production))
+ {
+ return _resourceCacheEnabled = Boolean.FALSE;
+ }
+
+ ExternalContext externalContext = facesContext.getExternalContext();
+ if (externalContext == null)
+ return false; //don't cache right now, but don't disable it yet either
+
+ //if in production, make sure that the cache is not explicitly disabled via context param
+ String configParam = externalContext
+ .getInitParameter(ResourceHandlerCache.RESOURCE_HANDLER_CACHE_ENABLED_ATTRIBUTE);
+ _resourceCacheEnabled = configParam == null ? ResourceHandlerCache.RESOURCE_HANDLER_CACHE_ENABLED_DEFAULT
+ : Boolean.parseBoolean(configParam);
+
+ if (log.isLoggable(Level.FINE))
+ {
+ log.log(Level.FINE, "MyFaces Resource Caching Enabled="
+ + _resourceCacheEnabled);
+ }
+ }
+ return _resourceCacheEnabled;
+ }
+
+ private int getMaxSize()
+ {
+ ExternalContext externalContext = FacesContext.getCurrentInstance()
+ .getExternalContext();
+
+ String configParam = externalContext == null ? null : externalContext
+ .getInitParameter(RESOURCE_HANDLER_CACHE_SIZE_ATTRIBUTE);
+ return configParam == null ? RESOURCE_HANDLER_CACHE_DEFAULT_SIZE
+ : Integer.parseInt(configParam);
+ }
+
+ public static class ResourceKey
+ {
+ private String resourceName;
+ private String libraryName;
+ private String contentType;
+
+ public ResourceKey(String resourceName, String libraryName,
+ String contentType)
+ {
+ this.resourceName = resourceName;
+ this.libraryName = libraryName;
+ this.contentType = contentType;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((contentType == null) ? 0 : contentType.hashCode());
+ result = prime * result
+ + ((libraryName == null) ? 0 : libraryName.hashCode());
+ result = prime * result
+ + ((resourceName == null) ? 0 : resourceName.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ResourceKey other = (ResourceKey) obj;
+ if (contentType == null)
+ {
+ if (other.contentType != null)
+ return false;
+ }
+ else if (!contentType.equals(other.contentType))
+ return false;
+ if (libraryName == null)
+ {
+ if (other.libraryName != null)
+ return false;
+ }
+ else if (!libraryName.equals(other.libraryName))
+ return false;
+ if (resourceName == null)
+ {
+ if (other.resourceName != null)
+ return false;
+ }
+ else if (!resourceName.equals(other.resourceName))
+ return false;
+ return true;
+ }
+ }
+
+ public static class ResourceValue
+ {
+ private ResourceMeta resourceMeta;
+
+ private ResourceLoader resourceLoader;
+
+ public ResourceValue(ResourceMeta resourceMeta,
+ ResourceLoader resourceLoader)
+ {
+ super();
+ this.resourceMeta = resourceMeta;
+ this.resourceLoader = resourceLoader;
+ }
+
+ public ResourceMeta getResourceMeta()
+ {
+ return resourceMeta;
+ }
+
+ public ResourceLoader getResourceLoader()
+ {
+ return resourceLoader;
+ }
+ }
+
+ private static class _ResourceMap<K, V> extends LinkedHashMap<K, V>
+ {
+ private static final long serialVersionUID = 1L;
+ private int maxCapacity;
+
+ public _ResourceMap(int cacheSize)
+ {
+ // create map at max capacity and 1.1 load factor to avoid rehashing
+ super(cacheSize + 1, 1.1f, true);
+ maxCapacity = cacheSize;
+ }
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<K, V> eldest)
+ {
+ return size() > maxCapacity;
+ }
+ }
+}
\ No newline at end of file
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerImpl.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerImpl.java
new file mode 100644
index 0000000..3b4cbcd
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerImpl.java
@@ -0,0 +1,567 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+import org.apache.myfaces.renderkit.ErrorPageWriter;
+import org.apache.myfaces.extensions.scripting.core.common.util.ClassLoaderUtils;
+
+import javax.faces.application.Resource;
+import javax.faces.application.ResourceHandler;
+import javax.faces.context.FacesContext;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author Simon Lessard (latest modification by $Author: slessard $)
+ *
+ * @version $Revision: 696515 $ $Date: 2008-09-17 19:37:53 -0500 (mer., 17 sept. 2008) $
+ */
+public abstract class ResourceHandlerImpl extends ResourceHandler
+{
+
+ private static final String IS_RESOURCE_REQUEST = "org.apache.myfaces.IS_RESOURCE_REQUEST";
+
+ private ResourceHandlerSupport _resourceHandlerSupport;
+
+ private ResourceHandlerCache _resourceHandlerCache;
+
+ //private static final Log log = LogFactory.getLog(ResourceHandlerImpl.class);
+ private static final Logger log = Logger.getLogger(ResourceHandlerImpl.class.getName());
+
+ private static final int _BUFFER_SIZE = 2048;
+
+ @Override
+ public Resource createResource(String resourceName)
+ {
+ return createResource(resourceName, null);
+ }
+
+ @Override
+ public Resource createResource(String resourceName, String libraryName)
+ {
+ return createResource(resourceName, libraryName, null);
+ }
+
+ @Override
+ public Resource createResource(String resourceName, String libraryName,
+ String contentType)
+ {
+ Resource resource = null;
+
+ if (contentType == null)
+ {
+ //Resolve contentType using ExternalContext.getMimeType
+ contentType = FacesContext.getCurrentInstance().getExternalContext().getMimeType(resourceName);
+ }
+
+ if(getResourceLoaderCache().containsResource(resourceName, libraryName, contentType))
+ {
+ ResourceHandlerCache.ResourceValue resourceValue = getResourceLoaderCache().getResource(resourceName, libraryName, contentType);
+ resource = new ResourceImpl(resourceValue.getResourceMeta(), resourceValue.getResourceLoader(),
+ getResourceHandlerSupport(), contentType);
+ }
+ else
+ {
+ for (ResourceLoader loader : getResourceHandlerSupport()
+ .getResourceLoaders())
+ {
+ ResourceMeta resourceMeta = deriveResourceMeta(loader,
+ resourceName, libraryName);
+
+ if (resourceMeta != null)
+ {
+ resource = new ResourceImpl(resourceMeta, loader,
+ getResourceHandlerSupport(), contentType);
+
+ getResourceLoaderCache().putResource(resourceName, libraryName, contentType, resourceMeta, loader);
+ break;
+ }
+ }
+ }
+
+ return resource;
+ }
+
+ /**
+ * This method try to create a ResourceMeta for a specific resource
+ * loader. If no library, or resource is found, just return null,
+ * so the algorithm in createResource can continue checking with the
+ * next registered ResourceLoader.
+ */
+ protected ResourceMeta deriveResourceMeta(ResourceLoader resourceLoader,
+ String resourceName, String libraryName)
+ {
+ String localePrefix = getLocalePrefixForLocateResource();
+ String resourceVersion = null;
+ String libraryVersion = null;
+ ResourceMeta resourceId = null;
+
+ //1. Try to locate resource in a localized path
+ if (localePrefix != null)
+ {
+ if (null != libraryName)
+ {
+ String pathToLib = localePrefix + '/' + libraryName;
+ libraryVersion = resourceLoader.getLibraryVersion(pathToLib);
+
+ if (null != libraryVersion)
+ {
+ String pathToResource = localePrefix + '/'
+ + libraryName + '/' + libraryVersion + '/'
+ + resourceName;
+ resourceVersion = resourceLoader
+ .getResourceVersion(pathToResource);
+ }
+ else
+ {
+ String pathToResource = localePrefix + '/'
+ + libraryName + '/' + resourceName;
+ resourceVersion = resourceLoader
+ .getResourceVersion(pathToResource);
+ }
+
+ if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+ {
+ resourceId = resourceLoader.createResourceMeta(localePrefix, libraryName,
+ libraryVersion, resourceName, resourceVersion);
+ }
+ }
+ else
+ {
+ resourceVersion = resourceLoader
+ .getResourceVersion(localePrefix + '/'+ resourceName);
+ if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+ {
+ resourceId = resourceLoader.createResourceMeta(localePrefix, null, null,
+ resourceName, resourceVersion);
+ }
+ }
+
+ if (resourceId != null)
+ {
+ URL url = resourceLoader.getResourceURL(resourceId);
+ if (url == null)
+ {
+ resourceId = null;
+ }
+ }
+ }
+
+ //2. Try to localize resource in a non localized path
+ if (resourceId == null)
+ {
+ if (null != libraryName)
+ {
+ libraryVersion = resourceLoader.getLibraryVersion(libraryName);
+
+ if (null != libraryVersion)
+ {
+ String pathToResource = (libraryName + '/' + libraryVersion
+ + '/' + resourceName);
+ resourceVersion = resourceLoader
+ .getResourceVersion(pathToResource);
+ }
+ else
+ {
+ String pathToResource = (libraryName + '/'
+ + resourceName);
+ resourceVersion = resourceLoader
+ .getResourceVersion(pathToResource);
+ }
+
+ if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+ {
+ resourceId = resourceLoader.createResourceMeta(null, libraryName,
+ libraryVersion, resourceName, resourceVersion);
+ }
+ }
+ else
+ {
+ resourceVersion = resourceLoader
+ .getResourceVersion(resourceName);
+
+ if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
+ {
+ resourceId = resourceLoader.createResourceMeta(null, null, null,
+ resourceName, resourceVersion);
+ }
+ }
+
+ if (resourceId != null)
+ {
+ URL url = resourceLoader.getResourceURL(resourceId);
+ if (url == null)
+ {
+ resourceId = null;
+ }
+ }
+ }
+
+ return resourceId;
+ }
+
+ @Override
+ public String getRendererTypeForResourceName(String resourceName)
+ {
+ if (resourceName.endsWith(".js"))
+ return "javax.faces.resource.Script";
+ else if (resourceName.endsWith(".css"))
+ return "javax.faces.resource.Stylesheet";
+ return null;
+ }
+
+ /**
+ * Handle the resource request, writing in the output.
+ *
+ * This method implements an algorithm semantically identical to
+ * the one described on the javadoc of ResourceHandler.handleResourceRequest
+ */
+ @Override
+ public void handleResourceRequest(FacesContext facesContext) throws IOException
+ {
+ try
+ {
+ String resourceBasePath = getResourceHandlerSupport()
+ .calculateResourceBasePath(facesContext);
+
+ if (resourceBasePath == null)
+ {
+ // No base name could be calculated, so no further
+ //advance could be done here. HttpServletResponse.SC_NOT_FOUND
+ //cannot be returned since we cannot extract the
+ //resource base name
+ return;
+ }
+
+ // We neet to get an instance of HttpServletResponse, but sometimes
+ // the response object is wrapped by several instances of
+ // ServletResponseWrapper (like ResponseSwitch).
+ // Since we are handling a resource, we can expect to get an
+ // HttpServletResponse.
+ Object response = facesContext.getExternalContext().getResponse();
+
+ //TODO merge the servlet response determination in
+ HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+ if (httpServletResponse == null)
+ {
+ throw new IllegalStateException("Could not obtain an instance of HttpServletResponse.");
+ }
+
+ if (isResourceIdentifierExcluded(facesContext, resourceBasePath))
+ {
+ httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ String resourceName = null;
+ if (resourceBasePath.startsWith(ResourceHandler.RESOURCE_IDENTIFIER))
+ {
+ resourceName = resourceBasePath
+ .substring(ResourceHandler.RESOURCE_IDENTIFIER.length() + 1);
+ }
+ else
+ {
+ //Does not have the conditions for be a resource call
+ httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ String libraryName = facesContext.getExternalContext()
+ .getRequestParameterMap().get("ln");
+
+ Resource resource = null;
+ if (libraryName != null)
+ {
+ //log.info("libraryName=" + libraryName);
+ resource = facesContext.getApplication().getResourceHandler().createResource(resourceName, libraryName);
+ }
+ else
+ {
+ resource = facesContext.getApplication().getResourceHandler().createResource(resourceName);
+ }
+
+ if (resource == null)
+ {
+ httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ if (!resource.userAgentNeedsUpdate(facesContext))
+ {
+ httpServletResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+ return;
+ }
+
+ httpServletResponse.setContentType(resource.getContentType());
+
+ Map<String, String> headers = resource.getResponseHeaders();
+
+ for (Map.Entry<String, String> entry : headers.entrySet())
+ {
+ httpServletResponse.setHeader(entry.getKey(), entry.getValue());
+ }
+
+ //serve up the bytes (taken from trinidad ResourceServlet)
+ try
+ {
+ InputStream in = resource.getInputStream();
+ OutputStream out = httpServletResponse.getOutputStream();
+ byte[] buffer = new byte[_BUFFER_SIZE];
+
+ try
+ {
+ int count = pipeBytes(in, out, buffer);
+ //set the content lenght
+ httpServletResponse.setContentLength(count);
+ }
+ finally
+ {
+ try
+ {
+ in.close();
+ }
+ finally
+ {
+ out.close();
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ //TODO: Log using a localized message (which one?)
+ if (log.isLoggable(Level.SEVERE))
+ log.severe("Error trying to load resource " + resourceName
+ + " with library " + libraryName + " :"
+ + e.getMessage());
+ httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ }
+ }
+ catch (Throwable ex)
+ {
+ // handle the Throwable accordingly. Maybe generate an error page.
+ // FIXME we are creating a html error page for a non html request here
+ // shouln't we do something better? -=Jakob Korherr=-
+ ErrorPageWriter.handleThrowable(facesContext, ex);
+ }
+ }
+
+ /**
+ * Reads the specified input stream into the provided byte array storage and
+ * writes it to the output stream.
+ */
+ private static int pipeBytes(InputStream in, OutputStream out, byte[] buffer)
+ throws IOException
+ {
+ int count = 0;
+ int length;
+
+ while ((length = (in.read(buffer))) >= 0)
+ {
+ out.write(buffer, 0, length);
+ count += length;
+ }
+ return count;
+ }
+
+ @Override
+ public boolean isResourceRequest(FacesContext facesContext)
+ {
+ // Since this method could be called many times we save it
+ //on request map so the first time is calculated it remains
+ //alive until the end of the request
+ Boolean value = (Boolean) facesContext.getAttributes().get(IS_RESOURCE_REQUEST);
+
+ if (value != null && value)
+ {
+ //return the saved value
+ return value;
+ }
+ else
+ {
+ String resourceBasePath = getResourceHandlerSupport()
+ .calculateResourceBasePath(facesContext);
+
+ if (resourceBasePath != null
+ && resourceBasePath.startsWith(ResourceHandler.RESOURCE_IDENTIFIER))
+ {
+ facesContext.getAttributes().put(IS_RESOURCE_REQUEST, Boolean.TRUE);
+ return true;
+ }
+ else
+ {
+ facesContext.getAttributes().put(IS_RESOURCE_REQUEST, Boolean.FALSE);
+ return false;
+ }
+ }
+ }
+
+ protected String getLocalePrefixForLocateResource()
+ {
+ String localePrefix = null;
+ FacesContext context = FacesContext.getCurrentInstance();
+
+ String bundleName = context.getApplication().getMessageBundle();
+
+ if (null != bundleName)
+ {
+ Locale locale = context.getApplication().getViewHandler()
+ .calculateLocale(context);
+
+ ResourceBundle bundle = ResourceBundle
+ .getBundle(bundleName, locale, ClassLoaderUtils.getDefaultClassLoader());
+
+ if (bundle != null)
+ {
+ try
+ {
+ localePrefix = bundle.getString(ResourceHandler.LOCALE_PREFIX);
+ }
+ catch (MissingResourceException e)
+ {
+ // Ignore it and return null
+ }
+ }
+ }
+ return localePrefix;
+ }
+
+ private static ResourceBundle getBundle(FacesContext facesContext, Locale locale, String bundleName)
+ {
+ try
+ {
+ // First we try the JSF implementation class loader
+ return ResourceBundle.getBundle(bundleName, locale, facesContext.getClass().getClassLoader());
+ }
+ catch (MissingResourceException ignore1)
+ {
+ try
+ {
+ // Next we try the JSF API class loader
+ return ResourceBundle.getBundle(bundleName, locale, ResourceHandlerImpl.class.getClassLoader());
+ }
+ catch (MissingResourceException ignore2)
+ {
+ try
+ {
+ // Last resort is the context class loader
+ return ResourceBundle.getBundle(bundleName, locale, ClassLoaderUtils.getDefaultClassLoader());
+ }
+ catch (MissingResourceException damned)
+ {
+ return null;
+ }
+ }
+ }
+ }
+
+ protected boolean isResourceIdentifierExcluded(FacesContext context,
+ String resourceIdentifier)
+ {
+ String value = context.getExternalContext().getInitParameter(
+ RESOURCE_EXCLUDES_PARAM_NAME);
+ if (value == null)
+ {
+ value = RESOURCE_EXCLUDES_DEFAULT_VALUE;
+ }
+ //TODO: optimize this code
+ String[] extensions = value.split("\\s+");
+ for (int i = 0; i < extensions.length; i++)
+ {
+ if (resourceIdentifier.endsWith(extensions[i]))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if a library exists or not. This is done delegating
+ * to each ResourceLoader used, because each one has a different
+ * prefix and way to load resources.
+ *
+ */
+ @Override
+ public boolean libraryExists(String libraryName)
+ {
+ String localePrefix = getLocalePrefixForLocateResource();
+
+ String pathToLib = null;
+
+ if (localePrefix != null)
+ {
+ //Check with locale
+ pathToLib = localePrefix + '/' + libraryName;
+
+ for (ResourceLoader loader : getResourceHandlerSupport()
+ .getResourceLoaders())
+ {
+ if (loader.libraryExists(pathToLib))
+ {
+ return true;
+ }
+ }
+ }
+
+ //Check without locale
+ for (ResourceLoader loader : getResourceHandlerSupport()
+ .getResourceLoaders())
+ {
+ if (loader.libraryExists(libraryName))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param resourceHandlerSupport
+ * the resourceHandlerSupport to set
+ */
+ public void setResourceHandlerSupport(
+ ResourceHandlerSupport resourceHandlerSupport)
+ {
+ _resourceHandlerSupport = resourceHandlerSupport;
+ }
+
+ /**
+ * @return the resourceHandlerSupport
+ */
+ protected abstract ResourceHandlerSupport getResourceHandlerSupport();
+
+ private ResourceHandlerCache getResourceLoaderCache()
+ {
+ if (_resourceHandlerCache == null)
+ _resourceHandlerCache = new ResourceHandlerCache();
+ return _resourceHandlerCache;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerSupport.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerSupport.java
new file mode 100644
index 0000000..1ee9365
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerSupport.java
@@ -0,0 +1,86 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+import javax.faces.context.FacesContext;
+
+/**
+ * A utility class to isolate a ResourceHandler implementation from its
+ * underlying implementation
+ *
+ * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
+ * @version $Revision: 891494 $ $Date: 2009-12-16 19:42:18 -0500 (Mié, 16 Dic 2009) $
+ */
+public abstract class ResourceHandlerSupport
+{
+
+ /**
+ * Calculate the resource base path.
+ *
+ * It should extract a string like:
+ *
+ * ResourceHandler.RESOURCE_IDENTIFIER + '/' + getResourceName()
+ *
+ * For example:
+ *
+ * /javax.faces.resource/image.jpg
+ *
+ * This is used on ResourceHandler.handleResourceRequest()
+ *
+ */
+ public abstract String calculateResourceBasePath(FacesContext facesContext);
+
+ /**
+ * Return an array of resource loaders used to find resources
+ * using the standard. The order of ResourceLoaders define
+ * its precedence.
+ *
+ * @return
+ */
+ public abstract ResourceLoader[] getResourceLoaders();
+
+ /**
+ * Check if the mapping used is done using extensions (.xhtml, .jsf)
+ * or if it is not (/faces/*)
+ * @return
+ */
+ public abstract boolean isExtensionMapping();
+
+ /**
+ * Get the mapping used as prefix(/faces) or sufix(.jsf)
+ *
+ * @return
+ */
+ public abstract String getMapping();
+
+ /**
+ * Return the time when the app started. This is useful to set the
+ * "Last-Modified" header in some specific cases.
+ *
+ * @return
+ */
+ public abstract long getStartupTime();
+
+ /**
+ * Return the time that should be set on "Expires" header in a resource.
+ *
+ * @return
+ */
+ public abstract long getMaxTimeExpires();
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceImpl.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceImpl.java
new file mode 100644
index 0000000..ca25f89
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceImpl.java
@@ -0,0 +1,312 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ValueExpression;
+import javax.faces.application.ProjectStage;
+import javax.faces.application.Resource;
+import javax.faces.application.ResourceHandler;
+import javax.faces.context.FacesContext;
+import javax.faces.event.ExceptionQueuedEvent;
+import javax.faces.event.ExceptionQueuedEventContext;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.net.URL;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Default implementation for resources
+ *
+ * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
+ * @version $Revision: 957739 $ $Date: 2010-06-24 17:27:40 -0500 (Jue, 24 Jun 2010) $
+ */
+public class ResourceImpl extends Resource {
+
+
+ private ResourceMeta _resourceMeta;
+ private ResourceLoader _resourceLoader;
+ private ResourceHandlerSupport _resourceHandlerSupport;
+
+ public ResourceImpl(ResourceMeta resourceMeta,
+ ResourceLoader resourceLoader, ResourceHandlerSupport support, String contentType) {
+ _resourceMeta = resourceMeta;
+ _resourceLoader = resourceLoader;
+ _resourceHandlerSupport = support;
+ setLibraryName(resourceMeta.getLibraryName());
+ setResourceName(resourceMeta.getResourceName());
+ setContentType(contentType);
+ }
+
+ public ResourceLoader getResourceLoader() {
+ return _resourceLoader;
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ if (couldResourceContainValueExpressions()) {
+ return new ValueExpressionFilterInputStream(
+ getResourceLoader().getResourceInputStream(_resourceMeta));
+ } else {
+ return getResourceLoader().getResourceInputStream(_resourceMeta);
+ }
+ }
+
+ private boolean couldResourceContainValueExpressions() {
+ if (_resourceMeta.couldResourceContainValueExpressions()) {
+ return true;
+ } else {
+ //By default only css resource contain value expressions
+ String contentType = getContentType();
+
+ return ("text/css".equals(contentType));
+ }
+ }
+
+ private class ValueExpressionFilterInputStream extends InputStream {
+ private PushbackInputStream delegate;
+
+ public ValueExpressionFilterInputStream(InputStream in) {
+ super();
+ delegate = new PushbackInputStream(in, 255);
+ }
+
+ @Override
+ public int read() throws IOException {
+ int c1 = delegate.read();
+
+ if (c1 == -1) return -1;
+
+ if (((char) c1) == '#') {
+ int c2 = delegate.read();
+ if (c2 == -1) return -1;
+ if (((char) c2) == '{') {
+ //It is a value expression. We need
+ //to look for a occurrence of } to
+ //extract the expression and evaluate it,
+ //the result should be unread.
+ List<Integer> expressionList = new ArrayList<Integer>();
+ int c3 = delegate.read();
+ while (c3 != -1 && ((char) c3) != '}') {
+ expressionList.add(c3);
+ c3 = delegate.read();
+ }
+
+ if (c3 == -1) {
+ //get back the data, because we can't
+ //extract any value expression
+ for (int i = 0; i < expressionList.size(); i++) {
+ delegate.unread(expressionList.get(i));
+ }
+ delegate.unread(c2);
+ return c1;
+ } else {
+ //EL expression found. Evaluate it and pushback
+ //the result into the stream
+ FacesContext context = FacesContext.getCurrentInstance();
+ ELContext elContext = context.getELContext();
+ try {
+ ValueExpression ve = context.getApplication().
+ getExpressionFactory().createValueExpression(
+ elContext,
+ "#{" + convertToExpression(expressionList) + "}",
+ String.class);
+ String value = (String) ve.getValue(elContext);
+
+ for (int i = value.length() - 1; i >= 0; i--) {
+ delegate.unread((int) value.charAt(i));
+ }
+ } catch (ELException e) {
+ ExceptionQueuedEventContext equecontext = new ExceptionQueuedEventContext(context, e, null);
+ context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, equecontext);
+
+ Logger log = Logger.getLogger(ResourceImpl.class.getName());
+ if (log.isLoggable(Level.SEVERE))
+ log.severe("Cannot evaluate EL expression " + convertToExpression(expressionList) + " in resource " + getLibraryName() + ":" + getResourceName());
+
+ delegate.unread(c3);
+ for (int i = expressionList.size() - 1; i >= 0; i--) {
+ delegate.unread(expressionList.get(i));
+ }
+ delegate.unread(c2);
+ return c1;
+ }
+
+ //read again
+ return delegate.read();
+ }
+ } else {
+ delegate.unread(c2);
+ return c1;
+ }
+ } else {
+ //just continue
+ return c1;
+ }
+ }
+
+ private String convertToExpression(List<Integer> expressionList) {
+ char[] exprArray = new char[expressionList.size()];
+
+ for (int i = 0; i < expressionList.size(); i++) {
+ exprArray[i] = (char) expressionList.get(i).intValue();
+ }
+ return String.valueOf(exprArray);
+ }
+ }
+
+ @Override
+ public String getRequestPath() {
+ String path;
+ if (_resourceHandlerSupport.isExtensionMapping()) {
+ path = ResourceHandler.RESOURCE_IDENTIFIER + '/' +
+ getResourceName() + _resourceHandlerSupport.getMapping();
+ } else {
+ String mapping = _resourceHandlerSupport.getMapping();
+ path = ResourceHandler.RESOURCE_IDENTIFIER + '/' + getResourceName();
+ path = (mapping == null) ? path : mapping + path;
+ }
+
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ String metadata = null;
+ boolean useAmp = false;
+ if (getLibraryName() != null) {
+ metadata = "?ln=" + getLibraryName();
+ path = path + metadata;
+ useAmp = true;
+
+ if (!facesContext.isProjectStage(ProjectStage.Production)
+ && ResourceLoaderUtils.JSF_JS_RESOURCE_NAME.equals(getResourceName())
+ && ResourceLoaderUtils.JAVAX_FACES_LIBRARY_NAME.equals(getLibraryName())) {
+ // append &stage=?? for all ProjectStages except Production
+ path = path + "&stage=" + facesContext.getApplication().getProjectStage().toString();
+ }
+ }
+
+ return facesContext.getApplication().getViewHandler().getResourceURL(facesContext, path);
+ }
+
+ @Override
+ public Map<String, String> getResponseHeaders() {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+
+ if (facesContext.getApplication().getResourceHandler().isResourceRequest(facesContext)) {
+ Map<String, String> headers = new HashMap<String, String>();
+
+ long lastModified;
+ try {
+ lastModified = ResourceLoaderUtils.getResourceLastModified(this.getURL());
+ } catch (IOException e) {
+ lastModified = -1;
+ }
+
+ // Here we have two cases: If the file could contain EL Expressions
+ // the last modified time is the greatest value between application startup and
+ // the value from file.
+ if (this.couldResourceContainValueExpressions() &&
+ lastModified < _resourceHandlerSupport.getStartupTime()) {
+ lastModified = _resourceHandlerSupport.getStartupTime();
+ } else if (_resourceMeta instanceof AliasResourceMetaImpl &&
+ lastModified < _resourceHandlerSupport.getStartupTime()) {
+ // If the resource meta is aliased, the last modified time is the greatest
+ // value between application startup and the value from file.
+ lastModified = _resourceHandlerSupport.getStartupTime();
+ }
+
+ if (lastModified >= 0) {
+ headers.put("Last-Modified", ResourceLoaderUtils.formatDateHeader(lastModified));
+
+ long expires;
+ if (facesContext.isProjectStage(ProjectStage.Development)) {
+ // Force to expire now to prevent caching on development time.
+ expires = System.currentTimeMillis();
+ } else {
+ expires = System.currentTimeMillis() + _resourceHandlerSupport.getMaxTimeExpires();
+ }
+ headers.put("Expires", ResourceLoaderUtils.formatDateHeader(expires));
+ }
+
+ return headers;
+ } else {
+ //No need to return headers
+ return Collections.emptyMap();
+ }
+ }
+
+ @Override
+ public URL getURL() {
+ return getResourceLoader().getResourceURL(_resourceMeta);
+ }
+
+ @Override
+ public boolean userAgentNeedsUpdate(FacesContext context) {
+ // RFC2616 says related to If-Modified-Since header the following:
+ //
+ // "... The If-Modified-Since request-header field is used with a method to
+ // make it conditional: if the requested variant has not been modified since
+ // the time specified in this field, an entity will not be returned from
+ // the server; instead, a 304 (not modified) response will be returned
+ // without any message-body..."
+ //
+ // This method is called from ResourceHandlerImpl.handleResourceRequest and if
+ // returns false send a 304 Not Modified response.
+
+ String ifModifiedSinceString = context.getExternalContext().getRequestHeaderMap().get("If-Modified-Since");
+
+ if (ifModifiedSinceString == null) {
+ return true;
+ }
+
+ Long ifModifiedSince = ResourceLoaderUtils.parseDateHeader(ifModifiedSinceString);
+
+ if (ifModifiedSince == null) {
+ return true;
+ }
+
+ Long lastModified;
+ try {
+ lastModified = ResourceLoaderUtils.getResourceLastModified(this.getURL());
+ } catch (IOException exception) {
+ lastModified = -1L;
+ }
+
+ if (lastModified >= 0) {
+ if (this.couldResourceContainValueExpressions() &&
+ lastModified < _resourceHandlerSupport.getStartupTime()) {
+ lastModified = _resourceHandlerSupport.getStartupTime();
+ }
+
+ // If the lastModified date is lower or equal than ifModifiedSince,
+ // the agent does not need to update.
+ // Note the lastModified time is set at milisecond precision, but when
+ // the date is parsed and sent on ifModifiedSince, the exceding miliseconds
+ // are trimmed. So, we have to compare trimming this from the calculated
+ // lastModified time.
+ if ((lastModified - (lastModified % 1000)) <= ifModifiedSince) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceLoader.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceLoader.java
new file mode 100644
index 0000000..b7c75ce
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceLoader.java
@@ -0,0 +1,155 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Comparator;
+
+/**
+ * Base class for resource loaders. Resource loaders can lookup resources
+ * as URLs from arbitrary locations, including JAR files.
+ *
+ * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
+ * @version $Revision: 882702 $ $Date: 2009-11-20 15:16:07 -0500 (Vie, 20 Nov 2009) $
+ */
+public abstract class ResourceLoader
+{
+
+ public static final String VERSION_INVALID = "INVALID";
+
+ private String _prefix;
+
+ public ResourceLoader(String prefix)
+ {
+ _prefix = prefix;
+ }
+
+ public abstract String getResourceVersion(String path);
+
+ /**
+ * Return the max available version found (if exists) or
+ * return null if no version available.
+ */
+ public abstract String getLibraryVersion(String path);
+
+ /**
+ * Return the max available version found (if exists) or
+ * return null if no version available.
+ */
+ public abstract URL getResourceURL(ResourceMeta resourceMeta);
+
+ public abstract InputStream getResourceInputStream(ResourceMeta resourceMeta);
+
+ public abstract ResourceMeta createResourceMeta(String prefix, String libraryName, String libraryVersion,
+ String resourceName, String resourceVersion);
+
+ public abstract boolean libraryExists(String libraryName);
+
+ private Comparator<String> _versionComparator = null;
+
+ protected Comparator<String> getVersionComparator()
+ {
+ if (_versionComparator == null)
+ {
+ _versionComparator = new VersionComparator();
+ }
+ return _versionComparator;
+ }
+
+ protected void setVersionComparator(Comparator<String> versionComparator)
+ {
+ _versionComparator = versionComparator;
+ }
+
+ public class VersionComparator implements Comparator<String>
+ {
+
+ public int compare(String s1, String s2)
+ {
+ int n1 = 0;
+ int n2 = 0;
+ String o1 = s1;
+ String o2 = s2;
+
+ boolean p1 = true;
+ boolean p2 = true;
+
+ while (n1 == n2 && (p1 || p2))
+ {
+ int i1 = o1.indexOf('_');
+ int i2 = o2.indexOf('_');
+ if (i1 < 0)
+ {
+ if (o1.length() > 0)
+ {
+ p1 = false;
+ n1 = Integer.valueOf(o1);
+ o1 = "";
+ }
+ else
+ {
+ p1 = false;
+ n1 = 0;
+ }
+ }
+ else
+ {
+ n1 = Integer.valueOf(o1.substring(0, i1));
+ o1 = o1.substring(i1 + 1);
+ }
+ if (i2 < 0)
+ {
+ if (o2.length() > 0)
+ {
+ p2 = false;
+ n2 = Integer.valueOf(o2);
+ o2 = "";
+ }
+ else
+ {
+ p2 = false;
+ n2 = 0;
+ }
+ }
+ else
+ {
+ n2 = Integer.valueOf(o2.substring(0, i2));
+ o2 = o2.substring(i2 + 1);
+ }
+ }
+
+ if (n1 == n2)
+ {
+ return s1.length() - s2.length();
+ }
+ return n1 - n2;
+ }
+ }
+
+ public String getPrefix()
+ {
+ return _prefix;
+ }
+
+ public void setPrefix(String prefix)
+ {
+ _prefix = prefix;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceLoaderUtils.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceLoaderUtils.java
new file mode 100644
index 0000000..2c21bc4
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceLoaderUtils.java
@@ -0,0 +1,125 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class ResourceLoaderUtils {
+
+ public final static String JAVAX_FACES_LIBRARY_NAME = "javax.faces";
+ public final static String JSF_JS_RESOURCE_NAME = "jsf.js";
+
+ public final static String MYFACES_JS_RESOURCE_NAME = "oamSubmit.js";
+ public final static String MYFACES_JS_RESOURCE_NAME_UNCOMPRESSED = "oamSubmit-uncompressed.js";
+
+ public final static String MYFACES_LIBRARY_NAME = "org.apache.myfaces";
+ private final static String RENDERED_MYFACES_JS = "org.apache.myfaces.RENDERED_MYFACES_JS";
+
+ // TODO: In tomcat and jetty it is implemented a Flyweight pattern when converting
+ // date headers. For now it is better keep this stuff simple.
+ private static final String HTTP_RESPONSE_DATE_HEADER =
+ "EEE, dd MMM yyyy HH:mm:ss zzz";
+
+ private static final String[] HTTP_REQUEST_DATE_HEADER = {
+ "EEE, dd MMM yyyy HH:mm:ss zzz", "EEEEEE, dd-MMM-yy HH:mm:ss zzz",
+ "EEE MMMM d HH:mm:ss yyyy"};
+
+ private static TimeZone __GMT = TimeZone.getTimeZone("GMT");
+
+ public static String formatDateHeader(long value) {
+ SimpleDateFormat format = new SimpleDateFormat(
+ HTTP_RESPONSE_DATE_HEADER,
+ Locale.US);
+ format.setTimeZone(__GMT);
+ return format.format(new Date(value));
+ }
+
+ public static Long parseDateHeader(String value) {
+ Date date = null;
+ for (int i = 0; (date == null) && (i < HTTP_REQUEST_DATE_HEADER.length); i++) {
+ try {
+ SimpleDateFormat format = new SimpleDateFormat(
+ HTTP_REQUEST_DATE_HEADER[i], Locale.US);
+ format.setTimeZone(__GMT);
+ date = format.parse(value);
+ } catch (ParseException e) {
+ ;
+ }
+ }
+ if (date == null) {
+ return null;
+ }
+ return new Long(date.getTime());
+ }
+
+ //Taken from trinidad URLUtils
+ public static long getResourceLastModified(URL url) throws IOException {
+ if ("file".equals(url.getProtocol())) {
+ String externalForm = url.toExternalForm();
+ // Remove the "file:"
+ File file = new File(externalForm.substring(5));
+
+ return file.lastModified();
+ } else {
+ return getResourceLastModified(url.openConnection());
+ }
+ }
+
+ //Taken from trinidad URLUtils
+ public static long getResourceLastModified(URLConnection connection) throws IOException {
+ long modified;
+ if (connection instanceof JarURLConnection) {
+ // The following hack is required to work-around a JDK bug.
+ // getLastModified() on a JAR entry URL delegates to the actual JAR file
+ // rather than the JAR entry.
+ // This opens internally, and does not close, an input stream to the JAR
+ // file.
+ // In turn, you cannot close it by yourself, because it's internal.
+ // The work-around is to get the modification date of the JAR file
+ // manually,
+ // and then close that connection again.
+
+ URL jarFileUrl = ((JarURLConnection) connection).getJarFileURL();
+ URLConnection jarFileConnection = jarFileUrl.openConnection();
+
+ try {
+ modified = jarFileConnection.getLastModified();
+ } finally {
+ try {
+ jarFileConnection.getInputStream().close();
+ } catch (Exception exception) {
+ // Ignored
+ }
+ }
+ } else {
+ modified = connection.getLastModified();
+ }
+
+ return modified;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceMeta.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceMeta.java
new file mode 100644
index 0000000..7033bb4
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceMeta.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+/**
+ * Contains the metadata information to reference a resource
+ *
+ * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
+ * @version $Revision: 946779 $ $Date: 2010-05-20 15:31:42 -0500 (Jue, 20 May 2010) $
+ */
+public abstract class ResourceMeta
+{
+
+ public abstract String getLibraryName();
+
+ public abstract String getResourceName();
+
+ public abstract String getLocalePrefix();
+
+ public abstract String getLibraryVersion();
+
+ public abstract String getResourceVersion();
+
+ public abstract String getResourceIdentifier();
+
+ public abstract boolean couldResourceContainValueExpressions();
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceMetaImpl.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceMetaImpl.java
new file mode 100644
index 0000000..03ce139
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceMetaImpl.java
@@ -0,0 +1,116 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+/**
+ * Contains the metadata information to reference a resource
+ *
+ * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
+ * @version $Revision: 700544 $ $Date: 2008-09-30 13:44:02 -0500 (Mar, 30 Sep 2008) $
+ */
+public class ResourceMetaImpl extends ResourceMeta
+{
+
+ private final String _prefix;
+ private final String _libraryName;
+ private final String _libraryVersion;
+ private final String _resourceName;
+ private final String _resourceVersion;
+
+ public ResourceMetaImpl(String prefix, String libraryName, String libraryVersion,
+ String resourceName, String resourceVersion)
+ {
+ _prefix = prefix;
+ _libraryName = libraryName;
+ _libraryVersion = libraryVersion;
+ _resourceName = resourceName;
+ _resourceVersion = resourceVersion;
+ }
+
+ public String getLibraryName()
+ {
+ return _libraryName;
+ }
+
+ public String getResourceName()
+ {
+ return _resourceName;
+ }
+
+ public String getLocalePrefix()
+ {
+ return _prefix;
+ }
+
+ public String getLibraryVersion()
+ {
+ return _libraryVersion;
+ }
+
+ public String getResourceVersion()
+ {
+ return _resourceVersion;
+ }
+
+ @Override
+ public String getResourceIdentifier()
+ {
+ StringBuilder builder = new StringBuilder();
+ boolean firstSlashAdded = false;
+ if (_prefix != null && _prefix.length() > 0)
+ {
+ builder.append(_prefix);
+ firstSlashAdded = true;
+ }
+ if (_libraryName != null)
+ {
+ if (firstSlashAdded) builder.append('/');
+ builder.append(_libraryName);
+ firstSlashAdded = true;
+ }
+ if (_libraryVersion != null)
+ {
+ if (firstSlashAdded) builder.append('/');
+ builder.append(_libraryVersion);
+ firstSlashAdded = true;
+ }
+ if (_resourceName != null)
+ {
+ if (firstSlashAdded) builder.append('/');
+ builder.append(_resourceName);
+ firstSlashAdded = true;
+ }
+ if (_resourceVersion != null)
+ {
+ if (firstSlashAdded) builder.append('/');
+ builder.append(_resourceVersion);
+ builder.append(
+ _resourceName.substring(_resourceName.lastIndexOf('.')));
+ firstSlashAdded = true;
+ }
+
+ return builder.toString();
+ }
+
+ @Override
+ public boolean couldResourceContainValueExpressions()
+ {
+ return false;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceHandler.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceHandler.java
new file mode 100644
index 0000000..1f03b61
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceHandler.java
@@ -0,0 +1,209 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.Decorated;
+
+import javax.faces.application.Resource;
+import javax.faces.application.ResourceHandler;
+import javax.faces.context.FacesContext;
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * A simple delegating resource handler
+ * which is supposed to pick up resources from a
+ * given location if the Weaving configuration
+ * has the parameter present
+ */
+public class SourceResourceHandler extends ResourceHandlerImpl implements Decorated
+{
+
+ ResourceHandler _delegate = null;
+ ResourceHandlerSupport _sourceHandlerSupport = null;
+
+ public SourceResourceHandler(ResourceHandler delegate) {
+ _delegate = delegate;
+ }
+
+ public Resource createResource(String resourceName) {
+ Resource retVal = super.createResource(resourceName);
+ if (retVal != null)
+ return retVal;
+
+ return _delegate.createResource(resourceName);
+ }
+
+ public Resource createResource(String resourceName, String libraryName) {
+ Resource retVal = super.createResource(resourceName, libraryName);
+ if (retVal != null)
+ return retVal;
+
+ return _delegate.createResource(resourceName, libraryName);
+ }
+
+ public Resource createResource(String resourceName, String libraryName, String contentType) {
+ Resource retVal = super.createResource(resourceName, libraryName, contentType);
+
+ if (retVal != null)
+ return retVal;
+
+ return _delegate.createResource(resourceName, libraryName, contentType);
+ }
+
+ public String getRendererTypeForResourceName(String resourceName) {
+
+ String retVal = super.getRendererTypeForResourceName(resourceName);
+ if (retVal != null)
+ return retVal;
+
+ return _delegate.getRendererTypeForResourceName(resourceName);
+ }
+
+ public void handleResourceRequest(FacesContext context) throws IOException {
+ super.handleResourceRequest(context);
+ }
+
+ public boolean isResourceRequest(FacesContext context) {
+ return _delegate.isResourceRequest(context);
+ }
+
+ public boolean libraryExists(String libraryName) {
+ return _delegate.libraryExists(libraryName);
+ }
+
+ public ResourceHandler getDelegate() {
+ return _delegate;
+ }
+
+ @Override
+ /**
+ * central override of this class it provides a new handler
+ * support class which allows source pickups after the
+ * JSF2 specified restful algorithms
+ *
+ * @return A support instance which allows also source pickups from resources additionally to the
+ * default deployment and jar pickups
+ */
+ protected ResourceHandlerSupport getResourceHandlerSupport() {
+ if (_sourceHandlerSupport == null) {
+ _sourceHandlerSupport = new SourceResourceHandlerSupport();
+ }
+ return _sourceHandlerSupport;
+ }
+
+ protected ResourceMeta deriveResourceMeta(ResourceLoader resourceLoader,
+ String resourceName, String libraryName) {
+ String localePrefix = getLocalePrefixForLocateResource();
+ String resourceVersion = null;
+ String libraryVersion = null;
+ ResourceMeta resourceId = null;
+
+ //1. Try to locate resource in a localized path
+ if (localePrefix != null) {
+ if (null != libraryName) {
+ String pathToLib = localePrefix + '/' + libraryName;
+ libraryVersion = resourceLoader.getLibraryVersion(pathToLib);
+
+ if (null != libraryVersion) {
+ String pathToResource = localePrefix + '/'
+ + libraryName + '/' + libraryVersion + '/'
+ + resourceName;
+ resourceVersion = resourceLoader
+ .getResourceVersion(pathToResource);
+ } else {
+ String pathToResource = localePrefix + '/'
+ + libraryName + '/' + resourceName;
+ resourceVersion = resourceLoader
+ .getResourceVersion(pathToResource);
+ }
+
+ if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion))) {
+ resourceId = resourceLoader.createResourceMeta(localePrefix, libraryName,
+ libraryVersion, resourceName, resourceVersion);
+ }
+ } else {
+ resourceVersion = resourceLoader
+ .getResourceVersion(localePrefix + '/' + resourceName);
+ if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion))) {
+ resourceId = resourceLoader.createResourceMeta(localePrefix, null, null,
+ resourceName, resourceVersion);
+ } else if (resourceVersion == ResourceLoader.VERSION_INVALID) {
+ resourceId = resourceLoader.createResourceMeta(null, libraryName,
+ null, resourceName, null);
+ }
+ }
+
+ if (resourceId != null) {
+ URL url = resourceLoader.getResourceURL(resourceId);
+ if (url == null) {
+ resourceId = null;
+ }
+ }
+ }
+
+ //2. Try to localize resource in a non localized path
+ if (resourceId == null) {
+ if (null != libraryName) {
+ libraryVersion = resourceLoader.getLibraryVersion(libraryName);
+
+ if (null != libraryVersion) {
+ String pathToResource = (libraryName + '/' + libraryVersion
+ + '/' + resourceName);
+ resourceVersion = resourceLoader
+ .getResourceVersion(pathToResource);
+ } else {
+ String pathToResource = (libraryName + '/'
+ + resourceName);
+ resourceVersion = resourceLoader
+ .getResourceVersion(pathToResource);
+ }
+
+ if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion))) {
+ resourceId = resourceLoader.createResourceMeta(null, libraryName,
+ libraryVersion, resourceName, resourceVersion);
+ } else if (resourceVersion == ResourceLoader.VERSION_INVALID) {
+ resourceId = resourceLoader.createResourceMeta(null, libraryName,
+ libraryVersion, resourceName, null);
+ }
+ } else {
+ resourceVersion = resourceLoader
+ .getResourceVersion(resourceName);
+
+ if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion))) {
+ resourceId = resourceLoader.createResourceMeta(null, null, null,
+ resourceName, resourceVersion);
+ } else if (resourceVersion == ResourceLoader.VERSION_INVALID) {
+ resourceId = resourceLoader.createResourceMeta(null, null,
+ null, resourceName, null);
+ }
+ }
+
+ if (resourceId != null) {
+ URL url = resourceLoader.getResourceURL(resourceId);
+ if (url == null) {
+ resourceId = null;
+ }
+ }
+ }
+
+ return resourceId;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceHandlerSupport.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceHandlerSupport.java
new file mode 100644
index 0000000..7b2c90f
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceHandlerSupport.java
@@ -0,0 +1,82 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+import javax.faces.context.FacesContext;
+
+/**
+ * impl specific handler support which attaches
+ * the source resource loader upfront before
+ * checking the other ones
+ * <p/>
+ * a delegate is used to limit the binding between
+ * the "parent" class and the child class
+ * in this case it simply makes sense to avoid any internal sideeffects
+ * between getResourceLoaders and the other methods
+ */
+public class SourceResourceHandlerSupport extends ResourceHandlerSupport {
+
+ private ResourceLoader[] _supportResourceLoaders;
+ private BaseResourceHandlerSupport _defaultSupport = new BaseResourceHandlerSupport();
+
+ @Override
+ /**
+ * Delivers a list of resource loaders in a binding order
+ * of the resource lookup algorithms
+ *
+ * @return a list of resource loaders with following order,
+ * <ul>
+ * <li>source lookup paths if present</li>
+ * <li>/resources directory</li>
+ * <li>META-INF/resources directory</li>
+ * </ul>
+ */
+ public ResourceLoader[] getResourceLoaders() {
+ if (_supportResourceLoaders == null) {
+ //The ExternalContextResourceLoader has precedence over
+ //ClassLoaderResourceLoader, so it goes first.
+ _supportResourceLoaders = new ResourceLoader[]{
+ new SourceResourceLoader("/resources"),
+ new ExternalContextResourceLoader("/resources"),
+ new ClassLoaderResourceLoader("META-INF/resources")
+ };
+ }
+ return _supportResourceLoaders;
+ }
+
+ public String calculateResourceBasePath(FacesContext facesContext) {
+ return _defaultSupport.calculateResourceBasePath(facesContext);
+ }
+
+ public boolean isExtensionMapping() {
+ return _defaultSupport.isExtensionMapping();
+ }
+
+ public String getMapping() {
+ return _defaultSupport.getMapping();
+ }
+
+ public long getStartupTime() {
+ return _defaultSupport.getStartupTime();
+ }
+
+ public long getMaxTimeExpires() {
+ return _defaultSupport.getMaxTimeExpires();
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceLoader.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceLoader.java
new file mode 100644
index 0000000..3f6e947
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceLoader.java
@@ -0,0 +1,104 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.resources;
+
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * internal resource loader to be used with our custom resource handler
+ * the resource loader is added to the list of available loaders
+ * so that the resource gets loaded properly from our source path
+ * instead of the web context if present, the source paths as usual
+ * are picked up by our context params.
+ */
+public class SourceResourceLoader extends ExternalContextResourceLoader {
+
+ public SourceResourceLoader(String prefix) {
+ super(prefix);
+ }
+
+ @Override
+ protected Set<String> getResourcePaths(String path) {
+ List<String> resourceRoots = WeavingContext.getInstance().getConfiguration().getResourceDirs();
+ if (resourceRoots == null || resourceRoots.isEmpty()) {
+ return Collections.EMPTY_SET;
+ }
+ Set<String> retVals = new HashSet<String>(resourceRoots.size());
+ //for (String resourceRoot : resourceRoots) {
+ retVals.add(getPrefix() + "/" + path);
+ //}
+ return retVals;
+ }
+
+ @Override
+ public URL getResourceURL(ResourceMeta resourceMeta) {
+ try {
+ List<String> resourceRoots = WeavingContext.getInstance().getConfiguration().getResourceDirs();
+ if (resourceRoots == null || resourceRoots.isEmpty()) {
+ return super.getResourceURL(resourceMeta);
+ }
+
+ for (String resourceRoot : resourceRoots) {
+ File resourceFile = new File(resourceRoot + getPrefix() + "/" + resourceMeta.getLibraryName() + "/"+ resourceMeta.getResourceName());
+ if (resourceFile.exists()) {
+ return resourceFile.toURI().toURL();
+ }
+ }
+
+ return super.getResourceURL(resourceMeta);
+ }
+ catch (MalformedURLException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public InputStream getResourceInputStream(ResourceMeta resourceMeta) {
+ try {
+ List<String> resourceRoots = WeavingContext.getInstance().getConfiguration().getResourceDirs();
+ if (resourceRoots == null || resourceRoots.isEmpty()) {
+ return super.getResourceInputStream(resourceMeta);
+ }
+
+ for (String resourceRoot : resourceRoots) {
+ File resourceFile = new File(resourceRoot + getPrefix() + "/" +resourceMeta.getLibraryName() + "/"+ resourceMeta.getResourceName());
+ if (resourceFile.exists()) {
+ return new FileInputStream(resourceFile);
+ }
+ }
+
+ return super.getResourceInputStream(resourceMeta);
+ }
+ catch (IOException e) {
+ return null;
+ }
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/startup/RefreshPhaseListener.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/startup/RefreshPhaseListener.java
new file mode 100644
index 0000000..5ab9b57
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/startup/RefreshPhaseListener.java
@@ -0,0 +1,62 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.startup;
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+
+import javax.faces.context.FacesContext;
+import javax.faces.event.PhaseEvent;
+import javax.faces.event.PhaseId;
+import javax.faces.event.PhaseListener;
+import java.util.Map;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ *
+ * This phase listener is needed because the annotation scanner
+ * relies on the facesContext to be present, we cannot do without it
+ */
+public class RefreshPhaseListener implements PhaseListener
+{
+
+ @Override
+ public void afterPhase(PhaseEvent event)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void beforePhase(PhaseEvent event)
+ {
+ FacesContext context = event.getFacesContext();
+ Map<Object, Object> params = context.getAttributes();
+ if(params.containsKey("ANN_PROCESSED")) return;
+ else params.put("ANN_PROCESSED", Boolean.TRUE);
+ WeavingContext.getInstance().annotationScan();
+ WeavingContext.getInstance().getImplementationSPI().refreshManagedBeans();
+ }
+
+ @Override
+ public PhaseId getPhaseId()
+ {
+ return PhaseId.ANY_PHASE; //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/startup/StartupServletContextPluginChainLoader.java b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/startup/StartupServletContextPluginChainLoader.java
new file mode 100644
index 0000000..bb2a512
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/org/apache/myfaces/extensions/scripting/jsf/startup/StartupServletContextPluginChainLoader.java
@@ -0,0 +1,96 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.jsf.startup;
+
+import org.apache.myfaces.webapp.StartupListener;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.monitor.ResourceMonitor;
+import org.apache.myfaces.extensions.scripting.jsf.adapters.MyFacesSPI;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * serves the startup process
+ * the chainloader hooks itself into the myfaces init process
+ * and is responsible for startup of the daemon the
+ * initial scan and compile
+ */
+
+public class StartupServletContextPluginChainLoader implements StartupListener
+{
+ final Logger _log = Logger.getLogger(this.getClass().getName());
+
+ public void preInit(ServletContextEvent servletContextEvent)
+ {
+ ServletContext servletContext = servletContextEvent.getServletContext();
+ try
+ {
+ WeavingContext context = WeavingContext.getInstance();
+ _log.info("[EXT-SCRIPTING] Instantiating StartupServletContextPluginChainLoader");
+ context.initEngines();
+ _log.info("[EXT-SCRIPTING] Loading configuration");
+ context.getConfiguration().init(servletContext);
+ _log.info("[EXT-SCRIPTING] Loading Scripting end");
+ _log.info("[EXT-SCRIPTING] initializing startup daemon");
+ ResourceMonitor.init(servletContext);
+ _log.info("[EXT-SCRIPTING] initializing startup daemon end");
+ _log.info("[EXT-SCRIPTING] Initial Scan and compile");
+ //the initial scan should happen synchronsously
+ ResourceMonitor.getInstance().performMonitoringTask();
+ _log.info("[EXT-SCRIPTING] Starting Change Monitor");
+ ResourceMonitor.getInstance().start();
+ _log.info("[EXT-SCRIPTING] Startup done");
+ _log.info("[EXT-SCRIPTING] init the chain loader for class loading");
+ //TODO make this more generic depending on the implementation
+ MyFacesSPI.getInstance().registerClassloadingExtension(servletContext);
+ _log.info("[EXT-SCRIPTING] registering the JSF Implementation");
+ WeavingContext.getInstance().setImplementation(MyFacesSPI.getInstance());
+ }
+ catch (IOException e)
+ {
+ _log.severe("[EXT-SCRIPTING] Engine startup failed terminating ext-scripting");
+ }
+
+ }
+
+ public void postInit(ServletContextEvent evt)
+ {
+ //tell the system that the startup phase is done
+ WeavingContext.getInstance().markPostInit();
+ }
+
+ public void preDestroy(ServletContextEvent evt)
+ {
+
+ }
+
+ public void postDestroy(ServletContextEvent evt)
+ {
+ //context is destroyed we have to shut down our daemon as well, by giving it
+ //a hint to shutdown
+ ResourceMonitor.getInstance().setRunning(false);
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/context/InitialFullScanTest.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/context/InitialFullScanTest.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/context/InitialFullScanTest.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/context/InitialFullScanTest.java
diff --git a/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/context/TaintHistoryTest.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/context/TaintHistoryTest.java
new file mode 100644
index 0000000..1f0e6d3
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/context/TaintHistoryTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.scanningcore.context;
+
+import org.apache.myfaces.extensions.scripting.core.api.ScriptingConst;
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.monitor.ClassResource;
+import org.apache.myfaces.extensions.scripting.jsf.components.RendererConst;
+import org.apache.myfaces.extensions.scripting.jsf.components.TaintHistory;
+import org.apache.myfaces.extensions.scripting.jsf.components.TaintHistoryRenderer;
+import org.apache.myfaces.extensions.scripting.scanningcore.support.ContextUtils;
+import org.apache.myfaces.renderkit.html.HtmlFormRenderer;
+import org.apache.myfaces.test.base.AbstractJsfTestCase;
+import org.apache.myfaces.test.mock.MockRenderKitFactory;
+import org.apache.myfaces.test.mock.MockResponseWriter;
+
+import javax.faces.component.html.HtmlForm;
+import javax.servlet.ServletContext;
+import java.io.File;
+import java.io.StringWriter;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * <p/>
+ * Taint history test, which is the first JSF related test
+ * we try to test here a simple mechanism, the taint history under
+ * JSF Testing conditions.
+ */
+
+public class TaintHistoryTest extends AbstractJsfTestCase
+{
+
+ ServletContext context;
+ private TaintHistory _taintHistory;
+ private TaintHistoryRenderer _taintHistoryRenderer;
+ private HtmlForm _form;
+ private MockResponseWriter _writer;
+ private static final String VAL_FILTER = "bla";
+ private static final String NO_TAINT_HISTORY_FOUND = "no taint history found";
+ private static final String BOOGA_JAVA = "booga.java";
+ private static final String TAINT_HISTORY_FOUND = "taint history found";
+ private static final String ATTR_NO_ENTRIES = "noEntries";
+ private static final String ATTR_FILTER = "filter";
+
+ public TaintHistoryTest(String name)
+ {
+ super(TaintHistoryTest.class.getName());
+ }
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ context = ContextUtils.startupSystem();
+ _writer = new MockResponseWriter(new StringWriter(), null, null);
+
+ facesContext.setResponseWriter(_writer);
+ _taintHistory = new TaintHistory();
+ _form = new HtmlForm();
+ _taintHistory.setParent(_form);
+
+ facesContext.getViewRoot().setRenderKitId(MockRenderKitFactory.HTML_BASIC_RENDER_KIT);
+ facesContext.getRenderKit().addRenderer(
+ _taintHistory.getFamily(),
+ _taintHistory.getRendererType(),
+ new TaintHistoryRenderer());
+ facesContext.getRenderKit().addRenderer(
+ _form.getFamily(),
+ _form.getRendererType(),
+ new HtmlFormRenderer());
+ }
+
+ public void testTaintHistory() throws Exception {
+ ClassResource historyEntry = new ClassResource();
+ historyEntry.setAClass(this.getClass());
+ //historyEntry.getRefreshAttribute().requestRefresh();
+ //historyEntry.getRefreshAttribute().executedRefresh();
+ historyEntry.setScriptingEngine(ScriptingConst.ENGINE_TYPE_JSF_JAVA);
+ historyEntry.setFile(new File(BOOGA_JAVA));
+ // historyEntry.getRefreshAttribute().requestRefresh();
+ historyEntry.setTainted(true);
+
+ //WeavingContext.getInstance().addTaintLogEntry(historyEntry);
+ //WeavingContext.getRefreshContext().addTaintLogEntry(historyEntry);
+
+ _taintHistory.encodeAll(facesContext);
+ facesContext.renderResponse();
+ assertFalse(TAINT_HISTORY_FOUND, _writer.getWriter().toString().contains(NO_TAINT_HISTORY_FOUND));
+
+ assertTrue(_writer.getWriter().toString().contains(BOOGA_JAVA));
+ }
+
+ public void testSaveRestore() {
+ _taintHistory.setFilter(VAL_FILTER);
+ _taintHistory.setNoEntries(10);
+ Object state = _taintHistory.saveState(facesContext);
+ _taintHistory.setFilter("");
+ _taintHistory.setNoEntries(0);
+ _taintHistory.restoreState(facesContext, state);
+
+ assertTrue(_taintHistory.getFilter().equals(VAL_FILTER));
+
+ assertTrue(_taintHistory.getNoEntries().equals(10));
+
+ }
+
+ public void testNoEntries() throws Exception {
+ int noEntries = 10;
+ for (int cnt = 0; cnt < 100; cnt++) {
+ ClassResource historyEntry = new ClassResource();
+ historyEntry.setAClass(this.getClass());
+ //historyEntry.getRefreshAttribute().executedRefresh();
+ historyEntry.setScriptingEngine(ScriptingConst.ENGINE_TYPE_JSF_JAVA);
+ if(cnt < 10)
+ historyEntry.setFile(new File("0"+cnt + "_"+BOOGA_JAVA));
+ else
+ historyEntry.setFile(new File(cnt + "_"+BOOGA_JAVA));
+ //historyEntry.getRefreshAttribute().requestRefresh();
+ //historyEntry.setTaintedOnce(true);
+ WeavingContext.getInstance().addTaintLogEntry(historyEntry);
+ }
+
+ _taintHistory.setNoEntries(10);
+ _taintHistory.encodeAll(facesContext);
+ facesContext.renderResponse();
+
+ assertTrue(_writer.getWriter().toString().contains("99_"+BOOGA_JAVA));
+ assertFalse(_writer.getWriter().toString().contains("89_"+BOOGA_JAVA));
+ assertFalse(_writer.getWriter().toString().contains("00_"+BOOGA_JAVA));
+ }
+
+ public void testElAttributes() {
+ assertTrue(_taintHistory.getFilter() == null);
+ assertTrue(_taintHistory.getNoEntries().equals(TaintHistory.DEFAULT_NO_ENTRIES));
+ _taintHistory.setNoEntries(null);
+
+ _taintHistory.getAttributes().put(ATTR_NO_ENTRIES, 20);
+ _taintHistory.getAttributes().put(ATTR_FILTER, VAL_FILTER);
+
+ assertTrue(_taintHistory.getNoEntries() == 20);
+ assertTrue(_taintHistory.getFilter().equals(VAL_FILTER));
+ }
+
+
+}
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/engine/EngineFactoryTest.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/engine/EngineFactoryTest.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/engine/EngineFactoryTest.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/engine/EngineFactoryTest.java
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/CompilationExceptionTest.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/CompilationExceptionTest.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/CompilationExceptionTest.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/CompilationExceptionTest.java
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/CompilationResultTest.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/CompilationResultTest.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/CompilationResultTest.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/CompilationResultTest.java
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/GroovyCompilerTest.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/GroovyCompilerTest.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/GroovyCompilerTest.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/GroovyCompilerTest.java
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/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/JavaCompilerTest.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/JavaCompilerTest.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/engine/compiler/JavaCompilerTest.java
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/probes/MethodReloadingProbe.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/probes/MethodReloadingProbe.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/probes/MethodReloadingProbe.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/probes/MethodReloadingProbe.java
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/probes/Probe.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/probes/Probe.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/probes/Probe.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/probes/Probe.java
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/probes/Probe2.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/probes/Probe2.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/probes/Probe2.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/probes/Probe2.java
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/support/AbstractGeneratorTestCase.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/support/AbstractGeneratorTestCase.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/support/AbstractGeneratorTestCase.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/support/AbstractGeneratorTestCase.java
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/support/Consts.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/support/Consts.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/support/Consts.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/support/Consts.java
diff --git a/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/support/ContextUtils.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/support/ContextUtils.java
new file mode 100644
index 0000000..358bed5
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/support/ContextUtils.java
@@ -0,0 +1,62 @@
+/*
+ * 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 org.apache.myfaces.extensions.scripting.scanningcore.support;
+
+import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
+import org.apache.myfaces.extensions.scripting.core.monitor.ResourceMonitor;
+import org.apache.myfaces.extensions.scripting.scanningcore.support.MockServletContext;
+
+import java.io.IOException;
+
+/**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class ContextUtils
+{
+ /**
+ * A startup routine shared by many tests
+ * to do the basic weaving initialization
+ *
+ * @return the mockup servlet context
+ */
+ public static MockServletContext startupSystem() {
+ MockServletContext context = new org.apache.myfaces.extensions.scripting.scanningcore.support.MockServletContext();
+
+ WeavingContext wcontext = WeavingContext.getInstance();
+ try
+ {
+ wcontext.initEngines();
+ wcontext.getConfiguration().init(context);
+ ResourceMonitor.init(context);
+ //TODO ??
+ ResourceMonitor.getInstance().performMonitoringTask();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+
+
+ //WeavingContextInitializer.initWeavingContext(context);
+ return context;
+ }
+}
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/support/LoggingHandler.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/support/LoggingHandler.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/support/LoggingHandler.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/support/LoggingHandler.java
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/support/MockServletContext.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/support/MockServletContext.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/support/MockServletContext.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/support/MockServletContext.java
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/support/PathUtils.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/support/PathUtils.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/support/PathUtils.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/support/PathUtils.java
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/utilstest/FileUtilsTest.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/utilstest/FileUtilsTest.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/utilstest/FileUtilsTest.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/utilstest/FileUtilsTest.java
diff --git a/extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/utilstest/ReflectUtilTest.java b/extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/utilstest/ReflectUtilTest.java
similarity index 100%
rename from extscript-core-root/extscript-core/src/test/java/rewrite/org/apache/myfaces/extensions/scripting/scanningcore/utilstest/ReflectUtilTest.java
rename to extscript-core-root/extscript-core/src/test/java/org/apache/myfaces/extensions/scripting/scanningcore/utilstest/ReflectUtilTest.java