EXTSCRIPT-154: Code Rewrite/Refactoring, adding the resource handlers, we donĀ“t do anything with those in this rewrite, maybe in the next one we will patch facelets in the impl directly to handle that.


git-svn-id: https://svn.apache.org/repos/asf/myfaces/extensions/scripting/trunk@1298758 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/AliasResourceMetaImpl.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/AliasResourceMetaImpl.java
new file mode 100644
index 0000000..71248e3
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/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 rewrite.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/BaseResourceHandlerSupport.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/BaseResourceHandlerSupport.java
new file mode 100644
index 0000000..b51b2dd
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/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 rewrite.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ClassLoaderResourceLoader.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ClassLoaderResourceLoader.java
new file mode 100644
index 0000000..200142b
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ClassLoaderResourceLoader.java
@@ -0,0 +1,479 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License.  You may obtain a copy of the License at

+ *

+ *   http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied.  See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+package rewrite.org.apache.myfaces.extensions.scripting.jsf.resources;

+

+import org.apache.myfaces.extensions.scripting.core.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ExternalContextResourceLoader.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ExternalContextResourceLoader.java
new file mode 100644
index 0000000..6e10fa7
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/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 rewrite.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/FacesServletMapping.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/FacesServletMapping.java
new file mode 100644
index 0000000..aaa6e09
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/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 rewrite.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerCache.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerCache.java
new file mode 100644
index 0000000..5939464
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/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 rewrite.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerImpl.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerImpl.java
new file mode 100644
index 0000000..5f3ed59
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/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 rewrite.org.apache.myfaces.extensions.scripting.jsf.resources;
+
+import org.apache.myfaces.extensions.scripting.core.util.ClassLoaderUtils;
+import org.apache.myfaces.renderkit.ErrorPageWriter;
+
+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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerSupport.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceHandlerSupport.java
new file mode 100644
index 0000000..c4863da
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/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 rewrite.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceImpl.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceImpl.java
new file mode 100644
index 0000000..9a631e6
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/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 rewrite.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceLoader.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceLoader.java
new file mode 100644
index 0000000..3e9f1b3
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/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 rewrite.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceLoaderUtils.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceLoaderUtils.java
new file mode 100644
index 0000000..54762f8
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/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 rewrite.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceMeta.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceMeta.java
new file mode 100644
index 0000000..475bec2
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/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 rewrite.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceMetaImpl.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/ResourceMetaImpl.java
new file mode 100644
index 0000000..60259b4
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/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 rewrite.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceHandler.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceHandler.java
new file mode 100644
index 0000000..33f58eb
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceHandler.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package rewrite.org.apache.myfaces.extensions.scripting.jsf.resources;
+
+import org.apache.myfaces.extensions.scripting.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceHandlerSupport.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceHandlerSupport.java
new file mode 100644
index 0000000..d4445e1
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/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 rewrite.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/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceLoader.java b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceLoader.java
new file mode 100644
index 0000000..8423f4e
--- /dev/null
+++ b/extscript-core-root/extscript-core/src/main/java/rewrite/org/apache/myfaces/extensions/scripting/jsf/resources/SourceResourceLoader.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package rewrite.org.apache.myfaces.extensions.scripting.jsf.resources;
+
+import org.apache.myfaces.extensions.scripting.core.util.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.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.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.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;
+        }
+    }
+}