1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ch.qos.logback.classic.joran;
16
17 import ch.qos.logback.classic.ClassicConstants;
18 import ch.qos.logback.classic.LoggerContext;
19 import ch.qos.logback.classic.joran.serializedModel.HardenedModelInputStream;
20 import ch.qos.logback.classic.model.processor.LogbackClassicDefaultNestedComponentRules;
21 import ch.qos.logback.classic.spi.ConfiguratorRank;
22 import ch.qos.logback.core.Context;
23 import ch.qos.logback.core.LogbackException;
24 import ch.qos.logback.core.model.Model;
25 import ch.qos.logback.core.model.ModelUtil;
26 import ch.qos.logback.core.model.processor.DefaultProcessor;
27 import ch.qos.logback.core.model.processor.ModelInterpretationContext;
28 import ch.qos.logback.classic.spi.Configurator;
29 import ch.qos.logback.core.spi.ContextAwareBase;
30 import ch.qos.logback.core.status.InfoStatus;
31 import ch.qos.logback.core.status.StatusManager;
32 import ch.qos.logback.core.util.Loader;
33 import ch.qos.logback.core.util.OptionHelper;
34
35 import java.io.File;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.net.MalformedURLException;
39 import java.net.URL;
40 import java.util.concurrent.locks.ReentrantLock;
41
42 import static ch.qos.logback.core.CoreConstants.MODEL_CONFIG_FILE_EXTENSION;
43
44
45
46
47
48
49 @ConfiguratorRank(value = ConfiguratorRank.SERIALIZED_MODEL)
50 public class SerializedModelConfigurator extends ContextAwareBase implements Configurator {
51
52 final public static String AUTOCONFIG_MODEL_FILE = "logback"+ MODEL_CONFIG_FILE_EXTENSION;
53
54 final public static String TEST_AUTOCONFIG_MODEL_FILE = "logback-test"+ MODEL_CONFIG_FILE_EXTENSION;
55 protected ModelInterpretationContext modelInterpretationContext;
56
57 @Override
58 public ExecutionStatus configure(LoggerContext loggerContext) {
59
60 URL url = performMultiStepModelFileSearch(true);
61 if (url != null) {
62 addWarn("Replaced by logback-tyler, SerializedModelConfigurator has been deprecated and will be removed on 2025-07-01.");
63 configureByResource(url);
64 return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY;
65 } else {
66 return ExecutionStatus.INVOKE_NEXT_IF_ANY;
67 }
68 }
69
70 private void configureByResource(URL url) {
71 final String urlString = url.toString();
72 if (urlString.endsWith(MODEL_CONFIG_FILE_EXTENSION)) {
73 Model model = retrieveModel(url);
74 if(model == null) {
75 addWarn("Empty model. Abandoning.");
76 return;
77 }
78 ModelUtil.resetForReuse(model);
79 buildModelInterpretationContext(model);
80
81 DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext);
82 ModelClassToModelHandlerLinker mc2mhl = new ModelClassToModelHandlerLinker(context);
83 mc2mhl.link(defaultProcessor);
84
85
86 ReentrantLock configurationLock = context.getConfigurationLock();
87 try {
88 configurationLock.lock();
89 defaultProcessor.process(model);
90 } finally {
91 configurationLock.unlock();
92 }
93 } else {
94 throw new LogbackException(
95 "Unexpected filename extension of file [" + url.toString() + "]. Should be " + MODEL_CONFIG_FILE_EXTENSION);
96 }
97 }
98
99 private void buildModelInterpretationContext(Model topModel) {
100 this.modelInterpretationContext = new ModelInterpretationContext(context, this);
101 this.modelInterpretationContext.setTopModel(topModel);
102 LogbackClassicDefaultNestedComponentRules.addDefaultNestedComponentRegistryRules(
103 modelInterpretationContext.getDefaultNestedComponentRegistry());
104 this.modelInterpretationContext.createAppenderBags();
105 }
106
107 private Model retrieveModel(URL url) {
108 long start = System.currentTimeMillis();
109 try (InputStream is = url.openStream()) {
110 HardenedModelInputStream hmis = new HardenedModelInputStream(is);
111
112 Model model = (Model) hmis.readObject();
113 long diff = System.currentTimeMillis() - start;
114 addInfo("Model at ["+url+"] read in "+diff + " milliseconds");
115 return model;
116 } catch(IOException e) {
117 addError("Failed to open "+url, e);
118 } catch (ClassNotFoundException e) {
119 addError("Failed read model object in "+ url, e);
120 }
121 return null;
122 }
123
124 private URL performMultiStepModelFileSearch(boolean updateState) {
125 ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
126 URL url = findModelConfigFileURLFromSystemProperties(myClassLoader);
127 if (url != null) {
128 return url;
129 }
130
131 url = getResource(TEST_AUTOCONFIG_MODEL_FILE, myClassLoader, updateState);
132 if (url != null) {
133 return url;
134 }
135
136 url = getResource(AUTOCONFIG_MODEL_FILE, myClassLoader, updateState);
137 return url;
138 }
139
140 URL findModelConfigFileURLFromSystemProperties(ClassLoader classLoader) {
141 String logbackModelFile = OptionHelper.getSystemProperty(ClassicConstants.MODEL_CONFIG_FILE_PROPERTY);
142
143 if (logbackModelFile != null) {
144 URL result = null;
145 try {
146 result = new URL(logbackModelFile);
147 return result;
148 } catch (MalformedURLException e) {
149
150
151 result = Loader.getResource(logbackModelFile, classLoader);
152 if (result != null) {
153 return result;
154 }
155 File f = new File(logbackModelFile);
156 if (f.exists() && f.isFile()) {
157 try {
158 result = f.toURI().toURL();
159 return result;
160 } catch (MalformedURLException e1) {
161 }
162 }
163 } finally {
164 statusOnResourceSearch(logbackModelFile, result);
165 }
166 }
167 return null;
168 }
169
170
171 private URL getResource(String filename, ClassLoader classLoader, boolean updateStatus) {
172 URL url = Loader.getResource(filename, classLoader);
173 if (updateStatus) {
174 statusOnResourceSearch(filename, url);
175 }
176 return url;
177 }
178
179 private void statusOnResourceSearch(String resourceName, URL url) {
180 StatusManager sm = context.getStatusManager();
181 if (url == null) {
182 sm.add(new InfoStatus("Could NOT find resource [" + resourceName + "]", context));
183 } else {
184 sm.add(new InfoStatus("Found resource [" + resourceName + "] at [" + url.toString() + "]", context));
185 }
186 }
187 }