001/* 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2023, 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 */ 014 015package ch.qos.logback.classic.joran; 016 017import ch.qos.logback.classic.ClassicConstants; 018import ch.qos.logback.classic.LoggerContext; 019import ch.qos.logback.classic.joran.serializedModel.HardenedModelInputStream; 020import ch.qos.logback.classic.model.processor.LogbackClassicDefaultNestedComponentRules; 021import ch.qos.logback.classic.spi.ConfiguratorRank; 022import ch.qos.logback.core.Context; 023import ch.qos.logback.core.LogbackException; 024import ch.qos.logback.core.model.Model; 025import ch.qos.logback.core.model.ModelUtil; 026import ch.qos.logback.core.model.processor.DefaultProcessor; 027import ch.qos.logback.core.model.processor.ModelInterpretationContext; 028import ch.qos.logback.classic.spi.Configurator; 029import ch.qos.logback.core.spi.ContextAwareBase; 030import ch.qos.logback.core.status.InfoStatus; 031import ch.qos.logback.core.status.StatusManager; 032import ch.qos.logback.core.util.Loader; 033import ch.qos.logback.core.util.OptionHelper; 034 035import java.io.File; 036import java.io.IOException; 037import java.io.InputStream; 038import java.net.MalformedURLException; 039import java.net.URL; 040import java.util.concurrent.locks.ReentrantLock; 041 042import static ch.qos.logback.core.CoreConstants.MODEL_CONFIG_FILE_EXTENSION; 043 044/** 045 * @since 1.3.9/1.4.9 046 */ 047 048// BEWARE: the fqcn is used in SerializedModelModelHandler 049@ConfiguratorRank(value = ConfiguratorRank.SERIALIZED_MODEL) 050public class SerializedModelConfigurator extends ContextAwareBase implements Configurator { 051 052 final public static String AUTOCONFIG_MODEL_FILE = "logback"+ MODEL_CONFIG_FILE_EXTENSION; 053 054 final public static String TEST_AUTOCONFIG_MODEL_FILE = "logback-test"+ MODEL_CONFIG_FILE_EXTENSION; 055 protected ModelInterpretationContext modelInterpretationContext; 056 057 @Override 058 public ExecutionStatus configure(LoggerContext loggerContext) { 059 060 URL url = performMultiStepModelFileSearch(true); 061 if (url != null) { 062 configureByResource(url); 063 return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY; 064 } else { 065 return ExecutionStatus.INVOKE_NEXT_IF_ANY; 066 } 067 } 068 069 private void configureByResource(URL url) { 070 final String urlString = url.toString(); 071 if (urlString.endsWith(MODEL_CONFIG_FILE_EXTENSION)) { 072 Model model = retrieveModel(url); 073 if(model == null) { 074 addWarn("Empty model. Abandoning."); 075 return; 076 } 077 ModelUtil.resetForReuse(model); 078 buildModelInterpretationContext(model); 079 080 DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext); 081 ModelClassToModelHandlerLinker mc2mhl = new ModelClassToModelHandlerLinker(context); 082 mc2mhl.link(defaultProcessor); 083 084 // disallow simultaneous configurations of the same context 085 ReentrantLock configurationLock = context.getConfigurationLock(); 086 try { 087 configurationLock.lock(); 088 defaultProcessor.process(model); 089 } finally { 090 configurationLock.unlock(); 091 } 092 } else { 093 throw new LogbackException( 094 "Unexpected filename extension of file [" + url.toString() + "]. Should be " + MODEL_CONFIG_FILE_EXTENSION); 095 } 096 } 097 098 private void buildModelInterpretationContext(Model topModel) { 099 this.modelInterpretationContext = new ModelInterpretationContext(context, this); 100 this.modelInterpretationContext.setTopModel(topModel); 101 LogbackClassicDefaultNestedComponentRules.addDefaultNestedComponentRegistryRules( 102 modelInterpretationContext.getDefaultNestedComponentRegistry()); 103 this.modelInterpretationContext.createAppenderBags(); 104 } 105 106 private Model retrieveModel(URL url) { 107 long start = System.currentTimeMillis(); 108 try (InputStream is = url.openStream()) { 109 HardenedModelInputStream hmis = new HardenedModelInputStream(is); 110 111 Model model = (Model) hmis.readObject(); 112 long diff = System.currentTimeMillis() - start; 113 addInfo("Model at ["+url+"] read in "+diff + " milliseconds"); 114 return model; 115 } catch(IOException e) { 116 addError("Failed to open "+url, e); 117 } catch (ClassNotFoundException e) { 118 addError("Failed read model object in "+ url, e); 119 } 120 return null; 121 } 122 123 private URL performMultiStepModelFileSearch(boolean updateState) { 124 ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this); 125 URL url = findModelConfigFileURLFromSystemProperties(myClassLoader); 126 if (url != null) { 127 return url; 128 } 129 130 url = getResource(TEST_AUTOCONFIG_MODEL_FILE, myClassLoader, updateState); 131 if (url != null) { 132 return url; 133 } 134 135 url = getResource(AUTOCONFIG_MODEL_FILE, myClassLoader, updateState); 136 return url; 137 } 138 139 URL findModelConfigFileURLFromSystemProperties(ClassLoader classLoader) { 140 String logbackModelFile = OptionHelper.getSystemProperty(ClassicConstants.MODEL_CONFIG_FILE_PROPERTY); 141 142 if (logbackModelFile != null) { 143 URL result = null; 144 try { 145 result = new URL(logbackModelFile); 146 return result; 147 } catch (MalformedURLException e) { 148 // so, resource is not a URL: 149 // attempt to get the resource from the class path 150 result = Loader.getResource(logbackModelFile, classLoader); 151 if (result != null) { 152 return result; 153 } 154 File f = new File(logbackModelFile); 155 if (f.exists() && f.isFile()) { 156 try { 157 result = f.toURI().toURL(); 158 return result; 159 } catch (MalformedURLException e1) { 160 } 161 } 162 } finally { 163 statusOnResourceSearch(logbackModelFile, result); 164 } 165 } 166 return null; 167 } 168 169 170 private URL getResource(String filename, ClassLoader classLoader, boolean updateStatus) { 171 URL url = Loader.getResource(filename, classLoader); 172 if (updateStatus) { 173 statusOnResourceSearch(filename, url); 174 } 175 return url; 176 } 177 178 private void statusOnResourceSearch(String resourceName, URL url) { 179 StatusManager sm = context.getStatusManager(); 180 if (url == null) { 181 sm.add(new InfoStatus("Could NOT find resource [" + resourceName + "]", context)); 182 } else { 183 sm.add(new InfoStatus("Found resource [" + resourceName + "] at [" + url.toString() + "]", context)); 184 } 185 } 186}