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());
+ }
+ }
+ }
+ }
+
+}