1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.core.model.processor;
15
16 import java.lang.reflect.Constructor;
17 import java.lang.reflect.InvocationTargetException;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.function.Supplier;
21
22 import ch.qos.logback.core.Context;
23 import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache;
24 import ch.qos.logback.core.model.Model;
25 import ch.qos.logback.core.model.ModelHandlerFactoryMethod;
26 import ch.qos.logback.core.model.NamedComponentModel;
27 import ch.qos.logback.core.spi.ContextAwareBase;
28 import ch.qos.logback.core.spi.FilterReply;
29
30
31
32
33
34
35
36
37 public class DefaultProcessor extends ContextAwareBase {
38
39 interface TraverseMethod {
40 int traverse(Model model, ModelFilter modelFiler);
41 }
42
43 final protected ModelInterpretationContext mic;
44 final HashMap<Class<? extends Model>, ModelHandlerFactoryMethod> modelClassToHandlerMap = new HashMap<>();
45 final HashMap<Class<? extends Model>, Supplier<ModelHandlerBase>> modelClassToDependencyAnalyserMap = new HashMap<>();
46
47 ChainedModelFilter phaseOneFilter = new ChainedModelFilter();
48 ChainedModelFilter phaseTwoFilter = new ChainedModelFilter();
49
50 public DefaultProcessor(Context context, ModelInterpretationContext mic) {
51 this.setContext(context);
52 this.mic = mic;
53 }
54
55 public void addHandler(Class<? extends Model> modelClass, ModelHandlerFactoryMethod modelFactoryMethod) {
56
57 modelClassToHandlerMap.put(modelClass, modelFactoryMethod);
58
59 ProcessingPhase phase = determineProcessingPhase(modelClass);
60 switch (phase) {
61 case FIRST:
62 getPhaseOneFilter().allow(modelClass);
63 break;
64 case SECOND:
65 getPhaseTwoFilter().allow(modelClass);
66 break;
67 default:
68 throw new IllegalArgumentException("unexpected value " + phase + " for model class " + modelClass.getName());
69 }
70 }
71
72 private ProcessingPhase determineProcessingPhase(Class<? extends Model> modelClass) {
73
74 PhaseIndicator phaseIndicator = modelClass.getAnnotation(PhaseIndicator.class);
75 if (phaseIndicator == null) {
76 return ProcessingPhase.FIRST;
77 }
78
79 ProcessingPhase phase = phaseIndicator.phase();
80 return phase;
81 }
82
83 public void addAnalyser(Class<? extends Model> modelClass, Supplier<ModelHandlerBase> analyserSupplier) {
84 modelClassToDependencyAnalyserMap.put(modelClass, analyserSupplier);
85 }
86
87 private void traversalLoop(TraverseMethod traverseMethod, Model model, ModelFilter modelfFilter, String phaseName) {
88 int LIMIT = 3;
89 for (int i = 0; i < LIMIT; i++) {
90 int handledModelCount = traverseMethod.traverse(model, modelfFilter);
91 if (handledModelCount == 0)
92 break;
93 }
94 }
95
96 public void process(Model model) {
97
98 if (model == null) {
99 addError("Expecting non null model to process");
100 return;
101 }
102 initialObjectPush();
103
104 mainTraverse(model, getPhaseOneFilter());
105 analyseDependencies(model);
106 traversalLoop(this::secondPhaseTraverse, model, getPhaseTwoFilter(), "phase 2");
107
108 addInfo("End of configuration.");
109 finalObjectPop();
110 }
111
112 private void finalObjectPop() {
113 mic.popObject();
114 }
115
116 private void initialObjectPush() {
117 mic.pushObject(context);
118 }
119
120 public ChainedModelFilter getPhaseOneFilter() {
121 return phaseOneFilter;
122 }
123
124 public ChainedModelFilter getPhaseTwoFilter() {
125 return phaseTwoFilter;
126 }
127
128
129 protected void analyseDependencies(Model model) {
130 Supplier<ModelHandlerBase> analyserSupplier = modelClassToDependencyAnalyserMap.get(model.getClass());
131
132 ModelHandlerBase analyser = null;
133
134 if (analyserSupplier != null) {
135 analyser = analyserSupplier.get();
136 }
137
138 if (analyser != null && !model.isSkipped()) {
139 callAnalyserHandleOnModel(model, analyser);
140 }
141
142 for (Model m : model.getSubModels()) {
143 analyseDependencies(m);
144 }
145
146 if (analyser != null && !model.isSkipped()) {
147 callAnalyserPostHandleOnModel(model, analyser);
148 }
149 }
150
151 private void callAnalyserPostHandleOnModel(Model model, ModelHandlerBase analyser) {
152 try {
153 analyser.postHandle(mic, model);
154 } catch (ModelHandlerException e) {
155 addError("Failed to invoke postHandle on model " + model.getTag(), e);
156 }
157 }
158
159 private void callAnalyserHandleOnModel(Model model, ModelHandlerBase analyser) {
160 try {
161 analyser.handle(mic, model);
162 } catch (ModelHandlerException e) {
163 addError("Failed to traverse model " + model.getTag(), e);
164 }
165 }
166
167 static final int DENIED = -1;
168
169 private ModelHandlerBase createHandler(Model model) {
170 ModelHandlerFactoryMethod modelFactoryMethod = modelClassToHandlerMap.get(model.getClass());
171
172 if (modelFactoryMethod == null) {
173 addError("Can't handle model of type " + model.getClass() + " with tag: " + model.getTag() + " at line "
174 + model.getLineNumber());
175 return null;
176 }
177
178 ModelHandlerBase handler = modelFactoryMethod.make(context, mic);
179 if (handler == null)
180 return null;
181 if (!handler.isSupportedModelType(model)) {
182 addWarn("Handler [" + handler.getClass() + "] does not support " + model.idString());
183 return null;
184 }
185 return handler;
186 }
187
188 protected int mainTraverse(Model model, ModelFilter modelFiler) {
189
190 FilterReply filterReply = modelFiler.decide(model);
191 if (filterReply == FilterReply.DENY)
192 return DENIED;
193
194 int count = 0;
195
196 try {
197 ModelHandlerBase handler = null;
198 boolean unhandled = model.isUnhandled();
199
200 if (unhandled) {
201 handler = createHandler(model);
202 if (handler != null) {
203 handler.handle(mic, model);
204 model.markAsHandled();
205 count++;
206 }
207 }
208
209 if (!model.isSkipped()) {
210 for (Model m : model.getSubModels()) {
211 count += mainTraverse(m, modelFiler);
212 }
213 }
214
215 if (unhandled && handler != null) {
216 handler.postHandle(mic, model);
217 }
218 } catch (ModelHandlerException e) {
219 addError("Failed to traverse model " + model.getTag(), e);
220 }
221 return count;
222 }
223
224 protected int secondPhaseTraverse(Model model, ModelFilter modelFilter) {
225
226 FilterReply filterReply = modelFilter.decide(model);
227 if (filterReply == FilterReply.DENY) {
228 return 0;
229 }
230
231 int count = 0;
232
233 try {
234
235 boolean allDependenciesStarted = allDependenciesStarted(model);
236
237 ModelHandlerBase handler = null;
238 if (model.isUnhandled() && allDependenciesStarted) {
239 handler = createHandler(model);
240 if (handler != null) {
241 handler.handle(mic, model);
242 model.markAsHandled();
243 count++;
244 }
245 }
246
247 if (!allDependenciesStarted && !dependencyIsADirectSubmodel(model)) {
248 return count;
249 }
250
251 if (!model.isSkipped()) {
252 for (Model m : model.getSubModels()) {
253 count += secondPhaseTraverse(m, modelFilter);
254 }
255 }
256 if (handler != null) {
257 handler.postHandle(mic, model);
258 }
259 } catch (ModelHandlerException e) {
260 addError("Failed to traverse model " + model.getTag(), e);
261 }
262 return count;
263 }
264
265 private boolean dependencyIsADirectSubmodel(Model model) {
266 List<String> dependecyNames = this.mic.getDependeeNamesForModel(model);
267 if (dependecyNames == null || dependecyNames.isEmpty()) {
268 return false;
269 }
270 for (Model submodel : model.getSubModels()) {
271 if (submodel instanceof NamedComponentModel) {
272 NamedComponentModel namedComponentModel = (NamedComponentModel) submodel;
273 String subModelName = namedComponentModel.getName();
274 if (dependecyNames.contains(subModelName)) {
275 return true;
276 }
277 }
278 }
279
280 return false;
281 }
282
283 private boolean allDependenciesStarted(Model model) {
284 List<String> dependencyNames = mic.getDependeeNamesForModel(model);
285
286 if (dependencyNames == null || dependencyNames.isEmpty()) {
287 return true;
288 }
289 for (String name : dependencyNames) {
290 boolean isStarted = mic.isNamedDependeeStarted(name);
291 if (isStarted == false) {
292 return false;
293 }
294 }
295 return true;
296 }
297
298 ModelHandlerBase instantiateHandler(Class<? extends ModelHandlerBase> handlerClass) {
299 try {
300 Constructor<? extends ModelHandlerBase> commonConstructor = getWithContextConstructor(handlerClass);
301 if (commonConstructor != null) {
302 return commonConstructor.newInstance(context);
303 }
304 Constructor<? extends ModelHandlerBase> constructorWithBDC = getWithContextAndBDCConstructor(handlerClass);
305 if (constructorWithBDC != null) {
306 return constructorWithBDC.newInstance(context, mic.getBeanDescriptionCache());
307 }
308 addError("Failed to find suitable constructor for class [" + handlerClass + "]");
309 return null;
310 } catch (InstantiationException | IllegalAccessException | SecurityException | IllegalArgumentException
311 | InvocationTargetException e1) {
312 addError("Failed to instantiate " + handlerClass);
313 return null;
314 }
315 }
316
317 private Constructor<? extends ModelHandlerBase> getWithContextConstructor(
318 Class<? extends ModelHandlerBase> handlerClass) {
319 try {
320 Constructor<? extends ModelHandlerBase> constructor = handlerClass.getConstructor(Context.class);
321 return constructor;
322 } catch (NoSuchMethodException e) {
323 return null;
324 }
325 }
326
327 private Constructor<? extends ModelHandlerBase> getWithContextAndBDCConstructor(
328 Class<? extends ModelHandlerBase> handlerClass) {
329 try {
330 Constructor<? extends ModelHandlerBase> constructor = handlerClass.getConstructor(Context.class,
331 BeanDescriptionCache.class);
332 return constructor;
333 } catch (NoSuchMethodException e) {
334 return null;
335 }
336 }
337
338 }