Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
When generating JSP runtime error messages that quote the relevant JSP source code, switch from using the results of the JSP page parsing process to using the JSR 045 source map data to identify the correct part of the JSP source from the stack trace. This significantly reduces the memory footprint of Jasper in development mode, provides a small performance improvement for error page generation and enables source quotes to continue to be provided after a Tomcat restart.

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1800201 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
markt-asf committed Jun 28, 2017
1 parent 66c4b57 commit 93ae8bd
Show file tree
Hide file tree
Showing 14 changed files with 389 additions and 229 deletions.
2 changes: 1 addition & 1 deletion java/org/apache/jasper/JspC.java
Expand Up @@ -1252,7 +1252,7 @@ protected void processFile(String file)
targetClassName = null;
}
if (targetPackage != null) {
clctxt.setServletPackageName(targetPackage);
clctxt.setBasePackageName(targetPackage);
}

originalClassLoader = Thread.currentThread().getContextClassLoader();
Expand Down
16 changes: 12 additions & 4 deletions java/org/apache/jasper/JspCompilationContext.java
Expand Up @@ -445,7 +445,7 @@ public void setPrototypeMode(boolean pm) {
}

/**
* Package name for the generated class is make up of the base package
* Package name for the generated class is made up of the base package
* name, which is user settable, and the derived package name. The
* derived package name directly mirrors the file hierarchy of the JSP page.
* @return the package name
Expand Down Expand Up @@ -477,12 +477,20 @@ protected String getDerivedPackageName() {
return derivedPackageName;
}

/**
* @return The base package name into which all servlet and associated code
* is generated
*/
public String getBasePackageName() {
return basePackageName;
}

