import java.util.Map;
import java.util.Properties;
import java.util.Set;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.digester.RuleSet;
import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.util.scan.Jar;
+import org.apache.tomcat.util.scan.JarFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;
for (WebXml fragment : fragments) {
URL url = fragment.getURL();
- JarInputStream jarInputStream = null;
+ Jar jar = null;
InputStream is = null;
ServletContainerInitializer sci = null;
try {
if ("jar".equals(url.getProtocol())) {
- JarURLConnection jarConn =
- (JarURLConnection) url.openConnection();
- 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 = jarInputStream;
- }
+ jar = JarFactory.newInstance(url);
+ is = jar.getInputStream(SCI_LOCATION);
} else if ("file".equals(url.getProtocol())) {
String path = url.getPath();
File file = new File(path, SCI_LOCATION);
// Ignore
}
}
- if (jarInputStream != null) {
- try {
- jarInputStream.close();
- } catch (IOException e) {
- // Ignore
- }
+ if (jar != null) {
+ jar.close();
}
}
protected void processResourceJARs(Set<WebXml> fragments) {
for (WebXml fragment : fragments) {
URL url = fragment.getURL();
- JarInputStream jarInputStream = null;
+ Jar jar = null;
try {
// Note: Ignore file URLs for now since only jar URLs will be accepted
if ("jar".equals(url.getProtocol())) {
- JarURLConnection jarConn =
- (JarURLConnection) url.openConnection();
- 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) {
+ jar = JarFactory.newInstance(url);
+ if (jar.entryExists("META-INF/resources/")) {
context.addResourceJarUrl(url);
}
}
log.error(sm.getString("contextConfig.resourceJarFail", url,
context.getName()));
} finally {
- if (jarInputStream != null) {
- try {
- jarInputStream.close();
- } catch (IOException e) {
- // Ignore
- }
+ if (jar != null) {
+ jar.close();
}
}
}
protected void processAnnotationsJar(URL url, WebXml fragment) {
- JarInputStream jarInputStream = null;
+
+ Jar jar = null;
+ InputStream is;
try {
- URLConnection urlConn = url.openConnection();
- JarURLConnection jarConn;
- if (!(urlConn instanceof JarURLConnection)) {
- // This should never happen
- sm.getString("contextConfig.jarUrl", url);
- return;
- }
+ jar = JarFactory.newInstance(url);
- 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();
+ jar.nextEntry();
+ String entryName = jar.getEntryName();
+ while (entryName != null) {
if (entryName.endsWith(".class")) {
+ is = null;
try {
- processAnnotationsStream(jarInputStream, fragment);
+ is = jar.getEntryInputStream();
+ processAnnotationsStream(is, fragment);
} catch (IOException e) {
log.error(sm.getString("contextConfig.inputStreamJar",
entryName, url),e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException ioe) {
+ // Ignore
+ }
+ }
}
}
- entry = jarInputStream.getNextJarEntry();
+ jar.nextEntry();
+ entryName = jar.getEntryName();
}
} catch (IOException e) {
log.error(sm.getString("contextConfig.jarFile", url), e);
} finally {
- if (jarInputStream != null) {
- try {
- jarInputStream.close();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- }
+ if (jar != null) {
+ jar.close();
}
}
}
@Override
public void scan(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 url = jarConn.getURL();
URL resourceURL = jarConn.getJarFileURL();
-
- JarInputStream jarInputStream = null;
+ Jar jar = null;
+ InputStream is = null;
WebXml fragment = new WebXml();
try {
- 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();
- }
+ jar = JarFactory.newInstance(url);
+ is = jar.getInputStream(FRAGMENT_LOCATION);
- if (entry == null) {
+ if (is == null) {
// If there is no web.xml, normal JAR no impact on
// distributable
fragment.setDistributable(true);
} else {
InputSource source = new InputSource(
resourceURL.toString() + "!/" + FRAGMENT_LOCATION);
- source.setByteStream(jarInputStream);
+ source.setByteStream(is);
parseWebXml(source, fragment, true);
}
} finally {
- if (jarInputStream != null) {
+ if (is != null) {
try {
- jarInputStream.close();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
+ is.close();
+ } catch (IOException ioe) {
+ // Ignore
}
}
- fragment.setURL(jarConn.getURL());
+ if (jar != null) {
+ jar.close();
+ }
+ fragment.setURL(url);
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.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
-import java.util.jar.JarEntry;
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.apache.tomcat.util.scan.Jar;
+import org.apache.tomcat.util.scan.JarFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
*/
private void tldScanJar(JarURLConnection jarConn) {
- // 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;
-
+ Jar jar = null;
+ InputStream is;
+
try {
- 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/") && name.endsWith(".tld")) {
- XmlErrorHandler handler = tldScanStream(jarInputStream);
- handler.logFindings(log, jarConn.getURL() + name);
+ jar = JarFactory.newInstance(jarConn.getURL());
+
+ jar.nextEntry();
+ String entryName = jar.getEntryName();
+ while (entryName != null) {
+ if (entryName.startsWith("META-INF/") &&
+ entryName.endsWith(".tld")) {
+ is = null;
+ try {
+ is = jar.getEntryInputStream();
+ XmlErrorHandler handler = tldScanStream(is);
+ handler.logFindings(log, jarConn.getURL() + entryName);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException ioe) {
+ // Ignore
+ }
+ }
+ }
}
- entry = jarInputStream.getNextJarEntry();
+ jar.nextEntry();
+ entryName = jar.getEntryName();
}
} catch (IOException ioe) {
- log.warn(sm.getString("tldConfig.jarFail", jarConn.getURL() + name),
- ioe);
+ log.warn(sm.getString("tldConfig.jarFail", jarConn.getURL()), ioe);
} finally {
- if (jarInputStream != null) {
- try {
- jarInputStream.reallyClose();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- }
+ if (jar != null) {
+ jar.close();
}
}
}
import java.io.InputStream;
import java.net.JarURLConnection;
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 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;
+import org.apache.tomcat.util.scan.Jar;
+import org.apache.tomcat.util.scan.JarFactory;
/**
*/
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
+ Jar jar = null;
+ InputStream is;
+ boolean foundTld = false;
+
URL resourceURL = jarConn.getJarFileURL();
String resourcePath = resourceURL.toString();
- NonClosingJarInputStream jarInputStream = null;
-
- boolean foundTld = false;
try {
- 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/") && name.endsWith(".tld")) {
- foundTld = true;
- tldScanStream(resourcePath, name, jarInputStream);
+ jar = JarFactory.newInstance(jarConn.getURL());
+
+ jar.nextEntry();
+ String entryName = jar.getEntryName();
+ while (entryName != null) {
+ if (entryName.startsWith("META-INF/") &&
+ entryName.endsWith(".tld")) {
+ is = null;
+ try {
+ is = jar.getEntryInputStream();
+ foundTld = true;
+ tldScanStream(resourcePath, entryName, is);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException ioe) {
+ // Ignore
+ }
+ }
+ }
}
- entry = jarInputStream.getNextJarEntry();
+ jar.nextEntry();
+ entryName = jar.getEntryName();
}
} finally {
- if (jarInputStream != null) {
- try {
- jarInputStream.reallyClose();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- }
+ if (jar != null) {
+ jar.close();
}
}
+
if (!foundTld) {
log.info(Localizer.getMessage("jsp.tldCache.noTldInJar",
resourcePath));
--- /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.net.JarURLConnection;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+/**
+ * Implementation of {@link Jar} that is optimised for file based JAR URLs (e.g
+ * URLs of the form jar:file:...).
+ */
+public class FileUrlJar implements Jar {
+
+ private JarFile jarFile;
+ private Enumeration<JarEntry> entries;
+ private JarEntry entry = null;
+
+ public FileUrlJar(URL url) throws IOException {
+ JarURLConnection jarConn = (JarURLConnection) url.openConnection();
+ jarFile = jarConn.getJarFile();
+ }
+
+ @Override
+ public boolean entryExists(String name) {
+ ZipEntry entry = jarFile.getEntry(name);
+ return entry != null;
+ }
+
+ @Override
+ public InputStream getInputStream(String name) throws IOException {
+ ZipEntry entry = jarFile.getEntry(name);
+ if (entry == null) {
+ return null;
+ } else {
+ return jarFile.getInputStream(entry);
+ }
+ }
+
+ @Override
+ public void close() {
+ if (jarFile != null) {
+ try {
+ jarFile.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+
+ @Override
+ public void nextEntry() {
+ if (entries == null) {
+ entries = jarFile.entries();
+ }
+ if (entries.hasMoreElements()) {
+ entry = entries.nextElement();
+ } else {
+ entry = null;
+ }
+ }
+
+ @Override
+ public String getEntryName() {
+ if (entry == null) {
+ return null;
+ } else {
+ return entry.getName();
+ }
+ }
+
+ @Override
+ public InputStream getEntryInputStream() throws IOException {
+ if (entry == null) {
+ return null;
+ } else {
+ return jarFile.getInputStream(entry);
+ }
+ }
+
+ @Override
+ public void reset() throws IOException {
+ entries = null;
+ entry = null;
+ }
+}
--- /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;
+
+/**
+ * Provides an abstraction for use by the various classes that need to scan
+ * JARs. The classes provided by the JRE for accessing JARs ({@link JarFile} and
+ * {@link JarInputStream}) have significantly different performance
+ * characteristics depending on the form of the URL used to access the JAR.
+ * For file based JAR {@link URL}s, {@link JarFile} is faster but for non-file
+ * based {@link URL}s, {@link JarFile} creates a copy of the JAR in the
+ * temporary directory so {@link JarInputStream} is faster.
+ */
+public interface Jar {
+
+ /**
+ * Determines if a specific entry exists within the JAR.
+ *
+ * @param name Entry to look for
+ * @return <code>true</code> if the specified entry exists else
+ * <code>false</code>
+ */
+ boolean entryExists(String name) throws IOException;
+
+
+ /**
+ * Obtain an {@link InputStream} for a given entry in a JAR. The caller is
+ * responsible for closing the stream.
+ *
+ * @param name Entry to obtain an {@link InputStream} for
+ * @return An {@link InputStream} for the specified entry or null if
+ * the entry does not exist
+ */
+ InputStream getInputStream(String name) throws IOException;
+
+ /**
+ * Close any resources associated with this JAR.
+ */
+ void close();
+
+ /**
+ * Moves the internal pointer to the next entry in the JAR.
+ */
+ void nextEntry();
+
+ /**
+ * Obtains the name of the current entry.
+ *
+ * @return The entry name
+ */
+ String getEntryName();
+
+ /**
+ * Obtains the input stream for the current entry.
+ *
+ * @return The input stream
+ * @throws IOException If the stream cannot be obtained
+ */
+ InputStream getEntryInputStream() throws IOException;
+
+ /**
+ * Resets the internal pointer used to track JAR entries to the beginning of
+ * the JAR.
+ *
+ * @throws IOException If the pointer cannot be reset
+ */
+ void reset() throws IOException;
+}
--- /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.net.URL;
+
+/**
+ * Provide a mechanism to obtain objects that implement {@link Jar}.
+ */
+public class JarFactory {
+
+ private JarFactory() {
+ // Factory class. Hide public constructor.
+ }
+
+ public static Jar newInstance(URL url) throws IOException {
+ String jarUrl = url.toString();
+ if (jarUrl.startsWith("jar:file:")) {
+ return new FileUrlJar(url);
+ } else {
+ return new UrlJar(url);
+ }
+ }
+}
--- /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.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.jar.JarEntry;
+
+/**
+ * Implementation of {@link Jar} that is optimised for non-file based JAR URLs
+ * (e.g. JNDI based URLs of the form jar:jndi:...).
+ */
+public class UrlJar implements Jar {
+
+ private NonClosingJarInputStream jarInputStream = null;
+ private URL url = null;
+ private JarEntry entry = null;
+
+ public UrlJar(URL url) throws IOException {
+ this.url = url;
+ this.jarInputStream = createJarInputStream();
+ }
+
+ @Override
+ public boolean entryExists(String name) throws IOException {
+ JarEntry entry = jarInputStream.getNextJarEntry();
+ while (entry != null) {
+ if (name.equals(entry.getName())) {
+ break;
+ }
+ entry = jarInputStream.getNextJarEntry();
+ }
+
+ return entry != null;
+ }
+
+ @Override
+ public InputStream getInputStream(String name) throws IOException {
+ JarEntry entry = jarInputStream.getNextJarEntry();
+ while (entry != null) {
+ if (name.equals(entry.getName())) {
+ break;
+ }
+ entry = jarInputStream.getNextJarEntry();
+ }
+
+ if (entry == null) {
+ return null;
+ } else {
+ return jarInputStream;
+ }
+ }
+
+ @Override
+ public void close() {
+ if (jarInputStream != null) {
+ try {
+ jarInputStream.reallyClose();
+ } catch (IOException ioe) {
+ // Ignore
+ }
+ }
+ }
+
+ private NonClosingJarInputStream createJarInputStream() throws IOException {
+ JarURLConnection jarConn = (JarURLConnection) url.openConnection();
+ URL resourceURL = jarConn.getJarFileURL();
+ URLConnection resourceConn = resourceURL.openConnection();
+ resourceConn.setUseCaches(false);
+ return new NonClosingJarInputStream(resourceConn.getInputStream());
+ }
+
+ @Override
+ public void nextEntry() {
+ try {
+ entry = jarInputStream.getNextJarEntry();
+ } catch (IOException ioe) {
+ entry = null;
+ }
+ }
+
+ @Override
+ public String getEntryName() {
+ if (entry == null) {
+ return null;
+ } else {
+ return entry.getName();
+ }
+ }
+
+ @Override
+ public InputStream getEntryInputStream() throws IOException {
+ return jarInputStream;
+ }
+
+ @Override
+ public void reset() throws IOException {
+ close();
+ jarInputStream = createJarInputStream();
+ }
+}
Patch provided by Eiji Takahashi. (markt)
</fix>
<fix>
+ <bug>51276</bug>: Provide an abstraction for accessing content in JARs
+ so the most efficient method can be selected depending on the type of
+ URL used to identify the JAR. This improves startup time when JARs are
+ located in $CATALINA_BASE/lib. (mark)
+ </fix>
+ <fix>
<bug>51277</bug>: Improve error message if an application is deployed
with an incomplete FORM authentication configuration. (markt)
</fix>