Hello, I have a problem with Expires and Last-Modified headers for classpath resources inside JBoss/Tomcat.
It has seen here before: http://www.mail-archive.com/[email protected]/msg27853.html http://markmail.org/message/uwfozi756vfxd264 I think its a very important issue given the performance optimization expected by: https://issues.apache.org/jira/browse/TAPESTRY-2159 - YSlow Recommendation: Version bundled javascript and use far-future expires header <https://issues.apache.org/jira/browse/TAPESTRY-2159> I tried to write a test for this: ====================================================== package org.apache.tapestry5.internal.util; ... public class URLChangeTrackerTest extends TapestryTestCase { ... @Test public void lastmodified_of_jar_url_should_not_be_zero() throws Exception { URLChangeTracker t = new URLChangeTracker(converter, true); URL jarFileURL = new URL("file:" + this.getClass().getResource("url.jar").getFile()); URLClassLoader classLoader = new URLClassLoader(new URL[]{jarFileURL}); URL url = classLoader.getResource(classLoader.getResource("url/change/tracker/Foo.txt").toString()); assertTrue(t.add(url) > 0); } ... ====================================================== This tests PASSED. The error only occurs for URLs supplied by WebAppClassloader. Only inside JBoss/Tomcat. ====================================================== package org.apache.tapestry5.ioc.internal.util; ... public final class ClasspathResource extends AbstractResource { ... public synchronized URL toURL() { if (!urlResolved) { *// For JBoss/Tomcat classLoader is an instance of org.apache.catalina.loader.WebappClassLoader* url = classLoader.getResource(getPath()); urlResolved = true; } return url; } ... ====================================================== The proposed FIX by Geoff makes sense given that we can have distinct "lastmodified" dates for entries in a JAR. And it fixes the problem inside JBoss/Tomcat even I think they have a BUG because JarURLConnection should give the "lastmodified" date of the JAR. Follow some detail. Regards, Krishna ====================================================== public class ResourceStreamerImpl implements ResourceStreamer { ... public void streamResource(Resource resource) throws IOException { ... StreamableResource streamble = resourceCache.getStreamableResource(resource); * long lastModified = streamble.getLastModified(); response.setDateHeader("Last-Modified", lastModified); response.setDateHeader("Expires", lastModified + InternalConstants.TEN_YEARS);* ... ====================================================== package org.apache.tapestry5.internal.util; ... public class URLChangeTracker { ... public long add(URL url) { if (url == null) return 0; URL converted = classpathURLConverter.convert(url); if (!converted.getProtocol().equals("file")) return timestampForNonFileURL(converted); ... private long timestampForNonFileURL(URL url) { long timestamp; try { *// Inside JBoss/Tomcat its returning 0 for JarURLConnection* timestamp = url.openConnection().getLastModified(); } catch (IOException ex) { throw new RuntimeException(ex); } return applyGranularity(timestamp); } ... ====================================================== package java.net; ... public abstract class URLConnection { ... /** * Returns the value of the <code>last-modified</code> header field. * The result is the number of milliseconds since January 1, 1970 GMT. * * @return the date the resource referenced by this * <code>URLConnection</code> was last modified, or 0 if not known. * @see java.net.URLConnection#getHeaderField(java.lang.String) */ public long getLastModified() { *// No header fields for JarURLConnection (extends this not overidding this method) inside JBoss/Tomcat.* return getHeaderFieldDate("last-modified", 0); } ... ======================================================
