View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2022, QOS.ch. All rights reserved.
4    *
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    *
9    *   or (per the licensee's choosing)
10   *
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  
21  import ch.qos.logback.core.Context;
22  import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache;
23  import ch.qos.logback.core.model.Model;
24  import ch.qos.logback.core.model.ModelFactoryMethod;
25  import ch.qos.logback.core.model.NamedComponentModel;
26  import ch.qos.logback.core.spi.ContextAwareBase;
27  import ch.qos.logback.core.spi.FilterReply;
28  
29  /**
30   * DefaultProcessor traverses the Model produced at an earlier step and performs actual 
31   * configuration of logback according to the handlers it was given.
32   * 
33   * @author Ceki Gülcü
34   * @since 1.3.0
35   */
36  public class DefaultProcessor extends ContextAwareBase {
37  
38      interface TraverseMethod {
39          int traverse(Model model, ModelFilter modelFiler);
40      }
41   
42      final ModelInterpretationContext mic;
43      final HashMap<Class<? extends Model>, ModelFactoryMethod> modelClassToHandlerMap = new HashMap<>();
44      final HashMap<Class<? extends Model>, ModelHandlerBase> modelClassToDependencyAnalyserMap = new HashMap<>();
45  
46      ModelFilter phaseOneFilter = new AllowAllModelFilter();
47      ModelFilter phaseTwoFilter = new DenyAllModelFilter();
48  
49      public DefaultProcessor(Context context, ModelInterpretationContext mic) {
50          this.setContext(context);
51          this.mic = mic;
52      }
53  
54      public void addHandler(Class<? extends Model> modelClass, ModelFactoryMethod modelFactoryMethod) {
55          modelClassToHandlerMap.put(modelClass, modelFactoryMethod);
56      }
57  
58      public void addAnalyser(Class<? extends Model> modelClass, ModelHandlerBase handler) {
59          modelClassToDependencyAnalyserMap.put(modelClass, handler);
60      }
61  
62      private void traversalLoop(TraverseMethod traverseMethod, Model model, ModelFilter modelfFilter, String phaseName) {
63          int LIMIT = 3;
64          for (int i = 0; i < LIMIT; i++) {
65              int handledModelCount = traverseMethod.traverse(model, modelfFilter);
66              if (handledModelCount == 0)
67                  break;
68          }
69      }
70  
71      public void process(Model model) {
72  
73          if (model == null) {
74              addError("Expecting non null model to process");
75              return;
76          }
77          initialObjectPush();
78  
79          mainTraverse(model, getPhaseOneFilter());
80          analyseDependencies(model);
81          traversalLoop(this::secondPhaseTraverse, model, getPhaseTwoFilter(), "phase 2");
82  
83          addInfo("End of configuration.");
84          finalObjectPop();
85      }
86  
87      private void finalObjectPop() {
88          mic.popObject();
89      }
90  
91      private void initialObjectPush() {
92          mic.pushObject(context);
93      }
94  
95      public ModelFilter getPhaseOneFilter() {
96          return phaseOneFilter;
97      }
98  
99      public ModelFilter getPhaseTwoFilter() {
100         return phaseTwoFilter;
101     }
102 
103     public void setPhaseOneFilter(ModelFilter phaseOneFilter) {
104         this.phaseOneFilter = phaseOneFilter;
105     }
106 
107     public void setPhaseTwoFilter(ModelFilter phaseTwoFilter) {
108         this.phaseTwoFilter = phaseTwoFilter;
109     }
110 
111     protected void analyseDependencies(Model model) {
112         ModelHandlerBase handler = modelClassToDependencyAnalyserMap.get(model.getClass());
113 
114         if (handler != null) {
115             try {
116                 handler.handle(mic, model);
117             } catch (ModelHandlerException e) {
118                 addError("Failed to traverse model " + model.getTag(), e);
119             }
120         }
121 
122         for (Model m : model.getSubModels()) {
123             analyseDependencies(m);
124         }
125         if (handler != null) {
126             try {
127                 handler.postHandle(mic, model);
128             } catch (ModelHandlerException e) {
129                 addError("Failed to invole postHandle on model " + model.getTag(), e);
130             }
131         }
132     }
133 
134     static final int DENIED = -1;
135 
136     private ModelHandlerBase createHandler(Model model) {
137         ModelFactoryMethod modelFactoryMethod = modelClassToHandlerMap.get(model.getClass());
138 
139         if (modelFactoryMethod == null) {
140             addError("Can't handle model of type " + model.getClass() + "  with tag: " + model.getTag() + " at line "
141                     + model.getLineNumber());
142             return null;
143         }
144 
145         ModelHandlerBase handler = modelFactoryMethod.make(context, mic);
146         if (handler == null)
147             return null;
148         if (!handler.isSupportedModelType(model)) {
149             addWarn("Handler [" + handler.getClass() + "] does not support " + model.idString());
150             return null;
151         }
152         return handler;
153     }
154 
155     protected int mainTraverse(Model model, ModelFilter modelFiler) {
156 
157         FilterReply filterReply = modelFiler.decide(model);
158         if (filterReply == FilterReply.DENY)
159             return DENIED;
160 
161         int count = 0;
162 
163         try {
164             ModelHandlerBase handler = null;
165             if (model.isUnhandled()) {
166                 handler = createHandler(model);
167                 if (handler != null) {
168                     handler.handle(mic, model);
169                     model.markAsHandled();
170                     count++;
171                 }
172             }
173             // recurse into submodels handled or not
174 
175             if (!model.isSkipped()) {
176                 for (Model m : model.getSubModels()) {
177                     count += mainTraverse(m, modelFiler);
178                 }
179             }
180             if (handler != null) {
181                 handler.postHandle(mic, model);
182             }
183         } catch (ModelHandlerException e) {
184             addError("Failed to traverse model " + model.getTag(), e);
185         }
186         return count;
187     }
188 
189     protected int secondPhaseTraverse(Model model, ModelFilter modelFilter) {
190 
191         FilterReply filterReply = modelFilter.decide(model);
192         if (filterReply == FilterReply.DENY) {
193             return 0;
194         }
195 
196         int count = 0;
197 
198         try {
199 
200             boolean allDependenciesStarted = allDependenciesStarted(model);
201 
202             ModelHandlerBase handler = null;
203             if (model.isUnhandled() && allDependenciesStarted) {
204                 handler = createHandler(model);
205                 if (handler != null) {
206                     handler.handle(mic, model);
207                     model.markAsHandled();
208                     count++;
209                 }
210             }
211 
212             if (!allDependenciesStarted && !dependencyIsADirectSubmodel(model)) {
213                 return count;
214             }
215 
216             if (!model.isSkipped()) {
217                 for (Model m : model.getSubModels()) {
218                     count += secondPhaseTraverse(m, modelFilter);
219                 }
220             }
221             if (handler != null) {
222                 handler.postHandle(mic, model);
223             }
224         } catch (ModelHandlerException e) {
225             addError("Failed to traverse model " + model.getTag(), e);
226         }
227         return count;
228     }
229 
230     private boolean dependencyIsADirectSubmodel(Model model) {
231         List<String> dependecyNames = this.mic.getDependeeNamesForModel(model);
232         if (dependecyNames == null || dependecyNames.isEmpty()) {
233             return false;
234         }
235         for (Model submodel : model.getSubModels()) {
236             if (submodel instanceof NamedComponentModel) {
237                 NamedComponentModel namedComponentModel = (NamedComponentModel) submodel;
238                 String subModelName = namedComponentModel.getName();
239                 if (dependecyNames.contains(subModelName)) {
240                     return true;
241                 }
242             }
243         }
244 
245         return false;
246     }
247 
248     private boolean allDependenciesStarted(Model model) {
249         List<String> dependencyNames = mic.getDependeeNamesForModel(model);
250        
251         if (dependencyNames == null || dependencyNames.isEmpty()) {
252             return true;
253         }
254         for (String name : dependencyNames) {
255             boolean isStarted = mic.isNamedDependeeStarted(name);
256             if (isStarted == false) {
257                 return false;
258             }
259         }
260         return true;
261     }
262 
263     ModelHandlerBase instantiateHandler(Class<? extends ModelHandlerBase> handlerClass) {
264         try {
265             Constructor<? extends ModelHandlerBase> commonConstructor = getWithContextConstructor(handlerClass);
266             if (commonConstructor != null) {
267                 return commonConstructor.newInstance(context);
268             }
269             Constructor<? extends ModelHandlerBase> constructorWithBDC = getWithContextAndBDCConstructor(handlerClass);
270             if (constructorWithBDC != null) {
271                 return constructorWithBDC.newInstance(context, mic.getBeanDescriptionCache());
272             }
273             addError("Failed to find suitable constructor for class [" + handlerClass + "]");
274             return null;
275         } catch (InstantiationException | IllegalAccessException | SecurityException | IllegalArgumentException
276                 | InvocationTargetException e1) {
277             addError("Failed to instantiate " + handlerClass);
278             return null;
279         }
280     }
281 
282     private Constructor<? extends ModelHandlerBase> getWithContextConstructor(
283             Class<? extends ModelHandlerBase> handlerClass) {
284         try {
285             Constructor<? extends ModelHandlerBase> constructor = handlerClass.getConstructor(Context.class);
286             return constructor;
287         } catch (NoSuchMethodException e) {
288             return null;
289         }
290     }
291 
292     private Constructor<? extends ModelHandlerBase> getWithContextAndBDCConstructor(
293             Class<? extends ModelHandlerBase> handlerClass) {
294         try {
295             Constructor<? extends ModelHandlerBase> constructor = handlerClass.getConstructor(Context.class,
296                     BeanDescriptionCache.class);
297             return constructor;
298         } catch (NoSuchMethodException e) {
299             return null;
300         }
301     }
302 
303 }