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.HashMap; 019import java.util.List; 020import java.util.function.Supplier; 021 022import ch.qos.logback.core.Context; 023import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; 024import ch.qos.logback.core.model.Model; 025import ch.qos.logback.core.model.ModelHandlerFactoryMethod; 026import ch.qos.logback.core.model.NamedComponentModel; 027import ch.qos.logback.core.spi.ContextAwareBase; 028import ch.qos.logback.core.spi.FilterReply; 029 030/** 031 * DefaultProcessor traverses the Model produced at an earlier step and performs actual 032 * configuration of logback according to the handlers it was given. 033 * 034 * @author Ceki Gülcü 035 * @since 1.3.0 036 */ 037public class DefaultProcessor extends ContextAwareBase { 038 039 interface TraverseMethod { 040 int traverse(Model model, ModelFilter modelFiler); 041 } 042 043 final protected ModelInterpretationContext mic; 044 final HashMap<Class<? extends Model>, ModelHandlerFactoryMethod> modelClassToHandlerMap = new HashMap<>(); 045 final HashMap<Class<? extends Model>, Supplier<ModelHandlerBase>> modelClassToDependencyAnalyserMap = new HashMap<>(); 046 047 ChainedModelFilter phaseOneFilter = new ChainedModelFilter(); 048 ChainedModelFilter phaseTwoFilter = new ChainedModelFilter(); 049 050 public DefaultProcessor(Context context, ModelInterpretationContext mic) { 051 this.setContext(context); 052 this.mic = mic; 053 } 054 055 public void addHandler(Class<? extends Model> modelClass, ModelHandlerFactoryMethod modelFactoryMethod) { 056 057 modelClassToHandlerMap.put(modelClass, modelFactoryMethod); 058 059 ProcessingPhase phase = determineProcessingPhase(modelClass); 060 switch (phase) { 061 case FIRST: 062 getPhaseOneFilter().allow(modelClass); 063 break; 064 case SECOND: 065 getPhaseTwoFilter().allow(modelClass); 066 break; 067 default: 068 throw new IllegalArgumentException("unexpected value " + phase + " for model class " + modelClass.getName()); 069 } 070 } 071 072 private ProcessingPhase determineProcessingPhase(Class<? extends Model> modelClass) { 073 074 PhaseIndicator phaseIndicator = modelClass.getAnnotation(PhaseIndicator.class); 075 if (phaseIndicator == null) { 076 return ProcessingPhase.FIRST; 077 } 078 079 ProcessingPhase phase = phaseIndicator.phase(); 080 return phase; 081 } 082 083 public void addAnalyser(Class<? extends Model> modelClass, Supplier<ModelHandlerBase> analyserSupplier) { 084 modelClassToDependencyAnalyserMap.put(modelClass, analyserSupplier); 085 } 086 087 private void traversalLoop(TraverseMethod traverseMethod, Model model, ModelFilter modelfFilter, String phaseName) { 088 int LIMIT = 3; 089 for (int i = 0; i < LIMIT; i++) { 090 int handledModelCount = traverseMethod.traverse(model, modelfFilter); 091 if (handledModelCount == 0) 092 break; 093 } 094 } 095 096 public void process(Model model) { 097 098 if (model == null) { 099 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}