From 2f7de7eebaeca17a10f7692b450ad8f2eaa620e7 Mon Sep 17 00:00:00 2001 From: Thomas Scheffler Date: Thu, 16 Jul 2026 18:01:49 +0200 Subject: [PATCH] MCR-3764 Warn about missing or deprecated classes in configuration --- .../common/events/MCRStartupHandler.java | 75 +++++++++++++++++++ .../main/resources/config/mycore.properties | 3 +- 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/mycore-base/src/main/java/org/mycore/common/events/MCRStartupHandler.java b/mycore-base/src/main/java/org/mycore/common/events/MCRStartupHandler.java index faaad89178..d20d3852f8 100644 --- a/mycore-base/src/main/java/org/mycore/common/events/MCRStartupHandler.java +++ b/mycore-base/src/main/java/org/mycore/common/events/MCRStartupHandler.java @@ -24,9 +24,15 @@ import java.io.IOException; import java.time.ZoneId; import java.time.format.DateTimeFormatter; +import java.util.Comparator; import java.util.List; import java.util.Locale; +import java.util.Objects; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.jar.JarFile; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -82,6 +88,18 @@ public static void startUp(ServletContext servletContext) { LOGGER.info("Library order: {}", () -> servletContext.getAttribute(ORDERED_LIBS)); } + if (MCRConfiguration2.getBoolean("MCR.Startup.CheckClassProperties").orElse(false)) { + SortedSet propertyClassStatuses = checkClassProperties(); + if (!propertyClassStatuses.isEmpty()) { + MCRTableMessage propertyTable = new MCRTableMessage<>( + new MCRTableMessage.Column<>("Property", PropertyClassStatus::property), + new MCRTableMessage.Column<>("Class", PropertyClassStatus::className), + new MCRTableMessage.Column<>("Status", PropertyClassStatus::status)); + propertyClassStatuses.forEach(propertyTable::add); + LOGGER.warn(() -> propertyTable.logMessage("Configured Classes report:")); + } + } + MCRTableMessage executableTable = new MCRTableMessage<>( new MCRTableMessage.Column<>("Name", AutoExecutable::getName), new MCRTableMessage.Column<>("Priority", AutoExecutable::getPriority), @@ -98,6 +116,34 @@ public static void startUp(ServletContext servletContext) { } + private static SortedSet checkClassProperties() { + Pattern classPattern = Pattern.compile(MCRConfiguration2.getString("MCR.Startup.CheckClassRegEx").orElse("^$")); + return MCRConfiguration2.getPropertiesMap() + .entrySet() + .stream() + .filter(entry -> isClassProperty(entry.getKey(), entry.getValue(), classPattern)) + .flatMap(entry -> MCRConfiguration2.splitValue(entry.getValue()) + .map(value -> new PropertyClassStatus(entry.getKey(), value, null))) + .filter(pcs -> !pcs.className.isBlank()) + .map(pcs -> { + try { + Class aClass = Class.forName(pcs.className(), false, MCRClassTools.getClassLoader()); + if (aClass.getAnnotation(Deprecated.class) != null) { + return new PropertyClassStatus(pcs.property(), pcs.className(), ClassStatus.DEPRECATED); + } + } catch (ClassNotFoundException | LinkageError e) { + return new PropertyClassStatus(pcs.property(), pcs.className(), ClassStatus.NOT_FOUND); + } + return null; + }) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(TreeSet::new)); + } + + private static boolean isClassProperty(String key, String value, Pattern classPattern) { + return key.endsWith(".Class") || classPattern.matcher(value).find(); + } + private static String toType(MCRComponent component) { if (component.isMyCoReBaseComponent()) { return "MyCoRe base component"; @@ -168,4 +214,33 @@ default int compareTo(AutoExecutable other) { } } + + private enum ClassStatus { + NOT_FOUND("not found"), DEPRECATED("deprecated"); + + private final String tableValue; + + ClassStatus(String tableValue) { + this.tableValue = tableValue; + } + + @Override + public String toString() { + return tableValue; + } + } + + private record PropertyClassStatus(String property, String className, ClassStatus status) + implements Comparable { + + private static final Comparator COMPARATOR = + Comparator.comparing(PropertyClassStatus::status) + .thenComparing(PropertyClassStatus::property) + .thenComparing(PropertyClassStatus::className); + + @Override + public int compareTo(PropertyClassStatus other) { + return COMPARATOR.compare(this, other); + } + } } diff --git a/mycore-base/src/main/resources/config/mycore.properties b/mycore-base/src/main/resources/config/mycore.properties index 7d6d2a7f5e..7e414e84df 100644 --- a/mycore-base/src/main/resources/config/mycore.properties +++ b/mycore-base/src/main/resources/config/mycore.properties @@ -584,7 +584,8 @@ MCR.Jersey.Resource.ApplicationPaths=rsc ############################################################################## MCR.Startup.Class=org.mycore.backend.jpa.MCRJPAConfigurationCheck,org.mycore.backend.jpa.MCRJPABootstrapper,org.mycore.datamodel.niofs.MCRFileSystemPromoter,org.mycore.frontend.support.MCRAutoDeploy,org.mycore.frontend.fileupload.MCRUploadServletDeployer,org.mycore.frontend.jersey.MCRJWTUtil - +MCR.Startup.CheckClassProperties=true +MCR.Startup.CheckClassRegEx=(?:^|,\\s*)org\\.mycore(\\.[a-z0-9]+)*\\.[A-Z] ############################################################################## # Website # ##############################################################################