From 4af8382f489e689fba8398286096f71d99276490 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 26 Oct 2026 13:29:42 -0700 Subject: [PATCH 1/3] Fix JSP recompilation on Tomcat 11.0.27 --- build.gradle | 2 - gradle.properties | 2 - .../tools/ant/taskdefs/LogOutputStream.java | 38 +++++++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 server/bootstrap/src/org/apache/tools/ant/taskdefs/LogOutputStream.java diff --git a/build.gradle b/build.gradle index 3d5fdf07d1..97221303be 100644 --- a/build.gradle +++ b/build.gradle @@ -276,8 +276,6 @@ allprojects { force "com.fasterxml.jackson.core:jackson-annotations:${jacksonAnnotationsVersion}" // genotyping brings in a much older version of this, so we force a newer version for compatibility force "org.apache.commons:commons-compress:${commonsCompressVersion}" - // Force ant to be a newer version (transitive dependency of query > eigenbase-resgen > eigenbase-xom) - force "org.apache.ant:ant:${antVersion}" // Transitive dependency of commons-compress -- Transitive dependency com.github.samtools:htsjdk which references an older version force "org.tukaani:xz:${tukaaniXZVersion}" // force version for api and LDK, but not for the xsdDoc configuration, which requires diff --git a/gradle.properties b/gradle.properties index 310fbbc761..0c0633c68f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -93,8 +93,6 @@ angusMailVersion=2.0.5 annotationsVersion=15.0 -antVersion=1.10.18 - antlrST4Version=4.3.4 antlr4RuntimeVersion=4.13.2 diff --git a/server/bootstrap/src/org/apache/tools/ant/taskdefs/LogOutputStream.java b/server/bootstrap/src/org/apache/tools/ant/taskdefs/LogOutputStream.java new file mode 100644 index 0000000000..ffc5f773ce --- /dev/null +++ b/server/bootstrap/src/org/apache/tools/ant/taskdefs/LogOutputStream.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026 LabKey Corporation + * + * Licensed 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.tools.ant.taskdefs; + +import org.apache.tools.ant.Task; + +import java.io.OutputStream; + +/** + * LabKey's stub version of ant's LogOutputStream. Tomcat's JspC requires this class; providing it and a few others in our + * bootstrap jar eliminates the need to distribute and deploy ant.jar into the /tomcat/lib directory. + */ +@SuppressWarnings("unused") +public class LogOutputStream extends OutputStream +{ + // JspC constructs this only when getProject() is non-null, which never happens with our Task stub + public LogOutputStream(Task task, int level) + { + } + + @Override + public void write(int b) + { + } +} From 8a38eaef5212a11ea596de771f3466df7f57eb09 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 26 Oct 2026 13:57:15 -0700 Subject: [PATCH 2/3] Update comments for accuracy --- server/bootstrap/src/org/apache/tools/ant/AntClassLoader.java | 2 +- server/bootstrap/src/org/apache/tools/ant/BuildException.java | 2 +- server/bootstrap/src/org/apache/tools/ant/Project.java | 2 +- server/bootstrap/src/org/apache/tools/ant/Task.java | 2 +- .../src/org/apache/tools/ant/taskdefs/LogOutputStream.java | 2 +- server/bootstrap/src/org/apache/tools/ant/util/FileUtils.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/server/bootstrap/src/org/apache/tools/ant/AntClassLoader.java b/server/bootstrap/src/org/apache/tools/ant/AntClassLoader.java index d9be964a2c..38c7c566a0 100644 --- a/server/bootstrap/src/org/apache/tools/ant/AntClassLoader.java +++ b/server/bootstrap/src/org/apache/tools/ant/AntClassLoader.java @@ -17,7 +17,7 @@ /** * LabKey's stub version of AntClassLoader. Tomcat's JspC requires this class; providing it and a few others in our - * bootstrap jar eliminates the need to distribute and deploy ant.jar into the /tomcat/lib directory. + * bootstrap jar eliminates the need to distribute ant.jar. */ @SuppressWarnings("unused") public class AntClassLoader extends ClassLoader diff --git a/server/bootstrap/src/org/apache/tools/ant/BuildException.java b/server/bootstrap/src/org/apache/tools/ant/BuildException.java index ec02903091..68c257bede 100644 --- a/server/bootstrap/src/org/apache/tools/ant/BuildException.java +++ b/server/bootstrap/src/org/apache/tools/ant/BuildException.java @@ -17,7 +17,7 @@ /** * LabKey's stub version of ant's BuildException. Tomcat's JspC requires this class; providing it and a few others in our - * bootstrap jar eliminates the need to distribute and deploy ant.jar into the /tomcat/lib directory. + * bootstrap jar eliminates the need to distribute ant.jar. */ @SuppressWarnings("unused") public class BuildException extends RuntimeException diff --git a/server/bootstrap/src/org/apache/tools/ant/Project.java b/server/bootstrap/src/org/apache/tools/ant/Project.java index ec75a221e3..a21e1bd7d0 100644 --- a/server/bootstrap/src/org/apache/tools/ant/Project.java +++ b/server/bootstrap/src/org/apache/tools/ant/Project.java @@ -17,7 +17,7 @@ /** * LabKey's stub version of ant's Project. Tomcat's JspC requires this class; providing it and a few others in our - * bootstrap jar eliminates the need to distribute and deploy ant.jar into the /tomcat/lib directory. + * bootstrap jar eliminates the need to distribute ant.jar. */ public class Project { diff --git a/server/bootstrap/src/org/apache/tools/ant/Task.java b/server/bootstrap/src/org/apache/tools/ant/Task.java index 75dfe245f8..58fda9fd51 100644 --- a/server/bootstrap/src/org/apache/tools/ant/Task.java +++ b/server/bootstrap/src/org/apache/tools/ant/Task.java @@ -17,7 +17,7 @@ /** * LabKey's stub version of ant's Task. Tomcat's JspC requires this class; providing it and a few others in our - * bootstrap jar eliminates the need to distribute and deploy ant.jar into the /tomcat/lib directory. + * bootstrap jar eliminates the need to distribute ant.jar. */ @SuppressWarnings("unused") public class Task diff --git a/server/bootstrap/src/org/apache/tools/ant/taskdefs/LogOutputStream.java b/server/bootstrap/src/org/apache/tools/ant/taskdefs/LogOutputStream.java index ffc5f773ce..ef69ef114a 100644 --- a/server/bootstrap/src/org/apache/tools/ant/taskdefs/LogOutputStream.java +++ b/server/bootstrap/src/org/apache/tools/ant/taskdefs/LogOutputStream.java @@ -21,7 +21,7 @@ /** * LabKey's stub version of ant's LogOutputStream. Tomcat's JspC requires this class; providing it and a few others in our - * bootstrap jar eliminates the need to distribute and deploy ant.jar into the /tomcat/lib directory. + * bootstrap jar eliminates the need to distribute ant.jar. */ @SuppressWarnings("unused") public class LogOutputStream extends OutputStream diff --git a/server/bootstrap/src/org/apache/tools/ant/util/FileUtils.java b/server/bootstrap/src/org/apache/tools/ant/util/FileUtils.java index f72dea3a0e..58925b94e1 100644 --- a/server/bootstrap/src/org/apache/tools/ant/util/FileUtils.java +++ b/server/bootstrap/src/org/apache/tools/ant/util/FileUtils.java @@ -27,7 +27,7 @@ /** * LabKey's stripped down version of ant's FileUtils. Tomcat's JspC requires this class; providing it and a few others in - * our bootstrap jar eliminates the need to distribute and deploy ant.jar into the /tomcat/lib directory. + * our bootstrap jar eliminates the need to distribute ant.jar. */ public class FileUtils { From 53a6cd4beed60b9cf561434cf4d3a4a1cc250dec Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 27 Oct 2026 10:50:16 -0700 Subject: [PATCH 3/3] Refresh FileUtils from latest Ant sources --- .../org/apache/tools/ant/util/FileUtils.java | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/server/bootstrap/src/org/apache/tools/ant/util/FileUtils.java b/server/bootstrap/src/org/apache/tools/ant/util/FileUtils.java index 58925b94e1..885dd0263d 100644 --- a/server/bootstrap/src/org/apache/tools/ant/util/FileUtils.java +++ b/server/bootstrap/src/org/apache/tools/ant/util/FileUtils.java @@ -28,6 +28,7 @@ /** * LabKey's stripped down version of ant's FileUtils. Tomcat's JspC requires this class; providing it and a few others in * our bootstrap jar eliminates the need to distribute ant.jar. + * Methods refreshed from Ant master (commit f4ca034) on 2026-10-27. */ public class FileUtils { @@ -36,9 +37,9 @@ public class FileUtils // This portion of FileUtils used org.apache.tools.ant.taskdefs.condition.Os to determine a bunch of OS system // constants, for use in subsequent code (e.g., resolveFile()). These simplified versions should be identical. private static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.US); - private static final String PATH_SEP = System.getProperty("path.separator"); - private static final boolean onNetWare = OS_NAME.contains("netware"); // Essentially what Os.isFamily("netware") does - private static final boolean onDos = PATH_SEP.equals(";") && !onNetWare; // Essentially what Os.isFamily("dos") does + private static final String PATH_SEP = File.pathSeparator; + private static final boolean ON_NETWARE = OS_NAME.contains("netware"); // Essentially what Os.isFamily("netware") does + private static final boolean ON_DOS = PATH_SEP.equals(";") && !ON_NETWARE; // Essentially what Os.isFamily("dos") does /** * Method to retrieve The FileUtils, which is shared by all users of this @@ -109,7 +110,7 @@ public File resolveFile(File file, String filename) { * @since Ant 1.7 */ public static boolean isContextRelativePath(String filename) { - if (!(onDos || onNetWare) || filename.isEmpty()) { + if (!(ON_DOS || ON_NETWARE) || filename.isEmpty()) { return false; } char sep = File.separatorChar; @@ -118,7 +119,7 @@ public static boolean isContextRelativePath(String filename) { int len = filename.length(); return (c == sep && (len == 1 || filename.charAt(1) != sep)) || (Character.isLetter(c) && len > 1 - && filename.indexOf(':') == 1 + && filename.charAt(1) == ':' && (len == 2 || filename.charAt(2) != sep)); } @@ -133,18 +134,20 @@ public static boolean isContextRelativePath(String filename) { * @since Ant 1.6.3 */ public static boolean isAbsolutePath(String filename) { - int len = filename.length(); - if (len == 0) { + if (filename.isEmpty()) { return false; } + if (!ON_DOS && !ON_NETWARE) { + // Not upstream's startsWithPathSeparator(), which wrongly accepts File.pathSeparatorChar (':' on Unix) + char c = filename.charAt(0); + return c == '/' || c == '\\'; + } + int len = filename.length(); char sep = File.separatorChar; filename = filename.replace('/', sep).replace('\\', sep); char c = filename.charAt(0); - if (!(onDos || onNetWare)) { - return (c == sep); - } if (c == sep) { - if (!(onDos && len > 4 && filename.charAt(1) == sep)) { + if (!ON_DOS || len <= 4 || filename.charAt(1) != sep) { return false; } int nextsep = filename.indexOf(sep, 2); @@ -153,7 +156,7 @@ public static boolean isAbsolutePath(String filename) { int colon = filename.indexOf(':'); return (Character.isLetter(c) && colon == 1 && filename.length() > 2 && filename.charAt(2) == sep) - || (onNetWare && colon > 0); + || (ON_NETWARE && colon > 0); } /** @@ -167,8 +170,12 @@ public static boolean isAbsolutePath(String filename) { *
  • DOS style paths that start with a drive letter will have * \ as the separator.
  • * - * Unlike {@link File#getCanonicalPath()} this method - * specifically does not resolve symbolic links. + *

    Unlike {@link File#getCanonicalPath()} this method + * specifically does not resolve symbolic links or Windows junctions.

    + * + *

    If the path tries to go beyond the file system root (i.e. it + * contains more ".." segments than can be travelled up) the + * method will return the original path unchanged.

    * * @param path the path to be normalized. * @return the normalized version of the path. @@ -176,7 +183,7 @@ public static boolean isAbsolutePath(String filename) { * @throws java.lang.NullPointerException if path is null. */ public File normalize(final String path) { - Stack s = new Stack(); + Stack s = new Stack<>(); String[] dissect = dissect(path); s.push(dissect[0]); @@ -185,7 +192,8 @@ public File normalize(final String path) { String thisToken = tok.nextToken(); if (".".equals(thisToken)) { continue; - } else if ("..".equals(thisToken)) { + } + if ("..".equals(thisToken)) { if (s.size() < 2) { // Cannot resolve it, so skip it. return new File(path); @@ -195,8 +203,9 @@ public File normalize(final String path) { s.push(thisToken); } } - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < s.size(); i++) { + StringBuilder sb = new StringBuilder(); + final int size = s.size(); + for (int i = 0; i < size; i++) { if (i > 1) { // not before the filesystem root and not after it, since root // already contains one @@ -224,7 +233,7 @@ public String[] dissect(String path) { } String root; int colon = path.indexOf(':'); - if (colon > 0 && (onDos || onNetWare)) { + if (colon > 0 && (ON_DOS || ON_NETWARE)) { int next = colon + 1; root = path.substring(0, next); @@ -233,7 +242,7 @@ public String[] dissect(String path) { //remove the initial separator; the root has it. next = (ca[next] == sep) ? next + 1 : next; - StringBuffer sbPath = new StringBuffer(); + final StringBuilder sbPath = new StringBuilder(); // Eliminate consecutive slashes after the drive spec: for (int i = next; i < ca.length; i++) { if (ca[i] != sep || ca[i - 1] != sep) {