001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2022, QOS.ch. All rights reserved. 004 * 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 * 009 * or (per the licensee's choosing) 010 * 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) { 139 try { 140 analyser.handle(mic, model); 141 } catch (ModelHandlerException e) { 142 addError("Failed to traverse model " + model.getTag(), e); 143 } 144 } 145 146 for (Model m : model.getSubModels()) { 147 analyseDependencies(m); 148 } 149 if (analyser != null) { 150 try { 151 analyser.postHandle(mic, model); 152 } catch (ModelHandlerException e) { 153 addError("Failed to invoke postHandle on model " + model.getTag(), e); 154 } 155 } 156 } 157 158 static final int DENIED = -1; 159 160 private ModelHandlerBase createHandler(Model model) { 161 ModelHandlerFactoryMethod modelFactoryMethod = modelClassToHandlerMap.get(model.getClass()); 162 163 if (modelFactoryMethod == null) { 164 addError("Can't handle model of type " + model.getClass() + " with tag: " + model.getTag() + " at line " 165 + model.getLineNumber()); 166 return null; 167 } 168 169 ModelHandlerBase handler = modelFactoryMethod.make(context, mic); 170 if (handler == null) 171 return null; 172 if (!handler.isSupportedModelType(model)) { 173 addWarn("Handler [" + handler.getClass() + "] does not support " + model.idString()); 174 return null; 175 } 176 return handler; 177 } 178 179 protected int mainTraverse(Model model, ModelFilter modelFiler) { 180 181 FilterReply filterReply = modelFiler.decide(model); 182 if (filterReply == FilterReply.DENY) 183 return DENIED; 184 185 int count = 0; 186 187 try { 188 ModelHandlerBase handler = null; 189 boolean unhandled = model.isUnhandled(); 190 191 if (unhandled) { 192 handler = createHandler(model); 193 if (handler != null) { 194 handler.handle(mic, model); 195 model.markAsHandled(); 196 count++; 197 } 198 } 199 // recurse into submodels handled or not 200 if (!model.isSkipped()) { 201 for (Model m : model.getSubModels()) { 202 count += mainTraverse(m, modelFiler); 203 } 204 } 205 206 if (unhandled && handler != null) { 207 handler.postHandle(mic, model); 208 } 209 } catch (ModelHandlerException e) { 210 addError("Failed to traverse model " + model.getTag(), e); 211 } 212 return count; 213 } 214 215 protected int secondPhaseTraverse(Model model, ModelFilter modelFilter) { 216 217 FilterReply filterReply = modelFilter.decide(model); 218 if (filterReply == FilterReply.DENY) { 219 return 0; 220 } 221 222 int count = 0; 223 224 try { 225 226 boolean allDependenciesStarted = allDependenciesStarted(model); 227 228 ModelHandlerBase handler = null; 229 if (model.isUnhandled() && allDependenciesStarted) { 230 handler = createHandler(model); 231 if (handler != null) { 232 handler.handle(mic, model); 233 model.markAsHandled(); 234 count++; 235 } 236 } 237 238 if (!allDependenciesStarted && !dependencyIsADirectSubmodel(model)) { 239 return count; 240 } 241 242 if (!model.isSkipped()) { 243 for (Model m : model.getSubModels()) { 244 count += secondPhaseTraverse(m, modelFilter); 245 } 246 } 247 if (handler != null) { 248 handler.postHandle(mic, model); 249 } 250 } catch (ModelHandlerException e) { 251 addError("Failed to traverse model " + model.getTag(), e); 252 } 253 return count; 254 } 255 256 private boolean dependencyIsADirectSubmodel(Model model) { 257 List<String> dependecyNames = this.mic.getDependeeNamesForModel(model); 258 if (dependecyNames == null || dependecyNames.isEmpty()) { 259 return false; 260 } 261 for (Model submodel : model.getSubModels()) { 262 if (submodel instanceof NamedComponentModel) { 263 NamedComponentModel namedComponentModel = (NamedComponentModel) submodel; 264 String subModelName = namedComponentModel.getName(); 265 if (dependecyNames.contains(subModelName)) { 266 return true; 267 } 268 } 269 } 270 271 return false; 272 } 273 274 private boolean allDependenciesStarted(Model model) { 275 List<String> dependencyNames = mic.getDependeeNamesForModel(model); 276 277 if (dependencyNames == null || dependencyNames.isEmpty()) { 278 return true; 279 } 280 for (String name : dependencyNames) { 281 boolean isStarted = mic.isNamedDependeeStarted(name); 282 if (isStarted == false) { 283 return false; 284 } 285 } 286 return true; 287 } 288 289 ModelHandlerBase instantiateHandler(Class<? extends ModelHandlerBase> handlerClass) { 290 try { 291 Constructor<? extends ModelHandlerBase> commonConstructor = getWithContextConstructor(handlerClass); 292 if (commonConstructor != null) { 293 return commonConstructor.newInstance(context); 294 } 295 Constructor<? extends ModelHandlerBase> constructorWithBDC = getWithContextAndBDCConstructor(handlerClass); 296 if (constructorWithBDC != null) { 297 return constructorWithBDC.newInstance(context, mic.getBeanDescriptionCache()); 298 } 299 addError("Failed to find suitable constructor for class [" + handlerClass + "]"); 300 return null; 301 } catch (InstantiationException | IllegalAccessException | SecurityException | IllegalArgumentException 302 | InvocationTargetException e1) { 303 addError("Failed to instantiate " + handlerClass); 304 return null; 305 } 306 } 307 308 private Constructor<? extends ModelHandlerBase> getWithContextConstructor( 309 Class<? extends ModelHandlerBase> handlerClass) { 310 try { 311 Constructor<? extends ModelHandlerBase> constructor = handlerClass.getConstructor(Context.class); 312 return constructor; 313 } catch (NoSuchMethodException e) { 314 return null; 315 } 316 } 317 318 private Constructor<? extends ModelHandlerBase> getWithContextAndBDCConstructor( 319 Class<? extends ModelHandlerBase> handlerClass) { 320 try { 321 Constructor<? extends ModelHandlerBase> constructor = handlerClass.getConstructor(Context.class, 322 BeanDescriptionCache.class); 323 return constructor; 324 } catch (NoSuchMethodException e) { 325 return null; 326 } 327 } 328 329}