diff --git a/core/src/main/java/com/opensymphony/xwork2/util/fs/JarEntryRevision.java b/core/src/main/java/com/opensymphony/xwork2/util/fs/JarEntryRevision.java index 4b962af08d..268710fb83 100644 --- a/core/src/main/java/com/opensymphony/xwork2/util/fs/JarEntryRevision.java +++ b/core/src/main/java/com/opensymphony/xwork2/util/fs/JarEntryRevision.java @@ -3,12 +3,10 @@ import com.opensymphony.xwork2.FileManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; -import org.apache.commons.io.FileUtils; import java.io.IOException; +import java.net.JarURLConnection; import java.net.URL; -import java.util.jar.JarFile; -import java.util.zip.ZipEntry; /** * Represents jar resource revision, used for jar://* resource @@ -17,66 +15,63 @@ public class JarEntryRevision extends Revision { private static Logger LOG = LogManager.getLogger(JarEntryRevision.class); - private static final String JAR_FILE_NAME_SEPARATOR = "!/"; - private static final String JAR_FILE_EXTENSION_END = ".jar/"; - - private String jarFileName; - private String fileNameInJar; + private URL jarFileURL; private long lastModified; public static Revision build(URL fileUrl, FileManager fileManager) { // File within a Jar // Find separator index of jar filename and filename within jar - String jarFileName = ""; + JarURLConnection conn = null; try { - String fileName = fileUrl.toString(); - int separatorIndex = fileName.indexOf(JAR_FILE_NAME_SEPARATOR); - if (separatorIndex == -1) { - separatorIndex = fileName.lastIndexOf(JAR_FILE_EXTENSION_END); - } - if (separatorIndex == -1) { - LOG.warn("Could not find end of jar file!"); - return null; - } - - // Split file name - jarFileName = fileName.substring(0, separatorIndex); - int index = separatorIndex + JAR_FILE_NAME_SEPARATOR.length(); - String fileNameInJar = fileName.substring(index).replaceAll("%20", " "); - + conn = (JarURLConnection) fileUrl.openConnection(); + conn.setUseCaches(false); URL url = fileManager.normalizeToFileProtocol(fileUrl); if (url != null) { - JarFile jarFile = new JarFile(FileUtils.toFile(url)); - ZipEntry entry = jarFile.getEntry(fileNameInJar); - return new JarEntryRevision(jarFileName, fileNameInJar, entry.getTime()); + return new JarEntryRevision(fileUrl, conn.getJarEntry().getTime()); } else { return null; } } catch (Throwable e) { - LOG.warn("Could not create JarEntryRevision for [{}]!", jarFileName, e); + LOG.warn("Could not create JarEntryRevision for [{}]!", fileUrl, e); return null; } + finally { + if(null != conn) { + try { + conn.getInputStream().close(); + } catch (IOException ignored) { + } + } + } } - private JarEntryRevision(String jarFileName, String fileNameInJar, long lastModified) { - if ((jarFileName == null) || (fileNameInJar == null)) { - throw new IllegalArgumentException("JarFileName and FileNameInJar cannot be null"); + private JarEntryRevision(URL jarFileURL, long lastModified) { + if (jarFileURL == null) { + throw new IllegalArgumentException("jarFileURL cannot be null"); } - this.jarFileName = jarFileName; - this.fileNameInJar = fileNameInJar; + this.jarFileURL = jarFileURL; this.lastModified = lastModified; } public boolean needsReloading() { - ZipEntry entry; + JarURLConnection conn = null; + long lastLastModified = lastModified; try { - JarFile jarFile = new JarFile(this.jarFileName); - entry = jarFile.getEntry(this.fileNameInJar); - } catch (IOException e) { - entry = null; + conn = (JarURLConnection) jarFileURL.openConnection(); + conn.setUseCaches(false); + lastLastModified = conn.getJarEntry().getTime(); + } catch (IOException ignored) { + } + finally { + if(null != conn) { + try { + conn.getInputStream().close(); + } catch (IOException ignored) { + } + } } - return entry != null && (lastModified < entry.getTime()); + return lastModified < lastLastModified; } } diff --git a/core/src/test/java/com/opensymphony/xwork2/util/fs/JarEntryRevisionTest.java b/core/src/test/java/com/opensymphony/xwork2/util/fs/JarEntryRevisionTest.java new file mode 100644 index 0000000000..04826d502d --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/util/fs/JarEntryRevisionTest.java @@ -0,0 +1,54 @@ +package com.opensymphony.xwork2.util.fs; + +import com.opensymphony.xwork2.FileManager; +import com.opensymphony.xwork2.FileManagerFactory; +import com.opensymphony.xwork2.XWorkTestCase; +import org.apache.commons.io.IOUtils; + +import java.io.FileOutputStream; +import java.io.InputStream; +import java.net.URL; +import java.util.jar.Attributes; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; + +public class JarEntryRevisionTest extends XWorkTestCase { + + private FileManager fileManager; + + @Override + public void setUp() throws Exception { + super.setUp(); + fileManager = container.getInstance(FileManagerFactory.class).getFileManager(); + } + + private void createJarFile(long time) throws Exception { + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + FileOutputStream fos = new FileOutputStream("target/JarEntryRevisionTest_testNeedsReloading.jar", false); + JarOutputStream target = new JarOutputStream(fos, manifest); + target.putNextEntry(new ZipEntry("com/opensymphony/xwork2/util/fs/")); + ZipEntry entry = new ZipEntry("com/opensymphony/xwork2/util/fs/JarEntryRevisionTest.class"); + entry.setTime(time); + target.putNextEntry(entry); + InputStream source = getClass().getResourceAsStream("/com/opensymphony/xwork2/util/fs/JarEntryRevisionTest.class"); + IOUtils.copy(source, target); + source.close(); + target.closeEntry(); + target.close(); + fos.close(); + } + + public void testNeedsReloading() throws Exception { + long now = System.currentTimeMillis(); + + createJarFile(now); + URL url = new URL("jar:file:target/JarEntryRevisionTest_testNeedsReloading.jar!/com/opensymphony/xwork2/util/fs/JarEntryRevisionTest.class"); + Revision entry = JarEntryRevision.build(url, fileManager); + assertFalse(entry.needsReloading()); + + createJarFile(now + 60000); + assertTrue(entry.needsReloading()); + } +}