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 addWarn("Replaced by logback-tyler, SerializedModelConfigurator has been deprecated and will be removed on 2025-07-01."); 063 configureByResource(url); 064 return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY; 065 } else { 066 return ExecutionStatus.INVOKE_NEXT_IF_ANY; 067 } 068 } 069 070 private void configureByResource(URL url) { 071 final String urlString = url.toString(); 072 if (urlString.endsWith(MODEL_CONFIG_FILE_EXTENSION)) { 073 Model model = retrieveModel(url); 074 if(model == null) { 075 addWarn("Empty model. Abandoning."); 076 return; 077 } 078 ModelUtil.resetForReuse(model); 079 buildModelInterpretationContext(model); 080 081 DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext); 082 ModelClassToModelHandlerLinker mc2mhl = new ModelClassToModelHandlerLinker(context); 083 mc2mhl.link(defaultProcessor); 084 085 // disallow simultaneous configurations of the same context 086 ReentrantLock configurationLock = context.getConfigurationLock(); 087 try { 088 configurationLock.lock(); 089 defaultProcessor.process(model); 090 } finally { 091 configurationLock.unlock(); 092 } 093 } else { 094 throw new LogbackException( 095 "Unexpected filename extension of file [" + url.toString() + "]. Should be " + MODEL_CONFIG_FILE_EXTENSION); 096 } 097 } 098 099 private void buildModelInterpretationContext(Model topModel) { 100 this.modelInterpretationContext = new ModelInterpretationContext(context, this); 101 this.modelInterpretationContext.setTopModel(topModel); 102 LogbackClassicDefaultNestedComponentRules.addDefaultNestedComponentRegistryRules( 103 modelInterpretationContext.getDefaultNestedComponentRegistry()); 104 this.modelInterpretationContext.createAppenderBags(); 105 } 106 107 private Model retrieveModel(URL url) { 108 long start = System.currentTimeMillis(); 109 try (InputStream is = url.openStream()) { 110 HardenedModelInputStream hmis = new HardenedModelInputStream(is); 111 112 Model model = (Model) hmis.readObject(); 113 long diff = System.currentTimeMillis() - start; 114 addInfo("Model at ["+url+"] read in "+diff + " milliseconds"); 115 return model; 116 } catch(IOException e) { 117 addError("Failed to open "+url, e); 118 } catch (ClassNotFoundException e) { 119 addError("Failed read model object in "+ url, e); 120 } 121 return null; 122 } 123 124 private URL performMultiStepModelFileSearch(boolean updateState) { 125 ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this); 126 URL url = findModelConfigFileURLFromSystemProperties(myClassLoader); 127 if (url != null) { 128 return url; 129 } 130 131 url = getResource(TEST_AUTOCONFIG_MODEL_FILE, myClassLoader, updateState); 132 if (url != null) { 133 return url; 134 } 135 136 url = getResource(AUTOCONFIG_MODEL_FILE, myClassLoader, updateState); 137 return url; 138 } 139 140 URL findModelConfigFileURLFromSystemProperties(ClassLoader classLoader) { 141 String logbackModelFile = OptionHelper.getSystemProperty(ClassicConstants.MODEL_CONFIG_FILE_PROPERTY); 142 143 if (logbackModelFile != null) { 144 URL result = null; 145 try { 146 result = new URL(logbackModelFile); 147 return result; 148 } catch (MalformedURLException e) { 149 // so, resource is not a URL: 150 // attempt to get the resource from the class path 151 result = Loader.getResource(logbackModelFile, classLoader); 152 if (result != null) { 153 return result; 154 } 155 File f = new File(logbackModelFile); 156 if (f.exists() && f.isFile()) { 157 try { 158 result = f.toURI().toURL(); 159 return result; 160 } catch (MalformedURLException e1) { 161 } 162 } 163 } finally { 164 statusOnResourceSearch(logbackModelFile, result); 165 } 166 } 167 return null; 168 } 169 170 171 private URL getResource(String filename, ClassLoader classLoader, boolean updateStatus) { 172 URL url = Loader.getResource(filename, classLoader); 173 if (updateStatus) { 174 statusOnResourceSearch(filename, url); 175 } 176 return url; 177 } 178 179 private void statusOnResourceSearch(String resourceName, URL url) { 180 StatusManager sm = context.getStatusManager(); 181 if (url == null) { 182 sm.add(new InfoStatus("Could NOT find resource [" + resourceName + "]", context)); 183 } else { 184 sm.add(new InfoStatus("Found resource [" + resourceName + "] at [" + url.toString() + "]", context)); 185 } 186 } 187}