Author: markt Date: Mon Jul 2 19:47:37 2018 New Revision: 1834877 URL: http://svn.apache.org/viewvc?rev=1834877&view=rev Log: Make the Java file generation process multi-threaded. By default, one thread will be used per core. Based on a patch by Dan Fabulich.
Modified: tomcat/trunk/java/org/apache/jasper/JspC.java tomcat/trunk/java/org/apache/jasper/resources/LocalStrings.properties tomcat/trunk/webapps/docs/changelog.xml Modified: tomcat/trunk/java/org/apache/jasper/JspC.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/JspC.java?rev=1834877&r1=1834876&r2=1834877&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/jasper/JspC.java (original) +++ tomcat/trunk/java/org/apache/jasper/JspC.java Mon Jul 2 19:47:37 2018 @@ -40,6 +40,11 @@ import java.util.Set; import java.util.Stack; import java.util.StringTokenizer; import java.util.Vector; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import javax.servlet.jsp.JspFactory; import javax.servlet.jsp.tagext.TagLibraryInfo; @@ -140,6 +145,7 @@ public class JspC extends Task implement protected static final String SWITCH_NO_STRICT_QUOTE_ESCAPING = "-no-strictQuoteEscaping"; protected static final String SWITCH_QUOTE_ATTRIBUTE_EL = "-quoteAttributeEL"; protected static final String SWITCH_NO_QUOTE_ATTRIBUTE_EL = "-no-quoteAttributeEL"; + protected static final String SWITCH_THREAD_COUNT = "-threadCount"; protected static final String SHOW_SUCCESS ="-s"; protected static final String LIST_ERRORS = "-l"; protected static final int INC_WEBXML = 10; @@ -235,6 +241,9 @@ public class JspC extends Task implement */ protected String javaEncoding = "UTF-8"; + /** The number of threads to use; default is one per core */ + protected int threadCount = Runtime.getRuntime().availableProcessors(); + // Generation of web.xml fragments protected String webxmlFile; protected int webxmlLevel; @@ -414,6 +423,8 @@ public class JspC extends Task implement setQuoteAttributeEL(true); } else if (tok.equals(SWITCH_NO_QUOTE_ATTRIBUTE_EL)) { setQuoteAttributeEL(false); + } else if (tok.equals(SWITCH_THREAD_COUNT)) { + setThreadCount(nextArg()); } else { if (tok.startsWith("-")) { throw new JasperException("Unrecognized option: " + tok + @@ -970,6 +981,31 @@ public class JspC extends Task implement return quoteAttributeEL; } + public int getThreadCount() { + return threadCount; + } + + public void setThreadCount(String threadCount) { + if (threadCount == null) { + return; + } + int newThreadCount; + try { + if (threadCount.endsWith("C")) { + double factor = Double.parseDouble(threadCount.substring(0, threadCount.length() - 1)); + newThreadCount = (int) (factor * Runtime.getRuntime().availableProcessors()); + } else { + newThreadCount = Integer.parseInt(threadCount); + } + } catch (NumberFormatException e) { + throw new BuildException("Couldn't parse thread count: " + threadCount); + } + if (newThreadCount < 1) { + throw new BuildException("There must be at least one thread: " + newThreadCount); + } + this.threadCount = newThreadCount; + } + public void setListErrors( boolean b ) { listErrors = b; } @@ -1273,9 +1309,8 @@ public class JspC extends Task implement return result.toString(); } - protected void processFile(String file) - throws JasperException - { + protected void processFile(String file) throws JasperException { + if (log.isDebugEnabled()) { log.debug("Processing file: " + file); } @@ -1406,29 +1441,25 @@ public class JspC extends Task implement try { if (uriRoot == null) { - if( pages.size() == 0 ) { - throw new JasperException( - Localizer.getMessage("jsp.error.jspc.missingTarget")); + if (pages.size() == 0) { + throw new JasperException(Localizer.getMessage("jsp.error.jspc.missingTarget")); } - String firstJsp = pages.get( 0 ); - File firstJspF = new File( firstJsp ); + String firstJsp = pages.get(0); + File firstJspF = new File(firstJsp); if (!firstJspF.exists()) { - throw new JasperException( - Localizer.getMessage("jspc.error.fileDoesNotExist", - firstJsp)); + throw new JasperException(Localizer.getMessage( + "jspc.error.fileDoesNotExist", firstJsp)); } - locateUriRoot( firstJspF ); + locateUriRoot(firstJspF); } if (uriRoot == null) { - throw new JasperException( - Localizer.getMessage("jsp.error.jspc.no_uriroot")); + throw new JasperException(Localizer.getMessage("jsp.error.jspc.no_uriroot")); } File uriRootF = new File(uriRoot); if (!uriRootF.isDirectory()) { - throw new JasperException( - Localizer.getMessage("jsp.error.jspc.uriroot_not_dir")); + throw new JasperException(Localizer.getMessage("jsp.error.jspc.uriroot_not_dir")); } if (loader == null) { @@ -1448,6 +1479,10 @@ public class JspC extends Task implement int errorCount = 0; long start = System.currentTimeMillis(); + ExecutorService threadPool = Executors.newFixedThreadPool(threadCount); + ExecutorCompletionService<Void> service = new ExecutorCompletionService<>(threadPool); + int pageCount = pages.size(); + for (String nextjsp : pages) { File fjsp = new File(nextjsp); if (!fjsp.isAbsolute()) { @@ -1455,9 +1490,8 @@ public class JspC extends Task implement } if (!fjsp.exists()) { if (log.isWarnEnabled()) { - log.warn - (Localizer.getMessage - ("jspc.error.fileDoesNotExist", fjsp.toString())); + log.warn(Localizer.getMessage( + "jspc.error.fileDoesNotExist", fjsp.toString())); } continue; } @@ -1468,19 +1502,38 @@ public class JspC extends Task implement if (nextjsp.startsWith("." + File.separatorChar)) { nextjsp = nextjsp.substring(2); } + service.submit(new ProcessFile(nextjsp)); + } + JasperException reportableError = null; + for (int i = 0; i < pageCount; i++) { try { - processFile(nextjsp); - } catch (JasperException e) { + service.take().get(); + } catch (ExecutionException e) { if (failFast) { - throw e; + // Generation is not interruptible so any tasks that + // have started will complete. + List<Runnable> notExecuted = threadPool.shutdownNow(); + i += notExecuted.size(); + Throwable t = e.getCause(); + if (t instanceof JasperException) { + reportableError = (JasperException) t; + } else { + reportableError = new JasperException(t); + } + } else { + errorCount++; + log.error(e.getMessage()); } - errorCount++; - log.error(nextjsp + ":" + e.getMessage()); + } catch (InterruptedException e) { + // Ignore } } + if (reportableError != null) { + throw reportableError; + } long time = System.currentTimeMillis() - start; - String msg = Localizer.getMessage("jspc.compilation.result", + String msg = Localizer.getMessage("jspc.generation.result", Integer.toString(errorCount), Long.toString(time)); if (failOnError && errorCount > 0) { System.out.println("Error Count: " + errorCount); @@ -1801,4 +1854,19 @@ public class JspC extends Task implement throw ex; } } + + + private class ProcessFile implements Callable<Void> { + private final String file; + + private ProcessFile(String file) { + this.file = file; + } + + @Override + public Void call() throws Exception { + processFile(file); + return null; + } + } } Modified: tomcat/trunk/java/org/apache/jasper/resources/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/resources/LocalStrings.properties?rev=1834877&r1=1834876&r2=1834877&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/jasper/resources/LocalStrings.properties (original) +++ tomcat/trunk/java/org/apache/jasper/resources/LocalStrings.properties Mon Jul 2 19:47:37 2018 @@ -153,7 +153,7 @@ jsp.warning.compiler.javafile.delete.fai jsp.error.jspc.uriroot_not_dir=The -uriroot option must specify a pre-existing directory jsp.error.jspc.missingTarget=Missing target: Must specify -webapp or -uriroot, or one or more JSP pages jsp.error.jspc.no_uriroot=The uriroot is not specified and cannot be located with the specified JSP file(s) -jspc.compilation.result=Compilation completed with [{0}] errors in [{1}] milliseconds +jspc.generation.result=Generation completed with [{0}] errors in [{1}] milliseconds jspc.implicit.uriRoot=uriRoot implicitly set to [{0}] jspc.usage=Usage: jspc <options> [--] <jsp files>\n\ where jsp files is\n\ @@ -191,6 +191,8 @@ where options include:\n\ \ -javaEncoding <enc> Set the encoding charset for Java classes (default UTF-8)\n\ \ -source <version> Set the -source argument to the compiler (default 1.8)\n\ \ -target <version> Set the -target argument to the compiler (default 1.8)\n\ +\ -threadCount <count> Number of threads to use for compilation.\n\ +\ (\"2.0C\" means two threads per core)\n\ jspc.webxml.header=<?xml version="1.0" encoding="{0}"?>\n\ <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1834877&r1=1834876&r2=1834877&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Mon Jul 2 19:47:37 2018 @@ -81,6 +81,15 @@ </update> </changelog> </subsection> + <subsection name="Jasper"> + <changelog> + <add> + <bug>53492</bug>: Make the Java file generation process multi-threaded. + By default, one thread will be used per core. Based on a patch by Dan + Fabulich. (markt) + </add> + </changelog> + </subsection> <subsection name="Other"> <changelog> <update> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org