/**
* The package name into which the servlet class is generated.
* @param servletPackageName The package name to use
* @param basePackageName The package name to use
*/
public void setServletPackageName(String servletPackageName) {
this.basePackageName = servletPackageName;
public void setBasePackageName(String basePackageName) {
this.basePackageName = basePackageName;
}

/**
Expand Down
5 changes: 3 additions & 2 deletions java/org/apache/jasper/compiler/AntCompiler.java
Expand Up @@ -21,6 +21,7 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Map;
import java.util.StringTokenizer;

import org.apache.jasper.Constants;
Expand Down Expand Up @@ -117,7 +118,7 @@ protected String getReport() {
* Compile the servlet from .java file to .class file
*/
@Override
protected void generateClass(String[] smap)
protected void generateClass(Map<String,SmapStratum> smaps)
throws FileNotFoundException, JasperException, Exception {

long t1 = 0;
Expand Down Expand Up @@ -279,7 +280,7 @@ protected void generateClass(String[] smap)

// JSR45 Support
if (!options.isSmapSuppressed()) {
SmapUtil.installSmap(smap);
SmapUtil.installSmap(smaps);
}
}

Expand Down
61 changes: 31 additions & 30 deletions java/org/apache/jasper/compiler/Compiler.java
Expand Up @@ -80,29 +80,30 @@ public void init(JspCompilationContext ctxt, JspServletWrapper jsw) {

// --------------------------------------------------------- Public Methods

/**
* <p>
* Retrieves the parsed nodes of the JSP page, if they are available. May
* return null. Used in development mode for generating detailed error
* messages. http://bz.apache.org/bugzilla/show_bug.cgi?id=37062.
* </p>
* @return the page nodes
*/
public Node.Nodes getPageNodes() {
return this.pageNodes;
public SmapStratum getSmap(String className) {

Map<String,SmapStratum> smaps = ctxt.getRuntimeContext().getSmaps();
SmapStratum smap = smaps.get(className);

if (smap == null && !options.isSmapSuppressed()) {
// Tomcat was restarted so cached SMAP has been lost. However, it
// was written to the class file so it can be recovered.
smap = SmapUtil.loadSmap(className, ctxt.getJspLoader());
if (smap != null) {
smaps.put(className, smap);
}
}

return smap;
}


/**
* Compile the jsp file into equivalent servlet in .java file
*
* @return a smap for the current JSP page, if one is generated, null
* otherwise
* @throws Exception Error generating Java source
*/
protected String[] generateJava() throws Exception {

String[] smapStr = null;
protected Map<String,SmapStratum> generateJava() throws Exception {

long t1, t2, t3, t4;

Expand Down Expand Up @@ -212,7 +213,6 @@ protected String[] generateJava() throws Exception {
// generate prototype .java file for the tag file
try (ServletWriter writer = setupContextWriter(javaFileName)) {
Generator.generate(writer, this, pageNodes);
return null;
}
}

Expand Down Expand Up @@ -278,9 +278,14 @@ protected String[] generateJava() throws Exception {
throw e;
}

Map<String,SmapStratum> smaps = null;

// JSR45 Support
if (!options.isSmapSuppressed()) {
smapStr = SmapUtil.generateSmap(ctxt, pageNodes);
smaps = SmapUtil.generateSmap(ctxt, pageNodes);
// Add them to the web application wide cache for future lookup in
// error handling etc.
ctxt.getRuntimeContext().getSmaps().putAll(smaps);
}

// If any proto type .java and .class files was generated,
Expand All @@ -290,7 +295,7 @@ protected String[] generateJava() throws Exception {
// generate .class again from the new .java file just generated.
tfp.removeProtoTypeFiles(ctxt.getClassFileName());

return smapStr;
return smaps;
}

private ServletWriter setupContextWriter(String javaFileName)
Expand All @@ -316,12 +321,15 @@ private ServletWriter setupContextWriter(String javaFileName)
/**
* Servlet compilation. This compiles the generated sources into
* Servlets.
* @param smap The SMAP files for source debugging
*
* @param smaps The source maps for the class(es) generated from the source
* file
*
* @throws FileNotFoundException Source files not found
* @throws JasperException Compilation error
* @throws Exception Some other error
*/
protected abstract void generateClass(String[] smap)
protected abstract void generateClass(Map<String,SmapStratum> smaps)
throws FileNotFoundException, JasperException, Exception;

/**
Expand Down Expand Up @@ -371,12 +379,12 @@ public void compile(boolean compileClass, boolean jspcMode)
}

try {
String[] smap = generateJava();
Map<String,SmapStratum> smaps = generateJava();
File javaFile = new File(ctxt.getServletJavaFileName());
Long jspLastModified = ctxt.getLastModified(ctxt.getJspFile());
javaFile.setLastModified(jspLastModified.longValue());
if (compileClass) {
generateClass(smap);
generateClass(smaps);
// Fix for bugzilla 41606
// Set JspServletWrapper.servletClassLastModifiedTime after successful compile
File targetFile = new File(ctxt.getClassFileName());
Expand All @@ -399,14 +407,7 @@ public void compile(boolean compileClass, boolean jspcMode)
tfp = null;
errDispatcher = null;
pageInfo = null;

// Only get rid of the pageNodes if in production.
// In development mode, they are used for detailed
// error messages.
// http://bz.apache.org/bugzilla/show_bug.cgi?id=37062
if (!this.options.getDevelopment()) {
pageNodes = null;
}
pageNodes = null;

if (ctxt.getWriter() != null) {
ctxt.getWriter().close();
Expand Down
4 changes: 2 additions & 2 deletions java/org/apache/jasper/compiler/JDTCompiler.java
Expand Up @@ -69,7 +69,7 @@ public class JDTCompiler extends org.apache.jasper.compiler.Compiler {
* Compile the servlet from .java file to .class file
*/
@Override
protected void generateClass(String[] smap)
protected void generateClass(Map<String,SmapStratum> smaps)
throws FileNotFoundException, JasperException, Exception {

long t1 = 0;
Expand Down Expand Up @@ -470,7 +470,7 @@ public void acceptResult(CompilationResult result) {

// JSR45 Support
if (! options.isSmapSuppressed()) {
SmapUtil.installSmap(smap);
SmapUtil.installSmap(smaps);
}
}
}
14 changes: 13 additions & 1 deletion java/org/apache/jasper/compiler/JspRuntimeContext.java
Expand Up @@ -169,6 +169,14 @@ public JspRuntimeContext(ServletContext context, Options options) {
*/
private FastRemovalDequeue<JspServletWrapper> jspQueue = null;

/**
* Map of class name to associated source map. This is maintained here as
* multiple JSPs can depend on the same file (included JSP, tag file, etc.)
* so a web application scoped Map is required.
*/
private final Map<String,SmapStratum> smaps = new ConcurrentHashMap<>();


// ------------------------------------------------------ Public Methods

/**
Expand Down Expand Up @@ -389,9 +397,13 @@ public long getLastJspQueueUpdate() {
}


// -------------------------------------------------------- Private Methods
public Map<String,SmapStratum> getSmaps() {
return smaps;
}


// -------------------------------------------------------- Private Methods

/**
* Method used to initialize classpath for compiles.
* @return the compilation classpath
Expand Down
102 changes: 0 additions & 102 deletions java/org/apache/jasper/compiler/SmapGenerator.java

This file was deleted.

39 changes: 39 additions & 0 deletions java/org/apache/jasper/compiler/SmapInput.java
@@ -0,0 +1,39 @@
/*
* 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.jasper.compiler;

public class SmapInput {

private final String fileName;
private final int lineNumber;


public SmapInput(String fileName, int lineNumber) {
this.fileName = fileName;
this.lineNumber = lineNumber;
}


public String getFileName() {
return fileName;
}


public int getLineNumber() {
return lineNumber;
}
}

0 comments on commit 93ae8bd

Please sign in to comment.