001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2022, QOS.ch. All rights reserved. 004 * <p> 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * <p> 009 * or (per the licensee's choosing) 010 * <p> 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.model.processor; 015 016import java.lang.reflect.Constructor; 017import java.lang.reflect.InvocationTargetException; 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.List; 021import java.util.function.Supplier; 022 023import ch.qos.logback.core.Context; 024import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; 025import ch.qos.logback.core.model.Model; 026import ch.qos.logback.core.model.ModelHandlerFactoryMethod; 027import ch.qos.logback.core.model.NamedComponentModel; 028import ch.qos.logback.core.spi.ContextAwareBase; 029import ch.qos.logback.core.spi.FilterReply; 030 031/** 032 * DefaultProcessor traverses the Model produced at an earlier step and performs actual 033 * configuration of logback according to the handlers it was given. 034 * 035 * @author Ceki Gülcü 036 * @since 1.3.0 037 */ 038public class DefaultProcessor extends ContextAwareBase { 039 040 interface TraverseMethod { 041 int traverse(Model model, ModelFilter modelFiler); 042 } 043 044 final protected ModelInterpretationContext mic; 045 final HashMap<Class<? extends Model>, ModelHandlerFactoryMethod> modelClassToHandlerMap = new HashMap<>(); 046 final HashMap<Class<? extends Model>, List<Supplier<ModelHandlerBase>>> modelClassToDependencyAnalyserMap = new HashMap<>(); 047 048 ChainedModelFilter phaseOneFilter = new ChainedModelFilter(); 049 ChainedModelFilter phaseTwoFilter = new ChainedModelFilter(); 050 051 public DefaultProcessor(Context context, ModelInterpretationContext mic) { 052 this.setContext(context); 053 this.mic = mic; 054 } 055 056 public void addHandler(Class<? extends Model> modelClass, ModelHandlerFactoryMethod modelFactoryMethod) { 057 058 modelClassToHandlerMap.put(modelClass, modelFactoryMethod); 059 060 ProcessingPhase phase = determineProcessingPhase(modelClass); 061 switch (phase) { 062 case FIRST: 063 getPhaseOneFilter().allow(modelClass); 064 break; 065 case SECOND: 066 getPhaseTwoFilter().allow(modelClass); 067 break; 068 default: 069 throw new IllegalArgumentException("unexpected value " + phase + " for model class " + modelClass.getName()); 070 } 071 } 072 073 private ProcessingPhase determineProcessingPhase(Class<? extends Model> modelClass) { 074 075 PhaseIndicator phaseIndicator = modelClass.getAnnotation(PhaseIndicator.class); 076 if (phaseIndicator == null) { 077 return ProcessingPhase.FIRST; 078 } 079 080 ProcessingPhase phase = phaseIndicator.phase(); 081 return phase; 082 } 083 084 public void addAnalyser(Class<? extends Model> modelClass, Supplier<ModelHandlerBase> analyserSupplier) { 085 modelClassToDependencyAnalyserMap.computeIfAbsent(modelClass, x -> new ArrayList<>()).add(analyserSupplier); 086 } 087 088 private void traversalLoop(TraverseMethod traverseMethod, Model model, ModelFilter modelfFilter, String phaseName) { 089 int LIMIT = 3; 090 for (int i = 0; i < LIMIT; i++) { 091 int handledModelCount = traverseMethod.traverse(model, modelfFilter); 092 if (handledModelCount == 0) 093 break; 094 } 095 } 096 097 public void process(Model model) { 098 099 if (model == null) { 100 addError("Expecting non null model to process"); 101 return; 102 } 103 initialObjectPush(); 104 105 mainTraverse(model, getPhaseOneFilter()); 106 analyseDependencies(model); 107 traversalLoop(this::secondPhaseTraverse, model, getPhaseTwoFilter(), "phase 2"); 108 109 addInfo("End of configuration."); 110 finalObjectPop(); 111 } 112 113 private void finalObjectPop() { 114 mic.popObject(); 115 } 116 117 private void initialObjectPush() { 118 mic.pushObject(context); 119 } 120 121 public ChainedModelFilter getPhaseOneFilter() { 122 return phaseOneFilter; 123 } 124 125 public ChainedModelFilter getPhaseTwoFilter() { 126 return phaseTwoFilter; 127 } 128 129 130 protected void analyseDependencies(Model model) { 131 132 List<Supplier<ModelHandlerBase>> analyserSupplierList = modelClassToDependencyAnalyserMap.get(model.getClass()); 133 134 if (analyserSupplierList != null) { 135 for (Supplier<ModelHandlerBase> analyserSupplier : analyserSupplierList) { 136 ModelHandlerBase analyser = null; 137 138 if (analyserSupplier != null) { 139 analyser = analyserSupplier.get(); 140 } 141 142 if (analyser != null && !model.isSkipped()) { 143 callAnalyserHandleOnModel(model, analyser); 144 } 145 146 if (analyser != null && !model.isSkipped()) { 147 callAnalyserPostHandleOnModel(model, analyser); 148 } 149 } 150 } 151 152 for (Model m : model.getSubModels()) { 153 analyseDependencies(m); 154 } 155 156 } 157 158 private void callAnalyserPostHandleOnModel(Model model, ModelHandlerBase analyser) { 159 try { 160 analyser.postHandle(mic, model); 161 } catch (ModelHandlerException e) { 162 addError("Failed to invoke postHandle on model " + model.getTag(), e); 163 } 164 } 165 166 private void callAnalyserHandleOnModel(Model model, ModelHandlerBase analyser) { 167 try { 168 analyser.handle(mic, model); 169 } catch (ModelHandlerException e) { 170 addError("Failed to traverse model " + model.getTag(), e); 171 } 172 } 173 174 static final int DENIED = -1; 175 176 private ModelHandlerBase createHandler(Model model) { 177 ModelHandlerFactoryMethod modelFactoryMethod = modelClassToHandlerMap.get(model.getClass()); 178 179 if (modelFactoryMethod == null) { 180 addError("Can't handle model of type " + model.getClass() + " with tag: " + model.getTag() + " at line " 181 + model.getLineNumber()); 182 return null; 183 } 184 185 ModelHandlerBase handler = modelFactoryMethod.make(context, mic); 186 if (handler == null) 187 return null; 188 if (!handler.isSupportedModelType(model)) { 189 addWarn("Handler [" + handler.getClass() + "] does not support " + model.idString()); 190 return null; 191 } 192 return handler; 193 } 194 195 protected int mainTraverse(Model model, ModelFilter modelFiler) { 196 197 FilterReply filterReply = modelFiler.decide(model); 198 if (filterReply == FilterReply.DENY) 199 return DENIED; 200 201 int count = 0; 202 203 try { 204 ModelHandlerBase handler = null; 205 boolean unhandled = model.isUnhandled(); 206 207 if (unhandled) { 208 handler = createHandler(model); 209 if (handler != null) { 210 handler.handle(mic, model); 211 model.markAsHandled(); 212 count++; 213 } 214 } 215 // recurse into submodels handled or not 216 if (!model.isSkipped()) { 217 for (Model m : model.getSubModels()) { 218 count += mainTraverse(m, modelFiler); 219 } 220 } 221 222 if (unhandled && handler != null) { 223 handler.postHandle(mic, model); 224 } 225 } catch (ModelHandlerException e) { 226 addError("Failed to traverse model " + model.getTag(), e); 227 } 228 return count; 229 } 230 231 protected int secondPhaseTraverse(Model model, ModelFilter modelFilter) { 232 233 FilterReply filterReply = modelFilter.decide(model); 234 if (filterReply == FilterReply.DENY) { 235 return 0; 236 } 237 238 int count = 0; 239 240 try { 241 242 boolean allDependenciesStarted = allDependenciesStarted(model); 243 244 ModelHandlerBase handler = null; 245 if (model.isUnhandled() && allDependenciesStarted) { 246 handler = createHandler(model); 247 if (handler != null) { 248 handler.handle(mic, model); 249 model.markAsHandled(); 250 count++; 251 } 252 } 253 254 if (!allDependenciesStarted && !dependencyIsADirectSubmodel(model)) { 255 return count; 256 } 257 258 if (!model.isSkipped()) { 259 for (Model m : model.getSubModels()) { 260 count += secondPhaseTraverse(m, modelFilter); 261 } 262 } 263 if (handler != null) { 264 handler.postHandle(mic, model); 265 } 266 } catch (ModelHandlerException e) { 267 addError("Failed to traverse model " + model.getTag(), e); 268 } 269 return count; 270 } 271 272 private boolean dependencyIsADirectSubmodel(Model model) { 273 List<String> dependecyNames = this.mic.getDependeeNamesForModel(model); 274 if (dependecyNames == null || dependecyNames.isEmpty()) { 275 return false; 276 } 277 for (Model submodel : model.getSubModels()) { 278 if (submodel instanceof NamedComponentModel) { 279 NamedComponentModel namedComponentModel = (NamedComponentModel) submodel; 280 String subModelName = namedComponentModel.getName(); 281 if (dependecyNames.contains(subModelName)) { 282 return true; 283 } 284 } 285 } 286 287 return false; 288 } 289 290 private boolean allDependenciesStarted(Model model) { 291 // assumes that DependencyDefinitions have been registered 292 List<String> dependencyNames = mic.getDependeeNamesForModel(model); 293 294 if (dependencyNames == null || dependencyNames.isEmpty()) { 295 return true; 296 } 297 for (String name : dependencyNames) { 298 boolean isStarted = mic.isNamedDependeeStarted(name); 299 if (isStarted == false) { 300 return false; 301 } 302 } 303 return true; 304 } 305 306}