001/* 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2026, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v2.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014 015package ch.qos.logback.core.util; 016 017import ch.qos.logback.core.Context; 018import ch.qos.logback.core.status.InfoStatus; 019import ch.qos.logback.core.status.WarnStatus; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.lang.module.ModuleDescriptor; 024import java.util.Optional; 025import java.util.Properties; 026 027import static ch.qos.logback.core.CoreConstants.NA; 028 029// depender depends on dependency 030 031// dependency synonym dependee (only use dependency) 032// depender synonym dependent (only use depender) 033 034/** 035 * Utility class for handling and validating version information of various artifacts. 036 * 037 * <p>It is used by logback-classic, logback-access-common, logback-access-jetty11, logback-access-tomcat, etc. 038 * to alert users about version discrepancies between depender and dependency artifacts. 039 * </p> 040 * 041 * @since 1.5.25 042 */ 043public class VersionUtil { 044 045 /** 046 * @since 1.5.30 047 */ 048 Context context; 049 050 /** 051 * Instance methods allow for polymorphism, static methods do not. 052 * 053 * 054 * @param context 055 * @since 1.5.30 056 */ 057 protected VersionUtil(Context context) { 058 this.context = context; 059 } 060 061 /** 062 * Retrieves the version of an artifact, such as logback-core.jar, logback-access-common.jar etc. 063 * 064 * <p>The aClass parameter is assumed to be part of the artifact. 065 * </p> 066 * 067 * <p>The method first attempts to get the version from the module information. If the module version 068 * is not available, it falls back to retrieving the implementation version from the package. 069 * </p> 070 * 071 * @param aClass the class from which to retrieve the version information 072 * @return the version of the artifact where aClass is found, or null if the version cannot be determined 073 * @deprecated 074 */ 075 static public String getVersionOfArtifact(Class<?> aClass) { 076 String moduleVersion = getVersionOfClassByModule(aClass); 077 if (moduleVersion != null) 078 return moduleVersion; 079 080 Package pkg = aClass.getPackage(); 081 if (pkg == null) { 082 return null; 083 } 084 return pkg.getImplementationVersion(); 085 } 086 087 static public String nonNull(String input) { 088 if (input == null) { 089 return NA; 090 } else { 091 return input; 092 } 093 } 094 095 /** 096 * Retrieves the version of an artifact from the artifact's module metadata. 097 * 098 * <p>If the module or its descriptor does not provide a version, the method returns null. 099 * </p> 100 * 101 * @param aClass a class from which to retrieve the version information 102 * @return the version of class' module as a string, or null if the version cannot be determined 103 */ 104 static private String getVersionOfClassByModule(Class<?> aClass) { 105 Module module = aClass.getModule(); 106 if (module == null) 107 return null; 108 109 ModuleDescriptor md = module.getDescriptor(); 110 if (md == null) 111 return null; 112 Optional<String> opt = md.rawVersion(); 113 return opt.orElse(null); 114 } 115 116 protected String getExpectedVersionOfDependencyByProperties(Class<?> dependerClass, String propertiesFileName, String dependencyNameAsKey) { 117 // derived classes should override 118 return null; 119 } 120 121 122 /** 123 * Compares the versions of a depender and a dependency to determine if they are equal. 124 * Updates the context's status manager with version information and logs a warning 125 * if the versions differ. 126 * 127 * @since 1.5.26 128 */ 129 static public void checkForVersionEquality(Context context, String dependerVersion, String dependencyVersion, String dependerName, String dependencyName) { 130 // the depender depends on the dependency 131 addFoundVersionStatus(context, dependerName, dependerVersion); 132 133 dependerVersion = nonNull(dependerVersion); 134 135 if (dependerVersion.equals(NA) || !dependerVersion.equals(dependencyVersion)) { 136 addFoundVersionStatus(context, dependencyName, dependencyVersion); 137 String discrepancyMsg = String.format("Versions of %s and %s are different or unknown.", dependencyName, dependerVersion); 138 context.getStatusManager().add(new WarnStatus(discrepancyMsg, context)); 139 } 140 } 141 142 143 protected static void addFoundVersionStatus(Context context, String name, String version) { 144 String foundDependent = String.format("Found %s version %s", name, nonNull(version)); 145 context.getStatusManager().add(new InfoStatus(foundDependent, context)); 146 } 147 148 protected static String nameToPropertiesFilename(String name) { 149 return name + "-dependencies.properties"; 150 } 151 152 /** 153 * Compares the expected version of a dependency with the actual version found and updates the status context. 154 * If the versions do not match, a warning is added to the context's status manager. 155 * 156 * <p>Note: This method is used be logback-access-jetty11/12 and logback-access-tomcat.</p> 157 * 158 */ 159 public void compareExpectedAndFoundVersion(String actualDependencyVersion, Class<?> dependerClass, String dependerVersion, 160 String dependerName, String dependencyName) { 161 162 String propertiesFileName = nameToPropertiesFilename(dependerName); 163 164 String expectedDependencyVersion = this.getExpectedVersionOfDependencyByProperties(dependerClass, propertiesFileName, dependencyName); 165 String safeExpectedDependencyVersion = nonNull(expectedDependencyVersion); 166 167 addFoundVersionStatus(context, dependencyName, actualDependencyVersion); 168 addFoundVersionStatus(context, dependerName, dependerVersion); 169 170 if (!safeExpectedDependencyVersion.equals(actualDependencyVersion)) { 171 String discrepancyMsg = String.format("For %s, expected version %s but found %s", dependencyName, safeExpectedDependencyVersion, actualDependencyVersion); 172 context.getStatusManager().add(new WarnStatus(discrepancyMsg, context)); 173 } 174 } 175}