Author: markt Date: Mon Nov 28 11:14:29 2016 New Revision: 1771711 URL: http://svn.apache.org/viewvc?rev=1771711&view=rev Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=60410 Ensure that multiple calls to JarInputStreamWrapper#close() do not incorrectly trigger the closure of the underlying JAR or WAR file.
Added: tomcat/trunk/test/org/apache/catalina/webresources/TestJarInputStreamWrapper.java (with props) Modified: tomcat/trunk/java/org/apache/catalina/webresources/AbstractArchiveResource.java tomcat/trunk/webapps/docs/changelog.xml Modified: tomcat/trunk/java/org/apache/catalina/webresources/AbstractArchiveResource.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/webresources/AbstractArchiveResource.java?rev=1771711&r1=1771710&r2=1771711&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/webresources/AbstractArchiveResource.java (original) +++ tomcat/trunk/java/org/apache/catalina/webresources/AbstractArchiveResource.java Mon Nov 28 11:14:29 2016 @@ -21,6 +21,7 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.security.cert.Certificate; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.jar.JarEntry; import java.util.jar.Manifest; @@ -233,7 +234,7 @@ public abstract class AbstractArchiveRes /** * This wrapper assumes that the InputStream was created from a JarFile - * obtained from a call to getArchiveResourceSet().getJarFile(). If this is + * obtained from a call to getArchiveResourceSet().openJarFile(). If this is * not the case then the usage counting in AbstractArchiveResourceSet will * break and the JarFile may be unexpectedly closed. */ @@ -241,6 +242,7 @@ public abstract class AbstractArchiveRes private final JarEntry jarEntry; private final InputStream is; + private final AtomicBoolean closed = new AtomicBoolean(false); public JarInputStreamWrapper(JarEntry jarEntry, InputStream is) { @@ -281,7 +283,11 @@ public abstract class AbstractArchiveRes @Override public void close() throws IOException { - archiveResourceSet.closeJarFile(); + if (closed.compareAndSet(false, true)) { + // Must only call this once else the usage counting will break + archiveResourceSet.closeJarFile(); + } + is.close(); } Added: tomcat/trunk/test/org/apache/catalina/webresources/TestJarInputStreamWrapper.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/webresources/TestJarInputStreamWrapper.java?rev=1771711&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/catalina/webresources/TestJarInputStreamWrapper.java (added) +++ tomcat/trunk/test/org/apache/catalina/webresources/TestJarInputStreamWrapper.java Mon Nov 28 11:14:29 2016 @@ -0,0 +1,140 @@ +/* + * 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.catalina.webresources; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.WebResource; + +public class TestJarInputStreamWrapper { + + @Test + public void testReadAfterClose() throws Exception { + Method m = InputStream.class.getMethod("read", (Class<?>[]) null); + testMethodAfterClose(m, (Object[]) null); + } + + + @Test + public void testSkipAfterClose() throws Exception { + Method m = InputStream.class.getMethod("skip", long.class); + testMethodAfterClose(m, Long.valueOf(1)); + } + + + @Test + public void testAvailableAfterClose() throws Exception { + Method m = InputStream.class.getMethod("available", (Class<?>[]) null); + testMethodAfterClose(m, (Object[]) null); + } + + + @Test + public void testCloseAfterClose() throws Exception { + Method m = InputStream.class.getMethod("close", (Class<?>[]) null); + testMethodAfterClose(m, (Object[]) null); + } + + + @Test + public void testMarkAfterClose() throws Exception { + Method m = InputStream.class.getMethod("mark", int.class); + testMethodAfterClose(m, Integer.valueOf(1)); + } + + + @Test + public void testResetAfterClose() throws Exception { + Method m = InputStream.class.getMethod("reset", (Class<?>[]) null); + testMethodAfterClose(m, (Object[]) null); + } + + + @Test + public void testMarkSupprotedAfterClose() throws Exception { + Method m = InputStream.class.getMethod("markSupported", (Class<?>[]) null); + testMethodAfterClose(m, (Object[]) null); + } + + + private void testMethodAfterClose(Method m, Object... params) throws IOException { + InputStream unwrapped = getUnwrappedClosedInputStream(); + InputStream wrapped = getWrappedClosedInputStream(); + + Object unwrappedReturn = null; + Exception unwrappedException = null; + Object wrappedReturn = null; + Exception wrappedException = null; + + try { + unwrappedReturn = m.invoke(unwrapped, params); + } catch (Exception e) { + unwrappedException = e; + } + + try { + wrappedReturn = m.invoke(wrapped, params); + } catch (Exception e) { + wrappedException = e; + } + + if (unwrappedReturn == null) { + Assert.assertNull(wrappedReturn); + } else { + Assert.assertNotNull(wrappedReturn); + Assert.assertEquals(unwrappedReturn, wrappedReturn); + } + + if (unwrappedException == null) { + Assert.assertNull(wrappedException); + } else { + Assert.assertNotNull(wrappedException); + Assert.assertEquals(unwrappedException.getClass(), wrappedException.getClass()); + } + } + + + private InputStream getUnwrappedClosedInputStream() throws IOException { + File file = new File("test/webresources/non-static-resources.jar"); + JarFile jarFile = new JarFile(file); + ZipEntry jarEntry = jarFile.getEntry("META-INF/MANIFEST.MF"); + InputStream unwrapped = jarFile.getInputStream(jarEntry); + unwrapped.close(); + jarFile.close(); + return unwrapped; + } + + + private InputStream getWrappedClosedInputStream() throws IOException { + StandardRoot root = new StandardRoot(); + root.setCachingAllowed(false); + JarResourceSet jarResourceSet = + new JarResourceSet(root, "/", "test/webresources/non-static-resources.jar", "/"); + WebResource webResource = jarResourceSet.getResource("/META-INF/MANIFEST.MF"); + InputStream wrapped = webResource.getInputStream(); + wrapped.close(); + return wrapped; + } +} Propchange: tomcat/trunk/test/org/apache/catalina/webresources/TestJarInputStreamWrapper.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1771711&r1=1771710&r2=1771711&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Mon Nov 28 11:14:29 2016 @@ -132,6 +132,11 @@ Refactor the MBean implementations for the internal Tomcat components to reduce code duplication. (markt) </scode> + <fix> + <bug>60410</bug>: Ensure that multiple calls to + <code>JarInputStreamWrapper#close()</code> do not incorrectly trigger + the closure of the underlying JAR or WAR file. (markt) + </fix> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org