diff --git a/apps/i2ptunnel/java/build.xml b/apps/i2ptunnel/java/build.xml
index 04e7908398..6c8dee9cd7 100644
--- a/apps/i2ptunnel/java/build.xml
+++ b/apps/i2ptunnel/java/build.xml
@@ -307,6 +307,10 @@
+
+
+
+
diff --git a/apps/jetty/java/src/net/i2p/servlet/util/JspC.java b/apps/jetty/java/src/net/i2p/servlet/util/JspC.java
index 02b7040b33..1543bbea47 100644
--- a/apps/jetty/java/src/net/i2p/servlet/util/JspC.java
+++ b/apps/jetty/java/src/net/i2p/servlet/util/JspC.java
@@ -1,16 +1,35 @@
package net.i2p.servlet.util;
+import java.io.File;
+import java.io.InputStream;
+import java.io.IOException;
import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import net.i2p.util.FileSuffixFilter;
+import net.i2p.util.VersionComparator;
/**
* Simply call org.apache.jasper.JspC, then exit.
*
* As of Tomcat 8.5.33, forking their JspC won't complete,
* because the JspC compilation is now threaded and the thread pool workers aren't daemons.
- * May be fixed in a future release, maybe not, but we don't know what version distros may have.
+ * Will fixed in a 8.5.35, but we don't know what version distros may have.
+ *
+ * Additionally, if the system property build.reproducible is "true",
+ * attempts to generate a reproducible build by compiling the
+ * jsps in order, for a consistent web.xml file.
*
* https://tomcat.apache.org/tomcat-8.5-doc/changelog.html
* https://bz.apache.org/bugzilla/show_bug.cgi?id=53492
+ * http://trac.i2p2.i2p/ticket/2307
+ * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=908884
+ * https://bz.apache.org/bugzilla/show_bug.cgi?id=62674
*
* We could set fork=false in build.xml, but then the paths are all wrong.
* Only for use in build scripts, obviously not a public API.
@@ -19,7 +38,21 @@ import java.lang.reflect.Method;
* @since 0.9.37
*/
public class JspC {
+ // First Tomcat version to support multiple threads and -threadCount arg
+ private static final String THREADS_VERSION = "8.5.33";
+ // if true, try to make web.xml reproducible
+ private static final boolean REPRODUCIBLE = Boolean.valueOf(System.getProperty("build.reproducible"));
+ // if true, we must get the Tomcat version out of the jasper jar's manifest
+ private static final boolean SYSTEM_TOMCAT = Boolean.valueOf(System.getProperty("with-libtomcat8-java"));
+ // path to the jasper jar
+ private static final String JASPER_JAR = System.getProperty("jasper.jar");
+
+ /**
+ * @throws IllegalArgumentException
+ */
public static void main(String args[]) {
+ if (REPRODUCIBLE)
+ args = fixupArgs(args);
try {
String cls = "org.apache.jasper.JspC";
Class> c = Class.forName(cls, true, ClassLoader.getSystemClassLoader());
@@ -31,4 +64,93 @@ public class JspC {
System.exit(1);
}
}
+
+ /**
+ * Only call this if we want reproducible builds.
+ *
+ * Convert "-webapp dir/" arguments in the args to
+ * a sorted list of files, for reproducible builds.
+ */
+ private static String[] fixupArgs(String[] args) {
+ List largs = new ArrayList(32);
+
+ // change the webapp arg to uriroot, save the location
+ String sdir = null;
+ for (int i = 0; i < args.length; i++) {
+ String a = args[i];
+ if (a.equals("-webapp")) {
+ i++;
+ if (i >= args.length)
+ throw new IllegalArgumentException("no value for -webapp");
+ if (sdir != null)
+ throw new IllegalArgumentException("multiple -webapp args");
+ sdir = args[i];
+ largs.add("-uriroot");
+ largs.add(sdir);
+ } else {
+ largs.add(a);
+ }
+ }
+ if (sdir == null)
+ return args;
+ File dir = new File(sdir);
+ if (!dir.exists())
+ throw new IllegalArgumentException("webapp dir does not exist: " + sdir);
+ if (!dir.isDirectory())
+ throw new IllegalArgumentException("not a directory: " + sdir);
+
+ // If JspC supports the -threadCount argument, add it to force one thread.
+ boolean supportsThreads = false;
+ if (SYSTEM_TOMCAT) {
+ if (JASPER_JAR != null) {
+ // The JASPER_JAR property is a symlink to /usr/share/java/tomcat8-jasper.jar,
+ // pull the version out of its manifest.
+ Attributes atts = attributes(JASPER_JAR);
+ if (atts != null) {
+ String ver = atts.getValue("Implementation-Version");
+ if (ver != null && ver.startsWith("8.")) {
+ supportsThreads = VersionComparator.comp(ver, THREADS_VERSION) >= 0;
+ System.out.println("Found JspC version: " + ver + ", supports threads? " + supportsThreads);
+ }
+ }
+ }
+ } else {
+ // We bundle 8.5.34+
+ supportsThreads = true;
+ }
+ if (supportsThreads) {
+ largs.add("-threadCount");
+ largs.add("1");
+ }
+
+ // add all the files as individual args
+ File[] files = dir.listFiles(new FileSuffixFilter(".jsp"));
+ if (files == null || files.length == 0)
+ throw new IllegalArgumentException("no jsp files in webapp dir: " + sdir);
+ Arrays.sort(files);
+ for (int i = 0; i < files.length; i++) {
+ largs.add(files[i].getName());
+ }
+ System.out.println("JspC arguments for reproducible build: " + largs);
+ String[] rv = new String[largs.size()];
+ rv = largs.toArray(rv);
+ return rv;
+ }
+
+ /**
+ * jar manifest attributes
+ * @return null if not found
+ */
+ private static Attributes attributes(String f) {
+ InputStream in = null;
+ try {
+ in = (new URL("jar:file:" + f + "!/META-INF/MANIFEST.MF")).openStream();
+ Manifest man = new Manifest(in);
+ return man.getMainAttributes();
+ } catch (IOException ioe) {
+ return null;
+ } finally {
+ if (in != null) try { in.close(); } catch (IOException e) {}
+ }
+ }
}
diff --git a/apps/routerconsole/java/build.xml b/apps/routerconsole/java/build.xml
index 294023a981..974b948dd8 100644
--- a/apps/routerconsole/java/build.xml
+++ b/apps/routerconsole/java/build.xml
@@ -401,16 +401,23 @@
**
** As of Tomcat 8.5.33, forking their JspC won't complete,
** because the JspC compilation is now threaded and the thread pool workers aren't daemons.
- ** May be fixed in a future release, maybe not, but we don't know what version distros may have.
+ ** Will be fixed in 8.5.35, but we don't know what version distros may have.
**
** https://tomcat.apache.org/tomcat-8.5-doc/changelog.html
** https://bz.apache.org/bugzilla/show_bug.cgi?id=53492
+ ** http://trac.i2p2.i2p/ticket/2307
+ ** https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=908884
+ ** https://bz.apache.org/bugzilla/show_bug.cgi?id=62674
**
** We could set fork=false in build.xml, but then the paths are all wrong.
-->
+
+
+
+
diff --git a/apps/susidns/src/build.xml b/apps/susidns/src/build.xml
index 0eaac0d4d7..c2bf84e020 100644
--- a/apps/susidns/src/build.xml
+++ b/apps/susidns/src/build.xml
@@ -72,6 +72,10 @@
+
+
+
+
diff --git a/history.txt b/history.txt
index a932c289f7..043a642075 100644
--- a/history.txt
+++ b/history.txt
@@ -1,3 +1,6 @@
+2018-09-24 zzz
+ * Build: Compile jsps in-order for reproducibility (ticket #2279)
+
2018-09-23 zzz
* Plugins: Blacklist neodatis and seedless for Java 9+ (ticket #2295)
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index ae4c8ac3a4..c76c0f106b 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
- public final static long BUILD = 6;
+ public final static long BUILD = 7;
/** for example "-test" */
public final static String EXTRA = "-rc";