Skip to content

Conversation

@csutherl
Copy link
Member

Change Summary

This changeset enhances the output version.sh and version.bat to display comprehensive dependency and library version information that is available to tomcat for use (read: not configured in the server.xml), making it easier to verify which versions are available without the potentially costly action of grepping the logs after the fact. The update allows a single command to retrieve all the important version information a user may need without starting tomcat. The output is also an automation friendly parseable output for scripts and monitoring tools, and allows for a quick inventory of libraries that could be used by the instance.

I've added six public methods total across three classes to expose version info and reduce the need for as much reflection, introduced 14 new tests which covers all of the changes in ServerInfo, and ensured that the change is compatible with Java 8+ and ready for backporting all the way to Tomcat 9.0.x with no changes required.

Files Modified

  1. bin/catalina.sh and bin/catalina.bat

    • Updated classpath for version command to include:
      • tomcat-juli.jar (for logging support)
      • All JARs in $CATALINA_HOME/lib/* (for APR/FFM classes)
    • Added -Dcatalina.home and -Dcatalina.base system properties to have access to third-party jars
    • Note:These changes are minimal impact as they only affects version command, not normal startup
  2. java/org/apache/catalina/core/AprLifecycleListener.java

    • Added minimal public API (4 methods) to expose version information, per markt's suggestion on the dev-list thread:
      • getInstalledTcnVersion() - Returns Tomcat Native version string
      • getInstalledAprVersion() - Returns APR version string
      • getInstalledOpenSslVersion() - Returns OpenSSL version string (via APR)
      • getTcnVersionWarning() - Returns version warning if installed version is outdated
    • Encapsulates version comparison logic internally (no need to expose individual version components)
  3. java/org/apache/catalina/core/OpenSSLLifecycleListener.java

    • Added public static method getInstalledOpenSslVersion() to expose FFM OpenSSL version
    • Uses reflection to call OpenSSLLibrary.getVersionString() for native version string to avoid compile time deps
  4. java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java

    • Added public static method getVersionString() to expose native OpenSSL version string
    • Ensures FFM output format matches APR format for consistency
  5. java/org/apache/catalina/util/ServerInfo.java

    • Added proactive Tomcat Native and APR initialization and version detection
    • Added Tomcat Native version warning when installed version is older than recommended
    • Added proactive FFM OpenSSL initialization and version detection
    • Added third-party library scanning with manifest-based version extraction
    • Implemented manifest-based filtering to distinguish Tomcat core JARs from third-party libraries
    • Suppressed INFO logging for o.a.c.core and o.a.t.u.n.openssl.panama during initialization for clean output
    • Added well formatted, aligned output for third-party libraries
    • Uses public methods from listener classes instead of reflection (reduced reflection usage)
    • Uses getConstructor().newInstance() instead of Java 9+ getDeclaredConstructor() for Java 8 compat
  6. test/org/apache/catalina/util/TestServerInfo.java

    • Added 14 new tests bringing the total coverage up to 19 tests (may be a bit overkill, but good practice and it runs in 100ms or so)
    • Tests for manifest-based JAR filtering (isTomcatCoreJar())
    • Tests for version extraction (getJarVersion())
    • Tests for APR detection and version output
    • Tests for Tomcat Native version warning with real installed version
    • Tests for FFM OpenSSL detection and version output
    • Added helper method captureServerInfoOutput() to eliminate test code duplication
    • Uses Consumer<Manifest> for test JAR creation (Java 8 compatible)
    • Tests use Assume.assumeTrue() to skip gracefully when native libraries unavailable
  7. webapps/docs/changelog.xml

    • Added changelog entry documenting the enhancement

Example Output

Before:

Server version: Apache Tomcat/12.0.0-M1-dev
Server built:   Oct 22 2025 17:47:46 UTC
Server number:  12.0.0.0
OS Name:        Linux
OS Version:     6.17.4-200.fc42.x86_64
Architecture:   amd64
JVM Version:    25+36
JVM Vendor:     Red Hat, Inc.

After:

Server version: Apache Tomcat/12.0.0-M1-dev
Server built:   Oct 22 2025 17:47:46 UTC
Server number:  12.0.0.0
OS Name:        Linux
OS Version:     6.17.4-200.fc42.x86_64
Architecture:   amd64
JVM Version:    25+36
JVM Vendor:     Red Hat, Inc.
APR loaded:     true
APR Version:    1.7.6
Tomcat Native:  1.3.0
                WARNING: Tomcat recommends a minimum version of 2.0.5
OpenSSL (APR):  OpenSSL 3.2.6 30 Sep 2025
OpenSSL (FFM):  OpenSSL 3.2.6 30 Sep 2025

Third-party libraries:
  ecj-4.37.jar:                                    3.43.0.v20250819-1513
  test.jar:                                           (unknown)
  jakartaee-migration-1.0.9-shaded.jar:  1.0.9

New Information in Output

  1. APR and Tomcat Native Detection (when available)

    • APR loaded status
    • APR version
    • Tomcat Native version with version compatibility warning (displayed when installed version is older than recommended)
    • OpenSSL version (via APR)
  2. FFM OpenSSL Detection (when available)

    • OpenSSL library name and version (via FFM API)
  3. Third-Party Libraries

    • Automatic detection of non-Tomcat JARs in lib/
    • Version information extracted from JAR manifests
      • Shows "(unknown)" for JARs without version metadata
    • Clean, aligned formatting
    • Includes libraries like: ECJ compiler, JDBC drivers, migration tools, etc.


eval "\"$_RUNJAVA\"" "$JAVA_OPTS" \
-classpath "\"$CATALINA_HOME/lib/catalina.jar\"" \
-classpath "\"$CATALINA_HOME/bin/tomcat-juli.jar:$CATALINA_HOME/lib/*\"" \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes also the boot classpath...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on why you think that? The change was made in the version command's classpath which doesn't affect tomcat startup since the start command uses $CLASSPATH. Am I missing something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, you didn't miss anything. I expect that this change was deliberate. Ideally that would inlude rather the classpath from catalina.properties to be consistent. But if you think this is enough, I am fine with that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I could use that I guess. I only added what was needed for the new logic though. Let's leave this open and see if others have an opinion since you're neutral. Thanks for the feedback.

@csutherl csutherl force-pushed the version-output-enhancements branch from cea11af to 6c55a57 Compare November 13, 2025 14:08
@csutherl
Copy link
Member Author

Force pushed to fix checkstyle failings since validate is off by default :(

TCN_RECOMMENDED_MAJOR + "." + TCN_RECOMMENDED_MINOR + "." + TCN_RECOMMENDED_PV;
}
return null;
} catch (Exception e) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe logging a warning here to determine where the exception came from is helpful, but theoretically it should never happen.

if (JreCompat.isJre22Available()) {
try {
Class<?> openSSLLibraryClass =
Class.forName("org.apache.tomcat.util.net.openssl.panama.OpenSSLLibrary");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I consider making the forName and getMethod calls use a variable rather than a string to make it easier to update if the classes or packages change? Or are these internal APIs generally not changed?

return false;
}

private static String getJarVersion(File jarFile) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could probably optimize this method a bit and use a loop rather than the two ifs to make it look a bit nicer. Thoughts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants