View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2022, QOS.ch. All rights reserved.
4    * <p>
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    * <p>
9    * or (per the licensee's choosing)
10   * <p>
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
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   * DefaultProcessor traverses the Model produced at an earlier step and performs actual
32   * configuration of logback according to the handlers it was given.
33   *
34   * @author Ceki G&uuml;lc&uuml;
35   * @since 1.3.0
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             // recurse into submodels handled or not
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 }