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