1717package org .apache .catalina .util ;
1818
1919
20+ import java .io .File ;
2021import java .io .InputStream ;
22+ import java .util .ArrayList ;
23+ import java .util .List ;
2124import java .util .Properties ;
25+ import java .util .jar .JarFile ;
26+ import java .util .jar .Manifest ;
2227
2328import org .apache .tomcat .util .ExceptionUtils ;
2429
@@ -121,6 +126,10 @@ public static String getServerNumber() {
121126 }
122127
123128 public static void main (String [] args ) {
129+ // Suppress INFO logging from library initialization
130+ java .util .logging .Logger .getLogger ("org.apache.tomcat.util.net.openssl.panama" ).setLevel (java .util .logging .Level .WARNING );
131+ java .util .logging .Logger .getLogger ("org.apache.catalina.core" ).setLevel (java .util .logging .Level .WARNING );
132+
124133 System .out .println ("Server version: " + getServerInfo ());
125134 System .out .println ("Server built: " + getServerBuilt ());
126135 System .out .println ("Server number: " + getServerNumber ());
@@ -129,6 +138,175 @@ public static void main(String[] args) {
129138 System .out .println ("Architecture: " + System .getProperty ("os.arch" ));
130139 System .out .println ("JVM Version: " + System .getProperty ("java.runtime.version" ));
131140 System .out .println ("JVM Vendor: " + System .getProperty ("java.vm.vendor" ));
141+
142+ // Get CATALINA_HOME for library scanning (already displayed in catalina script output preface)
143+ String catalinaHome = System .getProperty ("catalina.home" );
144+
145+ // Display APR/Tomcat Native information if available
146+ boolean aprLoaded = false ;
147+ try {
148+ // Try to initialize APR by creating an instance and calling isAprAvailable()
149+ // Creating an instance sets the instance flag which allows initialization
150+ Class <?> aprLifecycleListenerClass = Class .forName ("org.apache.catalina.core.AprLifecycleListener" );
151+ aprLifecycleListenerClass .getConstructor ().newInstance ();
152+ Boolean aprAvailable = (Boolean ) aprLifecycleListenerClass .getMethod ("isAprAvailable" ).invoke (null );
153+ if (aprAvailable != null && aprAvailable .booleanValue ()) {
154+ // APR is available, get version information using public methods
155+ String tcnVersion = (String ) aprLifecycleListenerClass .getMethod ("getInstalledTcnVersion" ).invoke (null );
156+ String aprVersion = (String ) aprLifecycleListenerClass .getMethod ("getInstalledAprVersion" ).invoke (null );
157+
158+ System .out .println ("APR loaded: true" );
159+ System .out .println ("APR Version: " + aprVersion );
160+ System .out .println ("Tomcat Native: " + tcnVersion );
161+ aprLoaded = true ;
162+
163+ // Check if installed version is older than recommended
164+ try {
165+ String warning = (String ) aprLifecycleListenerClass .getMethod ("getTcnVersionWarning" ).invoke (null );
166+
167+ if (warning != null ) {
168+ System .out .println (" " + warning );
169+ }
170+ } catch (Exception e ) {
171+ // Failed to check version - ignore
172+ }
173+
174+ // Display OpenSSL version if available
175+ try {
176+ String openSSLVersion = (String ) aprLifecycleListenerClass .getMethod ("getInstalledOpenSslVersion" ).invoke (null );
177+
178+ if (openSSLVersion != null && !openSSLVersion .isEmpty ()) {
179+ System .out .println ("OpenSSL (APR): " + openSSLVersion );
180+ }
181+ } catch (Exception e ) {
182+ // SSL not initialized or not available
183+ }
184+ }
185+ } catch (ClassNotFoundException | NoClassDefFoundError e ) {
186+ // APR/Tomcat Native classes not available on classpath
187+ } catch (Exception e ) {
188+ // Error checking APR status
189+ }
190+
191+ if (!aprLoaded ) {
192+ System .out .println ("APR loaded: false" );
193+ }
194+
195+ // Display FFM OpenSSL information if available
196+ try {
197+ // Try to initialize FFM OpenSSL by creating an instance and calling isAvailable()
198+ // Creating an instance sets the instance flag which allows initialization
199+ Class <?> openSSLLifecycleListenerClass = Class .forName ("org.apache.catalina.core.OpenSSLLifecycleListener" );
200+ openSSLLifecycleListenerClass .getConstructor ().newInstance ();
201+ Boolean ffmAvailable = (Boolean ) openSSLLifecycleListenerClass .getMethod ("isAvailable" ).invoke (null );
202+
203+ if (ffmAvailable != null && ffmAvailable .booleanValue ()) {
204+ // FFM OpenSSL is available, get version information using public method
205+ String versionString = (String ) openSSLLifecycleListenerClass .getMethod ("getInstalledOpenSslVersion" ).invoke (null );
206+
207+ if (versionString != null && !versionString .isEmpty ()) {
208+ System .out .println ("OpenSSL (FFM): " + versionString );
209+ }
210+ }
211+ } catch (ClassNotFoundException | NoClassDefFoundError e ) {
212+ // FFM OpenSSL classes not available on classpath
213+ } catch (Exception e ) {
214+ // Error checking FFM OpenSSL status
215+ }
216+
217+ // Display third-party libraries in CATALINA_HOME/lib
218+ if (catalinaHome != null ) {
219+ File libDir = new File (catalinaHome , "lib" );
220+ if (libDir .exists () && libDir .isDirectory ()) {
221+ File [] allJars = libDir .listFiles ((dir , name ) -> name .endsWith (".jar" ));
222+
223+ if (allJars != null && allJars .length > 0 ) {
224+ // First pass: collect third-party JARs and find longest name
225+ List <File > thirdPartyJars = new ArrayList <>();
226+ int maxNameLength = 0 ;
227+ for (File jar : allJars ) {
228+ if (!isTomcatCoreJar (jar )) {
229+ thirdPartyJars .add (jar );
230+ maxNameLength = Math .max (maxNameLength , jar .getName ().length ());
231+ }
232+ }
233+
234+ // Second pass: print with aligned formatting
235+ if (!thirdPartyJars .isEmpty ()) {
236+ System .out .println ();
237+ System .out .println ("Third-party libraries:" );
238+ for (File jar : thirdPartyJars ) {
239+ String version = getJarVersion (jar );
240+ String jarName = jar .getName ();
241+ // Colon right after name, then pad to align version numbers
242+ String nameWithColon = jarName + ":" ;
243+ String paddedName = String .format ("%-" + (maxNameLength + 1 ) + "s" , nameWithColon );
244+ if (version != null ) {
245+ System .out .println (" " + paddedName + " " + version );
246+ } else {
247+ System .out .println (" " + paddedName + " (unknown)" );
248+ }
249+ }
250+ }
251+ }
252+ }
253+ }
254+ }
255+
256+ private static boolean isTomcatCoreJar (File jarFile ) {
257+ try (JarFile jar = new JarFile (jarFile )) {
258+ Manifest manifest = jar .getManifest ();
259+
260+ if (manifest != null ) {
261+ // Check Bundle-SymbolicName to identify Tomcat core JARs
262+ String bundleName = manifest .getMainAttributes ().getValue ("Bundle-SymbolicName" );
263+ if (bundleName != null ) {
264+ // Tomcat core JARs have Bundle-SymbolicName starting with org.apache.tomcat,
265+ // org.apache.catalina, or jakarta.
266+ if (bundleName .startsWith ("org.apache.tomcat" ) ||
267+ bundleName .startsWith ("org.apache.catalina" ) ||
268+ bundleName .startsWith ("jakarta." )) {
269+ return true ;
270+ }
271+ }
272+
273+ // Fallback: Check Implementation-Vendor and Implementation-Title
274+ String implVendor = manifest .getMainAttributes ().getValue ("Implementation-Vendor" );
275+ String implTitle = manifest .getMainAttributes ().getValue ("Implementation-Title" );
276+
277+ if ("Apache Software Foundation" .equals (implVendor ) && "Apache Tomcat" .equals (implTitle )) {
278+ return true ;
279+ }
280+ }
281+ } catch (Exception e ) {
282+ // Ignore errors reading JAR manifest
283+ }
284+
285+ return false ;
286+ }
287+
288+ private static String getJarVersion (File jarFile ) {
289+ try (JarFile jar = new JarFile (jarFile )) {
290+ Manifest manifest = jar .getManifest ();
291+
292+ if (manifest != null ) {
293+ // Try different common version attributes
294+ String version = manifest .getMainAttributes ().getValue ("Bundle-Version" );
295+ if (version == null ) {
296+ version = manifest .getMainAttributes ().getValue ("Implementation-Version" );
297+ }
298+
299+ if (version == null ) {
300+ version = manifest .getMainAttributes ().getValue ("Specification-Version" );
301+ }
302+
303+ return version ;
304+ }
305+ } catch (Exception e ) {
306+ // Ignore errors reading JAR manifest
307+ }
308+
309+ return null ;
132310 }
133311
134312}
0 commit comments