import java.util.Properties;
import java.util.Set;
import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.zip.ZipEntry;
+import java.util.jar.JarInputStream;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
for (WebXml fragment : fragments) {
URL url = fragment.getURL();
- JarFile jarFile = null;
+ JarInputStream jarInputStream = null;
InputStream is = null;
ServletContainerInitializer sci = null;
try {
if ("jar".equals(url.getProtocol())) {
- JarURLConnection conn =
+ JarURLConnection jarConn =
(JarURLConnection) url.openConnection();
- jarFile = conn.getJarFile();
- ZipEntry entry = jarFile.getEntry(SCI_LOCATION);
+ URL resourceURL = jarConn.getJarFileURL();
+ URLConnection resourceConn = resourceURL.openConnection();
+ resourceConn.setUseCaches(false);
+
+ jarInputStream =
+ new JarInputStream(resourceConn.getInputStream());
+ JarEntry entry = jarInputStream.getNextJarEntry();
+ while (entry != null) {
+ if (SCI_LOCATION.equals(entry.getName())) {
+ break;
+ }
+ entry = jarInputStream.getNextJarEntry();
+ }
if (entry != null) {
- is = jarFile.getInputStream(entry);
+ is = jarInputStream;
}
} else if ("file".equals(url.getProtocol())) {
String path = url.getPath();
// Ignore
}
}
- if (jarFile != null) {
+ if (jarInputStream != null) {
try {
- jarFile.close();
+ jarInputStream.close();
} catch (IOException e) {
// Ignore
}
protected void processResourceJARs(Set<WebXml> fragments) {
for (WebXml fragment : fragments) {
URL url = fragment.getURL();
- JarFile jarFile = null;
+ JarInputStream jarInputStream = null;
try {
// Note: Ignore file URLs for now since only jar URLs will be accepted
if ("jar".equals(url.getProtocol())) {
- JarURLConnection conn =
+ JarURLConnection jarConn =
(JarURLConnection) url.openConnection();
- jarFile = conn.getJarFile();
- ZipEntry entry = jarFile.getEntry("META-INF/resources/");
+ URL resourceURL = jarConn.getJarFileURL();
+ URLConnection resourceConn = resourceURL.openConnection();
+ resourceConn.setUseCaches(false);
+ jarInputStream =
+ new JarInputStream(resourceConn.getInputStream());
+ JarEntry entry = jarInputStream.getNextJarEntry();
+ while (entry != null) {
+ if ("META-INF/resources/".equals(entry.getName())) {
+ break;
+ }
+ entry = jarInputStream.getNextJarEntry();
+ }
if (entry != null) {
context.addResourceJarUrl(url);
}
log.error(sm.getString("contextConfig.resourceJarFail", url,
context.getName()));
} finally {
- if (jarFile != null) {
+ if (jarInputStream != null) {
try {
- jarFile.close();
+ jarInputStream.close();
} catch (IOException e) {
// Ignore
}
protected void processAnnotationsJar(URL url, WebXml fragment) {
- JarFile jarFile = null;
+ JarInputStream jarInputStream = null;
try {
URLConnection urlConn = url.openConnection();
- JarURLConnection jarUrlConn;
+ JarURLConnection jarConn;
if (!(urlConn instanceof JarURLConnection)) {
// This should never happen
sm.getString("contextConfig.jarUrl", url);
return;
}
- jarUrlConn = (JarURLConnection) urlConn;
- jarUrlConn.setUseCaches(false);
- jarFile = jarUrlConn.getJarFile();
-
- Enumeration<JarEntry> jarEntries = jarFile.entries();
- while (jarEntries.hasMoreElements()) {
- JarEntry jarEntry = jarEntries.nextElement();
- String entryName = jarEntry.getName();
+ jarConn = (JarURLConnection) urlConn;
+ jarConn.setUseCaches(false);
+ URL resourceURL = jarConn.getJarFileURL();
+ URLConnection resourceConn = resourceURL.openConnection();
+
+ jarInputStream = new JarInputStream(resourceConn.getInputStream());
+
+ JarEntry entry = jarInputStream.getNextJarEntry();
+ while (entry != null) {
+ String entryName = entry.getName();
if (entryName.endsWith(".class")) {
- InputStream is = null;
try {
- is = jarFile.getInputStream(jarEntry);
- processAnnotationsStream(is, fragment);
+ processAnnotationsStream(jarInputStream, fragment);
} catch (IOException e) {
log.error(sm.getString("contextConfig.inputStreamJar",
entryName, url),e);
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- }
- }
}
}
}
} catch (IOException e) {
log.error(sm.getString("contextConfig.jarFile", url), e);
} finally {
- if (jarFile != null) {
+ if (jarInputStream != null) {
try {
- jarFile.close();
+ jarInputStream.close();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
private Map<String,WebXml> fragments = new HashMap<String,WebXml>();
@Override
- public void scan(JarURLConnection urlConn) throws IOException {
+ public void scan(JarURLConnection jarConn) throws IOException {
- JarFile jarFile = null;
- InputStream stream = null;
+ // JarURLConnection#getJarFile() creates temporary copies of the JAR
+ // if the underlying resource is not a file URL. That can be slow so
+ // the InputStream for the resource is used
+ URL resourceURL = jarConn.getJarFileURL();
+
+ JarInputStream jarInputStream = null;
WebXml fragment = new WebXml();
try {
- urlConn.setUseCaches(false);
- jarFile = urlConn.getJarFile();
- JarEntry fragmentEntry =
- jarFile.getJarEntry(FRAGMENT_LOCATION);
- if (fragmentEntry == null) {
+ URLConnection resourceConn = resourceURL.openConnection();
+ resourceConn.setUseCaches(false);
+ jarInputStream =
+ new JarInputStream(resourceConn.getInputStream());
+ JarEntry entry = jarInputStream.getNextJarEntry();
+ while (entry != null) {
+ if (FRAGMENT_LOCATION.equals(entry.getName())) {
+ break;
+ }
+ entry = jarInputStream.getNextJarEntry();
+ }
+
+ if (entry == null) {
// If there is no web.xml, normal JAR no impact on
// distributable
fragment.setDistributable(true);
} else {
- stream = jarFile.getInputStream(fragmentEntry);
InputSource source = new InputSource(
- urlConn.getJarFileURL().toString() +
- "!/" + FRAGMENT_LOCATION);
- source.setByteStream(stream);
+ resourceURL.toString() + "!/" + FRAGMENT_LOCATION);
+ source.setByteStream(jarInputStream);
parseWebXml(source, fragment, true);
}
} finally {
- if (jarFile != null) {
+ if (jarInputStream != null) {
try {
- jarFile.close();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- }
- }
- if (stream != null) {
- try {
- stream.close();
+ jarInputStream.close();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
}
- fragment.setURL(urlConn.getURL());
+ fragment.setURL(jarConn.getURL());
if (fragment.getName() == null) {
fragment.setName(fragment.getURL().toString());
}
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
import javax.servlet.ServletContext;
import javax.servlet.descriptor.TaglibDescriptor;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.util.scan.NonClosingJarInputStream;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
log.trace(sm.getString("tldConfig.webxmlAdd", resourcePath,
descriptor.getTaglibURI()));
}
+ InputStream stream = null;
try {
- InputStream stream = context.getServletContext(
- ).getResourceAsStream(resourcePath);
+ stream = context.getServletContext().getResourceAsStream(
+ resourcePath);
XmlErrorHandler handler = tldScanStream(stream);
handler.logFindings(log, resourcePath);
taglibUris.add(descriptor.getTaglibURI());
} catch (IOException ioe) {
log.warn(sm.getString("tldConfig.webxmlFail", resourcePath,
descriptor.getTaglibURI()), ioe);
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ }
+ }
}
}
}
* Scans the given JarURLConnection for TLD files located in META-INF
* (or a sub-directory of it).
*
- * @param conn The JarURLConnection to the JAR file to scan
+ * @param jarConn The JarURLConnection to the JAR file to scan
*
* Keep in sync with o.a.j.comiler.TldLocationsCache
*/
- private void tldScanJar(JarURLConnection conn) {
+ private void tldScanJar(JarURLConnection jarConn) {
- JarFile jarFile = null;
+ // JarURLConnection#getJarFile() creates temporary copies of the JAR if
+ // the underlying resource is not a file URL. That can be slow so the
+ // InputStream for the resource is used
+ URL resourceURL = jarConn.getJarFileURL();
+ NonClosingJarInputStream jarInputStream = null;
String name = null;
+
try {
- conn.setUseCaches(false);
- jarFile = conn.getJarFile();
- Enumeration<JarEntry> entries = jarFile.entries();
- while (entries.hasMoreElements()) {
- JarEntry entry = entries.nextElement();
+ URLConnection resourceConn = resourceURL.openConnection();
+ resourceConn.setUseCaches(false);
+ jarInputStream =
+ new NonClosingJarInputStream(resourceConn.getInputStream());
+
+ JarEntry entry = jarInputStream.getNextJarEntry();
+ while (entry != null) {
name = entry.getName();
- if (!name.startsWith("META-INF/")) continue;
- if (!name.endsWith(".tld")) continue;
- InputStream stream = jarFile.getInputStream(entry);
- XmlErrorHandler handler = tldScanStream(stream);
- handler.logFindings(log, conn.getURL() + name);
+ if (name.startsWith("META-INF/") && name.endsWith(".tld")) {
+ XmlErrorHandler handler = tldScanStream(jarInputStream);
+ handler.logFindings(log, jarConn.getURL() + name);
+ }
+ entry = jarInputStream.getNextJarEntry();
}
} catch (IOException ioe) {
- log.warn(sm.getString("tldConfig.jarFail", conn.getURL() + name),
+ log.warn(sm.getString("tldConfig.jarFail", jarConn.getURL() + name),
ioe);
} finally {
- if (jarFile != null) {
+ if (jarInputStream != null) {
try {
- jarFile.close();
+ jarInputStream.reallyClose();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
throw new IOException(s);
} finally {
tldDigester.reset();
- if (resourceStream != null) {
- try {
- resourceStream.close();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- }
- }
}
return result;
}
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
-import java.util.Enumeration;
+import java.net.URL;
+import java.net.URLConnection;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
import javax.servlet.ServletContext;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.JarScanner;
import org.apache.tomcat.JarScannerCallback;
+import org.apache.tomcat.util.scan.NonClosingJarInputStream;
/**
* (or a subdirectory of it), adding an implicit map entry to the taglib
* map for any TLD that has a <uri> element.
*
- * @param conn The JarURLConnection to the JAR file to scan
+ * @param jarConn The JarURLConnection to the JAR file to scan
*
* Keep in sync with o.a.c.startup.TldConfig
*/
- private void tldScanJar(JarURLConnection conn) throws IOException {
-
- JarFile jarFile = null;
- String resourcePath = conn.getJarFileURL().toString();
+ private void tldScanJar(JarURLConnection jarConn) throws IOException {
+
+ // JarURLConnection#getJarFile() creates temporary copies of the JAR if
+ // the underlying resource is not a file URL. That can be slow so the
+ // InputStream for the resource is used
+ URL resourceURL = jarConn.getJarFileURL();
+ String resourcePath = resourceURL.toString();
+
+ NonClosingJarInputStream jarInputStream = null;
+
boolean foundTld = false;
try {
- conn.setUseCaches(false);
- jarFile = conn.getJarFile();
- Enumeration<JarEntry> entries = jarFile.entries();
- while (entries.hasMoreElements()) {
- JarEntry entry = entries.nextElement();
+ URLConnection resourceConn = resourceURL.openConnection();
+ resourceConn.setUseCaches(false);
+ jarInputStream =
+ new NonClosingJarInputStream(resourceConn.getInputStream());
+ JarEntry entry = jarInputStream.getNextJarEntry();
+ while (entry != null) {
String name = entry.getName();
- if (!name.startsWith("META-INF/")) continue;
- if (!name.endsWith(".tld")) continue;
- foundTld = true;
- InputStream stream = jarFile.getInputStream(entry);
- tldScanStream(resourcePath, name, stream);
+ if (name.startsWith("META-INF/") && name.endsWith(".tld")) {
+ foundTld = true;
+ tldScanStream(resourcePath, name, jarInputStream);
+ }
+ entry = jarInputStream.getNextJarEntry();
}
} finally {
- if (jarFile != null) {
+ if (jarInputStream != null) {
try {
- jarFile.close();
+ jarInputStream.reallyClose();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
} catch (JasperException e) {
// Hack - makes exception handling simpler
throw new IOException(e);
- } finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- }
- }
}
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.scan;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.jar.JarInputStream;
+
+/**
+ * When using a {@link JarInputStream} with an XML parser, the stream will be
+ * closed by the parser. This causes problems if multiple entries from the JAR
+ * need to be parsed. This implementation makes {{@link #close()} a NO-OP and
+ * adds {@link #reallyClose()} that will close the stream.
+ */
+public class NonClosingJarInputStream extends JarInputStream {
+
+ public NonClosingJarInputStream(InputStream in, boolean verify)
+ throws IOException {
+ super(in, verify);
+ }
+
+ public NonClosingJarInputStream(InputStream in) throws IOException {
+ super(in);
+ }
+
+ @Override
+ public void close() throws IOException {
+ // Make this a NO-OP so that further entries can be read from the stream
+ }
+
+ public void reallyClose() throws IOException {
+ super.close();
+ }
+}
fragments to the list of JARs to skip when scanning for TLDs and web
fragments. (markt)
</add>
+ <fix>
+ While scanning JARs for TLDs and fragments, avoid using JarFile and use
+ JarInputStream as in most circumstances where JARs are scanned, JarFile
+ will create a temporary copy of the JAR rather than using the resource
+ directly. This change significantly improves startup performance for
+ applications with lots of JARs to be scanned. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">