1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.common.config;
20
21 import java.io.File;
22 import java.lang.reflect.InaccessibleObjectException;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.net.URI;
26 import java.net.URL;
27 import java.net.URLClassLoader;
28 import java.util.Locale;
29 import java.util.Map;
30 import java.util.Objects;
31 import java.util.Optional;
32 import java.util.Set;
33 import java.util.function.BiConsumer;
34 import java.util.function.Function;
35 import java.util.stream.Collectors;
36 import java.util.stream.Stream;
37
38 import org.apache.logging.log4j.LogManager;
39 import org.apache.logging.log4j.core.LoggerContext;
40 import org.apache.logging.log4j.status.StatusLogger;
41 import org.apache.logging.log4j.web.Log4jServletContainerInitializer;
42 import org.mycore.common.MCRClassTools;
43 import org.mycore.common.MCRSessionMgr;
44 import org.mycore.common.events.MCRStartupHandler;
45 import org.mycore.common.events.MCRStartupHandler.AutoExecutable;
46 import org.mycore.common.log4j2.MCRSessionThreadContext;
47
48 import jakarta.servlet.ServletContext;
49 import jakarta.servlet.ServletException;
50
51
52
53
54
55
56
57 public class MCRConfigurationDirSetup implements AutoExecutable {
58
59 private static final StatusLogger LOGGER = StatusLogger.getLogger();
60
61 public static void loadExternalLibs() {
62 File resourceDir = MCRConfigurationDir.getConfigFile("resources");
63 if (resourceDir == null) {
64
65 return;
66 }
67 Optional<URLClassLoader> classLoaderOptional = Stream
68 .of(MCRClassTools.getClassLoader(), Thread.currentThread().getContextClassLoader())
69 .filter(URLClassLoader.class::isInstance)
70 .map(URLClassLoader.class::cast)
71 .findFirst();
72 if (classLoaderOptional.isEmpty()) {
73 System.err
74 .println(classLoaderOptional.getClass() + " is unsupported for adding extending CLASSPATH at runtime.");
75 return;
76 }
77 File libDir = MCRConfigurationDir.getConfigFile("lib");
78 URLClassLoader urlClassLoader = classLoaderOptional.get();
79 Set<URL> currentCPElements = Stream.of(urlClassLoader.getURLs()).collect(Collectors.toSet());
80 try {
81 Class<? extends ClassLoader> classLoaderClass = urlClassLoader.getClass();
82 BiConsumer<ClassLoader, URL> addUrlMethod = addToClassPath(classLoaderClass);
83 getFileStream(resourceDir, libDir)
84 .filter(Objects::nonNull)
85 .map(File::toURI)
86 .map(u -> {
87 try {
88 return u.toURL();
89 } catch (Exception e) {
90
91 return null;
92 }
93 })
94 .filter(Objects::nonNull)
95 .filter(u -> !currentCPElements.contains(u))
96 .peek(u -> System.out.println("Adding to CLASSPATH: " + u))
97 .forEach(url -> addUrlMethod.accept(urlClassLoader, url));
98 } catch (InaccessibleObjectException | ReflectiveOperationException | SecurityException e) {
99 LogManager.getLogger(MCRConfigurationInputStream.class)
100 .warn("{} does not support adding additional JARs at runtime", urlClassLoader.getClass(), e);
101 }
102 }
103
104
105
106
107 private static BiConsumer<ClassLoader, URL> addToClassPath(Class<? extends ClassLoader> classLoaderClass)
108 throws NoSuchMethodException {
109 Method method;
110 Function<URL, ?> argumentMapper;
111 final Method addURL = getDeclaredMethod(classLoaderClass, "addURL", URL.class);
112
113 if (addURL.trySetAccessible()) {
114
115 System.out.println("Using " + addURL + " to modify classpath.");
116 method = addURL;
117 argumentMapper = u -> u;
118 } else {
119 final Method jettyFallback = getDeclaredMethod(classLoaderClass, "addClassPath", String.class);
120 System.out.println("Using " + jettyFallback + " to modify classpath.");
121 argumentMapper = URL::toString;
122 method = jettyFallback;
123 }
124 Method finalMethod = method;
125 Function<URL, ?> finalArgumentMapper = argumentMapper;
126 return (cl, url) -> {
127 try {
128 finalMethod.invoke(cl, finalArgumentMapper.apply(url));
129 } catch (IllegalAccessException | InvocationTargetException e) {
130 LOGGER.error("Could not add {} to current classloader.", url, e);
131 }
132 };
133 }
134
135 private static Method getDeclaredMethod(Class<? extends ClassLoader> clazz, String method, Class... args)
136 throws NoSuchMethodException {
137 try {
138 return clazz.getDeclaredMethod(method, args);
139 } catch (NoSuchMethodException e) {
140 try {
141 if (ClassLoader.class.isAssignableFrom(clazz.getSuperclass())) {
142 return getDeclaredMethod((Class<? extends ClassLoader>) clazz.getSuperclass(), method, args);
143 }
144 } catch (NoSuchMethodException e2) {
145 throw e;
146 }
147 throw e;
148 }
149 }
150
151 private static Stream<File> getFileStream(File resourceDir, File libDir) {
152 Stream<File> toClassPath = Stream.of(resourceDir);
153 if (libDir.isDirectory()) {
154 File[] listFiles = libDir
155 .listFiles((dir, name) -> name.toLowerCase(Locale.ROOT).endsWith(".jar"));
156 if (listFiles.length != 0) {
157 toClassPath = Stream.concat(toClassPath, Stream.of(listFiles));
158 }
159 }
160 return toClassPath;
161 }
162
163
164
165
166 @Override
167 public String getName() {
168 return "Setup of MCRConfigurationDir";
169 }
170
171
172
173
174 @Override
175 public int getPriority() {
176 return Integer.MAX_VALUE - 100;
177 }
178
179
180
181
182 @Override
183 public void startUp(ServletContext servletContext) {
184 MCRConfigurationDir.setServletContext(servletContext);
185 loadExternalLibs();
186 MCRConfigurationLoader configurationLoader = MCRConfigurationLoaderFactory.getConfigurationLoader();
187 Map<String, String> properties = configurationLoader.load();
188 final Map<String, String> deprecated = configurationLoader.loadDeprecated();
189 MCRConfigurationBase.initialize(deprecated, properties, true);
190 if (servletContext != null) {
191 Log4jServletContainerInitializer log4jInitializer = new Log4jServletContainerInitializer();
192 try {
193 log4jInitializer.onStartup(null, servletContext);
194 } catch (ServletException e) {
195 System.err.println("Could not start Log4J2 context");
196 }
197 }
198 String configFileKey = "log4j.configurationFile";
199 URL log4j2ConfigURL = null;
200 if (System.getProperty(configFileKey) == null) {
201 log4j2ConfigURL = MCRConfigurationDir.getConfigResource("log4j2.xml");
202 }
203 LoggerContext logCtx;
204 if (log4j2ConfigURL == null) {
205 logCtx = (LoggerContext) LogManager.getContext(false);
206 } else {
207 logCtx = (LoggerContext) LogManager.getContext(null, false, URI.create(log4j2ConfigURL.toString()));
208 }
209 logCtx.reconfigure();
210 System.out.printf(Locale.ROOT, "Using Log4J2 configuration at: %s%n",
211 logCtx.getConfiguration().getConfigurationSource().getLocation());
212 MCRSessionMgr.addSessionListener(new MCRSessionThreadContext());
213 }
214 }