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 model) {
95
96 if (model == null) {
97 addError("Expecting non null model to process");
98 return;
99 }
100 initialObjectPush();
101
102 mainTraverse(model, getPhaseOneFilter());
103 analyseDependencies(model);
104 traversalLoop(this::secondPhaseTraverse, model, 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
131 if (analyserSupplierList != null) {
132 for (Supplier<ModelHandlerBase> analyserSupplier : analyserSupplierList) {
133 ModelHandlerBase analyser = null;
134
135 if (analyserSupplier != null) {
136 analyser = analyserSupplier.get();
137 }
138
139 if (analyser != null && !model.isSkipped()) {
140 callAnalyserHandleOnModel(model, analyser);
141 }
142
143 if (analyser != null && !model.isSkipped()) {
144 callAnalyserPostHandleOnModel(model, analyser);
145 }
146 }
147 }
148
149 for (Model m : model.getSubModels()) {
150 analyseDependencies(m);
151 }
152
153 }
154
155 private void callAnalyserPostHandleOnModel(Model model, ModelHandlerBase analyser) {
156 try {
157 analyser.postHandle(mic, model);
158 } catch (ModelHandlerException e) {
159 addError("Failed to invoke postHandle on model " + model.getTag(), e);
160 }
161 }
162
163 private void callAnalyserHandleOnModel(Model model, ModelHandlerBase analyser) {
164 try {
165 analyser.handle(mic, model);
166 } catch (ModelHandlerException e) {
167 addError("Failed to traverse model " + model.getTag(), e);
168 }
169 }
170
171 static final int DENIED = -1;
172
173 private ModelHandlerBase createHandler(Model model) {
174 ModelHandlerFactoryMethod modelFactoryMethod = modelClassToHandlerMap.get(model.getClass());
175
176 if (modelFactoryMethod == null) {
177 addError("Can't handle model of type " + model.getClass() + " with tag: " + model.getTag() + " at line "
178 + model.getLineNumber());
179 return null;
180 }
181
182 ModelHandlerBase handler = modelFactoryMethod.make(context, mic);
183 if (handler == null)
184 return null;
185 if (!handler.isSupportedModelType(model)) {
186 addWarn("Handler [" + handler.getClass() + "] does not support " + model.idString());
187 return null;
188 }
189 return handler;
190 }
191
192 protected int mainTraverse(Model model, ModelFilter modelFiler) {
193
194 FilterReply filterReply = modelFiler.decide(model);
195 if (filterReply == FilterReply.DENY)
196 return DENIED;
197
198 int count = 0;
199
200 try {
201 ModelHandlerBase handler = null;
202 boolean unhandled = model.isUnhandled();
203
204 if (unhandled) {
205 handler = createHandler(model);
206 if (handler != null) {
207 handler.handle(mic, model);
208 model.markAsHandled();
209 count++;
210 }
211 }
212
213 if (!model.isSkipped()) {
214 for (Model m : model.getSubModels()) {
215 count += mainTraverse(m, modelFiler);
216 }
217 }
218
219 if (unhandled && handler != null) {
220 handler.postHandle(mic, model);
221 }
222 } catch (ModelHandlerException e) {
223 addError("Failed to traverse model " + model.getTag(), e);
224 }
225 return count;
226 }
227
228 protected int secondPhaseTraverse(Model model, ModelFilter modelFilter) {
229
230 FilterReply filterReply = modelFilter.decide(model);
231 if (filterReply == FilterReply.DENY) {
232 return 0;
233 }
234
235 int count = 0;
236
237 try {
238
239 boolean allDependenciesStarted = allDependenciesStarted(model);
240
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 && !dependencyIsADirectSubmodel(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 dependencyIsADirectSubmodel(Model model) {
270 List<String> dependecyNames = this.mic.getDependencyNamesForModel(model);
271 if (dependecyNames == null || dependecyNames.isEmpty()) {
272 return false;
273 }
274 for (Model submodel : model.getSubModels()) {
275 if (submodel instanceof NamedComponentModel) {
276 NamedComponentModel namedComponentModel = (NamedComponentModel) submodel;
277 String subModelName = namedComponentModel.getName();
278 if (dependecyNames.contains(subModelName)) {
279 return true;
280 }
281 }
282 }
283
284 return false;
285 }
286
287 private boolean allDependenciesStarted(Model model) {
288
289 List<String> dependencyNames = mic.getDependencyNamesForModel(model);
290
291 if (dependencyNames == null || dependencyNames.isEmpty()) {
292 return true;
293 }
294 for (String name : dependencyNames) {
295 boolean isRegistered = AppenderDeclarationAnalyser.isAppenderDeclared(mic, name);
296 if (!isRegistered) {
297
298 continue;
299 }
300 boolean isStarted = mic.isNamedDependemcyStarted(name);
301 if (!isStarted) {
302 return false;
303 }
304 }
305 return true;
306 }
307
308 }