Downports a JSF fix to close opened JarFile
diff --git a/build.versions b/build.versions
index 117e630..f5ecc42 100644
--- a/build.versions
+++ b/build.versions
@@ -128,7 +128,7 @@
 org.eclipse.persistence.core=2.4.1.v20121003-ad44345
 org.eclipse.persistence.jpa.jpql=2.0.1.v20121003-ad44345
 org.eclipse.persistence.jpa=2.4.1.v20121003-ad44345
-org.glassfish.com.sun.faces=2.1.6.v201205171319-virgo-1
+org.glassfish.com.sun.faces=2.1.6.v201205171319-virgo-2
 org.glassfish.javax.faces=2.1.6.v201210041257
 org.ow2.jotm.jotm-core=2.1.9.v201204271116
 org.quartz=1.6.0.v201204271540
diff --git a/hotfix/plugins/org.glassfish.com.sun.faces_2.1.6.v201205171319-virgo-1.jar b/hotfix/plugins/org.glassfish.com.sun.faces_2.1.6.v201205171319-virgo-2.jar
similarity index 77%
rename from hotfix/plugins/org.glassfish.com.sun.faces_2.1.6.v201205171319-virgo-1.jar
rename to hotfix/plugins/org.glassfish.com.sun.faces_2.1.6.v201205171319-virgo-2.jar
index 91a4e2d..3d5a644 100755
--- a/hotfix/plugins/org.glassfish.com.sun.faces_2.1.6.v201205171319-virgo-1.jar
+++ b/hotfix/plugins/org.glassfish.com.sun.faces_2.1.6.v201205171319-virgo-2.jar
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces/Classpath.class b/hotfix/source/org.glassfish.com.sun.faces/Classpath.class
new file mode 100755
index 0000000..a7dcec3
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces/Classpath.class
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces/Classpath.java b/hotfix/source/org.glassfish.com.sun.faces/Classpath.java
new file mode 100755
index 0000000..c0425ff
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces/Classpath.java
@@ -0,0 +1,351 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ *
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2005-2007 The Apache Software Foundation
+ *
+ * Licensed 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 com.sun.faces.facelets.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLDecoder;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * @author Jacob Hookom
+ * @author Roland Huss
+ * @author Ales Justin (ales.justin@jboss.org)
+ */
+public final class Classpath {
+
+    // discard any urls that begin with rar: and sar:
+    // or end with their counterparts
+    // as these should not be looked at for JSF related content.
+    private static final String [] PREFIXES_TO_EXCLUDE = {
+        "rar:",
+        "sar:"
+    };
+    private static final String [] EXTENSIONS_TO_EXCLUDE = {
+        ".rar",
+        ".sar"
+    };
+
+    /**
+     *
+     */
+    public Classpath() {
+        super();
+    }
+
+    public static URL[] search(String prefix, String suffix)
+          throws IOException {
+        return search(Thread.currentThread().getContextClassLoader(), prefix,
+                      suffix);
+    }
+
+    public static URL[] search(ClassLoader cl, String prefix, String suffix)
+          throws IOException {
+        Enumeration[] e = new Enumeration[]{
+              cl.getResources(prefix),
+              cl.getResources(prefix + "MANIFEST.MF")
+        };
+        Set all = new LinkedHashSet();
+        URL url;
+        URLConnection conn;
+        JarFile jarFile = null;
+        for (int i = 0, s = e.length; i < s; ++i) {
+            while (e[i].hasMoreElements()) {
+                url = (URL) e[i].nextElement();
+                // Defensive programming.  Due to issue 13045 this collection
+                // can contain URLs that have their spaces incorrectly escaped
+                // by having %20 replaced with %2520.  This quick conditional 
+                // check catches this particular case and averts it.
+                String str = url.getPath();
+                if (-1 != str.indexOf("%2520")) {
+                    str = url.toExternalForm();
+                    str = str.replace("%2520", "%20");
+                    url = new URL(str);
+                }
+                conn = url.openConnection();
+                conn.setUseCaches(false);
+                conn.setDefaultUseCaches(false);
+                try {
+					if (conn instanceof JarURLConnection) {
+					    jarFile = ((JarURLConnection) conn).getJarFile();
+					} else {
+					    jarFile = getAlternativeJarFile(url);
+					}
+					if (jarFile != null) {
+					    searchJar(cl, all, jarFile, prefix, suffix);
+					} else {
+					    boolean searchDone =
+					          searchDir(all, new File(URLDecoder.decode(url.getFile(), "UTF-8")), suffix);
+					    if (!searchDone) {
+					        searchFromURL(all, prefix, suffix, url);
+					    }
+					}
+				} finally {
+					if (jarFile != null) {
+						try {
+							jarFile.close();
+						} catch (Exception ex) {
+							// do nothing
+						}
+					}
+				}
+            }
+        }
+        URL[] urlArray = (URL[]) all.toArray(new URL[all.size()]);
+        return urlArray;
+    }
+
+    private static boolean searchDir(Set result, File file, String suffix)
+          throws IOException {
+        if (file.exists() && file.isDirectory()) {
+            File[] fc = file.listFiles();
+            String path;
+            URL src;
+            // protect against Windows JDK bugs for listFiles -
+            // if it's null (even though it shouldn't be) return false
+            if (fc == null) return false;
+
+            for (int i = 0; i < fc.length; i++) {
+                path = fc[i].getAbsolutePath();
+                if (fc[i].isDirectory()) {
+                    searchDir(result, fc[i], suffix);
+                } else if (path.endsWith(suffix)) {
+                    // result.add(new URL("file:/" + path));
+                    result.add(fc[i].toURL());
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Search from URL. Fall back on prefix tokens if not able to read from
+     * original url param.
+     *
+     * @param result the result urls
+     * @param prefix the current prefix
+     * @param suffix the suffix to match
+     * @param url    the current url to start search
+     *
+     * @throws IOException for any error
+     */
+    private static void searchFromURL(Set result, String prefix, String suffix,
+                                      URL url) throws IOException {
+        boolean done = false;
+        InputStream is = getInputStream(url);
+        if (is != null) {
+            ZipInputStream zis;
+            if (is instanceof ZipInputStream) {
+                zis = (ZipInputStream) is;
+            } else {
+                zis = new ZipInputStream(is);
+            }
+            try {
+                ZipEntry entry = zis.getNextEntry();
+                // initial entry should not be null
+                // if we assume this is some inner jar
+                done = (entry != null);
+                while (entry != null) {
+                    String entryName = entry.getName();
+                    if (entryName.endsWith(suffix)) {
+                        String urlString = url.toExternalForm();
+                        result.add(new URL(urlString + entryName));
+                    }
+                    entry = zis.getNextEntry();
+                }
+            } finally {
+                zis.close();
+            }
+        }
+        if (!done && prefix.length() > 0) {
+            // we add '/' at the end since join adds it as well
+            String urlString = url.toExternalForm() + "/";
+            String[] split = prefix.split("/");
+            prefix = join(split, true);
+            String end = join(split, false);
+            int p = urlString.lastIndexOf(end);
+            urlString = urlString.substring(0, p);
+            for (String cur : PREFIXES_TO_EXCLUDE) {
+                if (urlString.startsWith(cur)) {
+                    return;
+                }
+            }
+            url = new URL(urlString);
+            searchFromURL(result, prefix, suffix, url);
+        }
+    }
+
+    /**
+     * Join tokens, exlude last if param equals true.
+     *
+     * @param tokens      the tokens
+     * @param excludeLast do we exclude last token
+     *
+     * @return joined tokens
+     */
+    private static String join(String[] tokens, boolean excludeLast) {
+        StringBuffer join = new StringBuffer();
+        for (int i = 0; i < tokens.length - (excludeLast ? 1 : 0); i++) {
+            join.append(tokens[i]).append("/");
+        }
+        return join.toString();
+    }
+
+    /**
+     * Open input stream from url. Ignore any errors.
+     *
+     * @param url the url to open
+     *
+     * @return input stream or null if not possible
+     */
+    private static InputStream getInputStream(URL url) {
+        try {
+            return url.openStream();
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    /**
+     * For URLs to JARs that do not use JarURLConnection - allowed by the servlet
+     * spec - attempt to produce a JarFile object all the same. Known servlet
+     * engines that function like this include Weblogic and OC4J. This is not a
+     * full solution, since an unpacked WAR or EAR will not have JAR "files" as
+     * such.
+     */
+    private static JarFile getAlternativeJarFile(URL url) throws IOException {
+        String urlFile = url.getFile();
+        return getAlternativeJarFile(urlFile);
+    }
+    
+    static JarFile getAlternativeJarFile(String urlFile) throws IOException {
+        JarFile result = null;
+        // Trim off any suffix - which is prefixed by "!/" on Weblogic
+        int bangSlash = urlFile.indexOf("!/");
+        // Try the less safe "!", used on OC4J
+        int bang = urlFile.indexOf('!');
+        int separatorIndex = -1;
+        
+        // if either are found, take the first one.
+        if (-1 != bangSlash || -1 != bang) {
+            if (bangSlash < bang) {
+                separatorIndex = bangSlash;
+            } else {
+                separatorIndex = bang;
+            }
+        }
+
+        if (separatorIndex != -1) {
+            String jarFileUrl = urlFile.substring(0, separatorIndex);
+            // And trim off any "file:" prefix.
+            if (jarFileUrl.startsWith("file:")) {
+                jarFileUrl = jarFileUrl.substring("file:".length());
+                jarFileUrl = URLDecoder.decode(jarFileUrl, "UTF-8");
+            }
+            boolean foundExclusion = false;
+            for (int i = 0; i < PREFIXES_TO_EXCLUDE.length; i++) {
+                if (jarFileUrl.startsWith(PREFIXES_TO_EXCLUDE[i]) ||
+                    jarFileUrl.endsWith(EXTENSIONS_TO_EXCLUDE[i])) {
+                    foundExclusion = true;
+                    break;
+                }
+            }
+            if (!foundExclusion) {
+                result = new JarFile(jarFileUrl);
+            }
+
+            return result;
+        }
+        return null;
+    }
+
+    private static void searchJar(ClassLoader cl, Set result, JarFile file,
+                                  String prefix, String suffix)
+          throws IOException {
+        Enumeration e = file.entries();
+        JarEntry entry;
+        String name;
+        while (e.hasMoreElements()) {
+            try {
+                entry = (JarEntry) e.nextElement();
+            } catch (Throwable t) {
+                continue;
+            }
+            name = entry.getName();
+            if (name.startsWith(prefix) && name.endsWith(suffix)) {
+                Enumeration e2 = cl.getResources(name);
+                while (e2.hasMoreElements()) {
+					result.add(e2.nextElement());
+				}
+			}
+		}
+	}
+
+}