}
os.write(buf, 0, n);
}
+ resourceFile.setLastModified(
+ jarEntry2.getTime());
} catch (IOException e) {
// Ignore
} finally {
return jspUri;
}
+ /**
+ * @deprecated Will be removed in Tomcat 8.0.x. Use
+ * {@link #getLastModified(String)} instead.
+ */
+ @Deprecated
public long getJspLastModified() {
long result = -1;
URLConnection uc = null;
return result;
}
+
+ public Long getLastModified(String resource) {
+ long result = -1;
+ URLConnection uc = null;
+ try {
+ URL jspUrl = getResource(resource);
+ if (jspUrl == null) {
+ incrementRemoved();
+ return Long.valueOf(result);
+ }
+ uc = jspUrl.openConnection();
+ if (uc instanceof JarURLConnection) {
+ result = ((JarURLConnection) uc).getJarEntry().getTime();
+ } else {
+ result = uc.getLastModified();
+ }
+ } catch (IOException e) {
+ if (log.isDebugEnabled()) {
+ log.debug(Localizer.getMessage(
+ "jsp.error.lastModified", getJspFile()), e);
+ }
+ result = -1;
+ } finally {
+ if (uc != null) {
+ try {
+ uc.getInputStream().close();
+ } catch (IOException e) {
+ if (log.isDebugEnabled()) {
+ log.debug(Localizer.getMessage(
+ "jsp.error.lastModified", getJspFile()), e);
+ }
+ result = -1;
+ }
+ }
+ }
+ return Long.valueOf(result);
+ }
+
public boolean isTagFile() {
return isTagFile;
}
import java.net.URL;
import java.net.URLConnection;
import java.util.Iterator;
-import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import org.apache.jasper.JasperException;
import org.apache.jasper.JspCompilationContext;
try {
String[] smap = generateJava();
+ File javaFile = new File(ctxt.getServletJavaFileName());
+ Long jspLastModified = ctxt.getLastModified(ctxt.getJspFile());
+ javaFile.setLastModified(jspLastModified.longValue());
if (compileClass) {
generateClass(smap);
// Fix for bugzilla 41606
String targetFileName = ctxt.getClassFileName();
if (targetFileName != null) {
File targetFile = new File(targetFileName);
- if (targetFile.exists() && jsw != null) {
- jsw.setServletClassLastModifiedTime(targetFile.lastModified());
+ if (targetFile.exists()) {
+ targetFile.setLastModified(jspLastModified.longValue());
+ if (jsw != null) {
+ jsw.setServletClassLastModifiedTime(
+ jspLastModified.longValue());
+ }
}
}
}
jsw.setLastModificationTest(System.currentTimeMillis());
}
- long jspRealLastModified = ctxt.getJspLastModified();
- if (jspRealLastModified < 0) {
+ Long jspRealLastModified = ctxt.getLastModified(ctxt.getJspFile());
+ if (jspRealLastModified.longValue() < 0) {
// Something went wrong - assume modification
return true;
}
if (checkClass && jsw != null) {
jsw.setServletClassLastModifiedTime(targetLastModified);
}
- if (targetLastModified < jspRealLastModified) {
+ if (targetLastModified != jspRealLastModified.longValue()) {
if (log.isDebugEnabled()) {
log.debug("Compiler: outdated: " + targetFile + " "
+ targetLastModified);
return false;
}
- List<String> depends = jsw.getDependants();
+ Map<String,Long> depends = jsw.getDependants();
if (depends == null) {
return false;
}
- Iterator<String> it = depends.iterator();
+ Iterator<Entry<String,Long>> it = depends.entrySet().iterator();
while (it.hasNext()) {
- String include = it.next();
+ Entry<String,Long> include = it.next();
try {
- URL includeUrl = ctxt.getResource(include);
+ URL includeUrl = ctxt.getResource(include.getKey());
if (includeUrl == null) {
return true;
}
}
iuc.getInputStream().close();
- if (includeLastModified > targetLastModified) {
+ if (includeLastModified != include.getValue().longValue()) {
return true;
}
} catch (Exception e) {
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.Vector;
out.println();
// Static data for getDependants()
- out.printil("private static java.util.List<java.lang.String> _jspx_dependants;");
+ out.printil("private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;");
out.println();
- List<String> dependants = pageInfo.getDependants();
- Iterator<String> iter = dependants.iterator();
+ Map<String,Long> dependants = pageInfo.getDependants();
+ Iterator<Entry<String,Long>> iter = dependants.entrySet().iterator();
if (!dependants.isEmpty()) {
out.printil("static {");
out.pushIndent();
- out.printin("_jspx_dependants = new java.util.ArrayList<java.lang.String>(");
+ out.printin("_jspx_dependants = new java.util.HashMap<java.lang.String,java.lang.Long>(");
out.print("" + dependants.size());
out.println(");");
while (iter.hasNext()) {
- out.printin("_jspx_dependants.add(\"");
- out.print(iter.next());
- out.println("\");");
+ Entry<String,Long> entry = iter.next();
+ out.printin("_jspx_dependants.put(\"");
+ out.print(entry.getKey());
+ out.print("\", Long.valueOf(");
+ out.print(entry.getValue().toString());
+ out.println("L));");
}
out.popIndent();
out.printil("}");
*/
private void genPreambleMethods() {
// Method used to get compile time file dependencies
- out.printil("public java.util.List<java.lang.String> getDependants() {");
+ out.printil("public java.util.Map<java.lang.String,java.lang.Long> getDependants() {");
out.pushIndent();
out.printil("return _jspx_dependants;");
out.popIndent();
out.println(" * Version: " + ctxt.getServletContext().getServerInfo());
out.println(" * Generated at: " + timestampFormat.format(new Date()) +
" UTC");
+ out.println(" * Note: The last modified time of this file was set to");
+ out.println(" * the last modified time of the source file after");
+ out.println(" * generation to assist with modification tracking.");
out.println(" */");
}
// Add implicit TLD to dependency list
if (pi != null) {
- pi.addDependant(path);
+ pi.addDependant(path, ctxt.getLastModified(path));
}
ParserUtils pu = new ParserUtils();
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.Vector;
class PageInfo {
private Vector<String> imports;
- private Vector<String> dependants;
+ private Map<String,Long> dependants;
private BeanRepository beanRepository;
private Set<String> varInfoNames;
this.xmlPrefixMapper = new HashMap<String, LinkedList<String>>();
this.nonCustomTagPrefixMap = new HashMap<String, Mark>();
this.imports = new Vector<String>();
- this.dependants = new Vector<String>();
+ this.dependants = new HashMap<String,Long>();
this.includePrelude = new Vector<String>();
this.includeCoda = new Vector<String>();
this.pluginDcls = new Vector<String>();
return jspFile;
}
- public void addDependant(String d) {
- if (!dependants.contains(d) && !jspFile.equals(d))
- dependants.add(d);
+ public void addDependant(String d, Long lastModified) {
+ if (!dependants.containsKey(d) && !jspFile.equals(d))
+ dependants.put(d, lastModified);
}
- public List<String> getDependants() {
+ public Map<String,Long> getDependants() {
return dependants;
}
if (parent != null) {
// Included resource, add to dependent list
if (jarFile == null) {
- compiler.getPageInfo().addDependant(absFileName);
+ compiler.getPageInfo().addDependant(absFileName,
+ ctxt.getLastModified(absFileName));
} else {
+ String entry = absFileName.substring(1);
compiler.getPageInfo().addDependant(
- jarResource.getEntry(absFileName.substring(1)).toString());
+ jarResource.getEntry(entry).toString(),
+ Long.valueOf(jarFile.getEntry(entry).getTime()));
}
}
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.Map.Entry;
import java.util.Vector;
import javax.el.MethodExpression;
try {
Object tagIns = tagClazz.newInstance();
if (tagIns instanceof JspSourceDependent) {
- Iterator<String> iter = ((JspSourceDependent) tagIns)
- .getDependants().iterator();
+ Iterator<Entry<String,Long>> iter = ((JspSourceDependent)
+ tagIns).getDependants().entrySet().iterator();
while (iter.hasNext()) {
- parentPageInfo.addDependant(iter.next());
+ Entry<String,Long> entry = iter.next();
+ parentPageInfo.addDependant(entry.getKey(),
+ entry.getValue());
}
}
} catch (Exception e) {
tagFileInfo.getTagInfo().getTagLibrary().getURI());
JarResource jarResource = location.getJarResource();
if (jarResource != null) {
- // Add TLD
- pageInfo.addDependant(jarResource.getEntry(location.getName()).toString());
- // Add Tag
- pageInfo.addDependant(jarResource.getEntry(tagFilePath.substring(1)).toString());
+ try {
+ // Add TLD
+ pageInfo.addDependant(jarResource.getEntry(location.getName()).toString(),
+ Long.valueOf(jarResource.getJarFile().getEntry(location.getName()).getTime()));
+ // Add Tag
+ pageInfo.addDependant(jarResource.getEntry(tagFilePath.substring(1)).toString(),
+ Long.valueOf(jarResource.getJarFile().getEntry(tagFilePath.substring(1)).getTime()));
+ } catch (IOException ioe) {
+ throw new JasperException(ioe);
+ }
}
else {
- pageInfo.addDependant(tagFilePath);
+ pageInfo.addDependant(tagFilePath,
+ compiler.getCompilationContext().getLastModified(
+ tagFilePath));
}
} else {
- pageInfo.addDependant(tagFilePath);
+ pageInfo.addDependant(tagFilePath,
+ compiler.getCompilationContext().getLastModified(
+ tagFilePath));
}
Class<?> c = loadTagFile(compiler, tagFilePath, n.getTagInfo(),
pageInfo);
// Add TLD to dependency list
PageInfo pageInfo = ctxt.createCompiler().getPageInfo();
if (pageInfo != null) {
- pageInfo.addDependant(tldName);
+ pageInfo.addDependant(tldName,
+ ctxt.getLastModified(tldName));
}
} else {
// Tag library is packaged in JAR file
package org.apache.jasper.runtime;
-import java.util.List;
+import java.util.Map;
/**
* Interface for tracking the source files dependencies, for the purpose
public interface JspSourceDependent {
/**
- * Returns a list of files names that the current page has a source
- * dependency on.
+ * Returns a map of file names and last modified time where the current page
+ * has a source dependency on the file.
*/
- public List<String> getDependants();
+ public Map<String,Long> getDependants();
}
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
@SuppressWarnings("deprecation") // Have to support SingleThreadModel
public class JspServletWrapper {
+ private static final Map<String,Long> ALWAYS_OUTDATED_DEPENDENCIES =
+ new HashMap<String,Long>();
+
+ static {
+ // If this is missing,
+ ALWAYS_OUTDATED_DEPENDENCIES.put("/WEB-INF/web.xml", Long.valueOf(-1));
+ }
+
// Logger
private final Log log = LogFactory.getLog(JspServletWrapper.class);
/**
* Get a list of files that the current page has source dependency on.
*/
- public java.util.List<String> getDependants() {
+ public java.util.Map<String,Long> getDependants() {
try {
Object target;
if (isTagFile) {
if (target != null && target instanceof JspSourceDependent) {
return ((JspSourceDependent) target).getDependants();
}
+ } catch (AbstractMethodError ame) {
+ // Almost certainly a pre Tomcat 7.0.17 compiled JSP using the old
+ // version of the interface. Force a re-compile.
+ return ALWAYS_OUTDATED_DEPENDENCIES;
} catch (Throwable ex) {
ExceptionUtils.handleThrowable(ex);
}
Improve the message printed by TldLocationsCache and add configuration
example to the <code>logging.properties</code> file. (kkolinko)
</update>
+ <fix>
+ <bug>33453</bug>: Recompile JSPs if last modified time of the source or
+ any of its dependencies changes either forwards or backwards. Note that
+ this introduces an incompatible change to the code generated for JSPs.
+ Tomcat will automatically re-compile any JSPs and tag files found in the
+ work directory when upgrading from 7.0.16 or earlier to 7.0.17 or later.
+ If you later downgrade from 7.0.17 or later to 7.0.16 or earlier, you
+ must empty the work directory as part of the downgrade process. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Cluster">