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.util.ArrayList; 017import java.util.Collections; 018import java.util.HashMap; 019import java.util.List; 020import java.util.Map; 021import java.util.Stack; 022import java.util.function.Supplier; 023 024import ch.qos.logback.core.Appender; 025import ch.qos.logback.core.Context; 026import ch.qos.logback.core.joran.GenericXMLConfigurator; 027import ch.qos.logback.core.joran.JoranConstants; 028import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; 029import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; 030import ch.qos.logback.core.model.Model; 031import ch.qos.logback.core.model.util.VariableSubstitutionsHelper; 032import ch.qos.logback.core.spi.AppenderAttachable; 033import ch.qos.logback.core.spi.ContextAwareBase; 034import ch.qos.logback.core.spi.ContextAwarePropertyContainer; 035 036public class ModelInterpretationContext extends ContextAwareBase implements ContextAwarePropertyContainer { 037 038 Stack<Object> objectStack; 039 Stack<Model> modelStack; 040 041 /** 042 * A supplier of JoranConfigurator instances. 043 * 044 * May be null. 045 * 046 * @since 1.5.5 047 */ 048 Supplier<? extends GenericXMLConfigurator> configuratorSupplier; 049 050 051 Map<String, Object> objectMap; 052 protected VariableSubstitutionsHelper variableSubstitutionsHelper; 053 protected Map<String, String> importMap; 054 055 final private BeanDescriptionCache beanDescriptionCache; 056 final DefaultNestedComponentRegistry defaultNestedComponentRegistry = new DefaultNestedComponentRegistry(); 057 List<DependencyDefinition> dependencyDefinitionList = new ArrayList<>(); 058 final List<String> startedDependees = new ArrayList<>(); 059 060 Object configuratorHint; 061 062 Model topModel; 063 064 public ModelInterpretationContext(Context context) { 065 this(context, null); 066 } 067 068 public ModelInterpretationContext(Context context, Object configuratorHint) { 069 this.context = context; 070 this.configuratorHint = configuratorHint; 071 this.objectStack = new Stack<>(); 072 this.modelStack = new Stack<>(); 073 this.beanDescriptionCache = new BeanDescriptionCache(context); 074 objectMap = new HashMap<>(5); 075 variableSubstitutionsHelper = new VariableSubstitutionsHelper(context); 076 importMap = new HashMap<>(5); 077 } 078 079 public ModelInterpretationContext(ModelInterpretationContext otherMic) { 080 this(otherMic.context, otherMic.configuratorHint); 081 importMap = new HashMap<>(otherMic.importMap); 082 variableSubstitutionsHelper = new VariableSubstitutionsHelper(context, otherMic.getCopyOfPropertyMap()); 083 defaultNestedComponentRegistry.duplicate(otherMic.getDefaultNestedComponentRegistry()); 084 createAppenderBags(); 085 } 086 087 public Map<String, Object> getObjectMap() { 088 return objectMap; 089 } 090 091 public void createAppenderBags() { 092 objectMap.put(JoranConstants.APPENDER_BAG, new HashMap<String, Appender<?>>()); 093 objectMap.put(JoranConstants.APPENDER_REF_BAG, new HashMap<String, AppenderAttachable<?>>()); 094 } 095 096 public Model getTopModel() { 097 return topModel; 098 } 099 100 public void setTopModel(Model topModel) { 101 this.topModel = topModel; 102 } 103 104 // modelStack ================================= 105 106 public void pushModel(Model m) { 107 modelStack.push(m); 108 } 109 110 public Model peekModel() { 111 return modelStack.peek(); 112 } 113 114 public boolean isModelStackEmpty() { 115 return modelStack.isEmpty(); 116 } 117 118 public Model popModel() { 119 return modelStack.pop(); 120 } 121 122 // =================== object stack 123 124 public Stack<Object> getObjectStack() { 125 return objectStack; 126 } 127 128 public boolean isObjectStackEmpty() { 129 return objectStack.isEmpty(); 130 } 131 132 public Object peekObject() { 133 return objectStack.peek(); 134 } 135 136 public void pushObject(Object o) { 137 objectStack.push(o); 138 } 139 140 public Object popObject() { 141 return objectStack.pop(); 142 } 143 144 public Object getObject(int i) { 145 return objectStack.get(i); 146 } 147 148 // ===================== END object stack 149 150 public Object getConfiguratorHint() { 151 return configuratorHint; 152 } 153 154 public void setConfiguratorHint(Object configuratorHint) { 155 this.configuratorHint = configuratorHint; 156 } 157 158 public BeanDescriptionCache getBeanDescriptionCache() { 159 return beanDescriptionCache; 160 } 161 162 /** 163 * Performs variable substitution on the provided {@code ref} string. 164 * 165 * <p>Value substitution will follow the order</p> 166 * <ol> 167 * <li>properties defined in this {@link ModelInterpretationContext}</li> 168 * <li>properties defined in the {@link Context context} of this {@link ModelInterpretationContext}</li> 169 * <li>System properties</li> 170 * <li>Environment variables</li> 171 * </ol> 172 * 173 * <p>If value substitution occurs it will be output as a status message, unless marked confidential, that is, 174 * if {@code ref} contains the case-insensitive strings PASSWORD, SECRET or CONFIDENTIAL.</p> 175 * 176 * @param ref the string that may contain variables to be substituted; can be {@code null} 177 * @return the string with substitutions applied if applicable; may return {@code null} if {@code ref} is {@code null} 178 */ 179 public String subst(String ref) { 180 181 String substituted = variableSubstitutionsHelper.subst(ref); 182 if(ref != null && !ref.equals(substituted) ) { 183 String sanitized = variableSubstitutionsHelper.sanitizeIfConfidential(ref, substituted); 184 addInfo("value \""+sanitized+"\" substituted for \""+ref+"\""); 185 } 186 return substituted; 187 } 188 189 public DefaultNestedComponentRegistry getDefaultNestedComponentRegistry() { 190 return defaultNestedComponentRegistry; 191 } 192 193 // ================================== dependencies 194 195 public void addDependencyDefinition(DependencyDefinition dd) { 196 dependencyDefinitionList.add(dd); 197 } 198 199 public List<DependencyDefinition> getDependencyDefinitions() { 200 return Collections.unmodifiableList(dependencyDefinitionList); 201 } 202 203 public List<String> getDependencyNamesForModel(Model model) { 204 List<String> dependencyList = new ArrayList<>(); 205 for (DependencyDefinition dd : dependencyDefinitionList) { 206 if (dd.getDepender() == model) { 207 dependencyList.add(dd.getDependency()); 208 } 209 } 210 return dependencyList; 211 } 212 213 public boolean hasDependers(String dependencyName) { 214 215 if (dependencyName == null || dependencyName.trim().length() == 0) { 216 new IllegalArgumentException("Empty dependeeName name not allowed here"); 217 } 218 219 for (DependencyDefinition dd : dependencyDefinitionList) { 220 if (dd.dependency.equals(dependencyName)) 221 return true; 222 } 223 224 return false; 225 } 226 227 228 public void markStartOfNamedDependee(String name) { 229 startedDependees.add(name); 230 } 231 232 public boolean isNamedDependemcyStarted(String name) { 233 return startedDependees.contains(name); 234 } 235 236 // ========================================== object map 237 238 /** 239 * Add a property to the properties of this execution context. If the property 240 * exists already, it is overwritten. 241 */ 242 @Override 243 public void addSubstitutionProperty(String key, String value) { 244 variableSubstitutionsHelper.addSubstitutionProperty(key, value); 245 } 246 247 /** 248 * If a key is found in propertiesMap then return it. Otherwise, delegate to the 249 * context. 250 */ 251 public String getProperty(String key) { 252 return variableSubstitutionsHelper.getProperty(key); 253 } 254 255 @Override 256 public Map<String, String> getCopyOfPropertyMap() { 257 return variableSubstitutionsHelper.getCopyOfPropertyMap(); 258 } 259 260 // imports =================================================================== 261 262 /** 263 * Add an import to the importMao 264 * 265 * @param stem the class to import 266 * @param fqcn the fully qualified name of the class 267 * 268 * @since 1.3 269 */ 270 public void addImport(String stem, String fqcn) { 271 importMap.put(stem, fqcn); 272 } 273 274 public Map<String, String> getImportMapCopy() { 275 return new HashMap<>(importMap); 276 } 277 278 279 /** 280 * Given a stem, get the fully qualified name of the class corresponding to the 281 * stem. For unknown stems, returns the stem as is. If stem is null, null is 282 * returned. 283 * 284 * @param stem may be null 285 * @return fully qualified name of the class corresponding to the stem. For 286 * unknown stems, returns the stem as is. If stem is null, null is 287 * returned. 288 * @since 1.3 289 */ 290 public String getImport(String stem) { 291 if (stem == null) 292 return null; 293 294 String result = importMap.get(stem); 295 if (result == null) 296 return stem; 297 else 298 return result; 299 } 300 301 /** 302 * Returns a supplier of {@link GenericXMLConfigurator} instance. The returned value may be null. 303 * 304 * @return a supplier of {@link GenericXMLConfigurator} instance, may be null 305 */ 306 @Override 307 public Supplier<? extends GenericXMLConfigurator> getConfiguratorSupplier() { 308 return this.configuratorSupplier; 309 } 310 311 /** 312 * 313 * @param configuratorSupplier 314 */ 315 public void setConfiguratorSupplier(Supplier<? extends GenericXMLConfigurator> configuratorSupplier) { 316 this.configuratorSupplier = configuratorSupplier; 317 } 318}