001/*
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2026, 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 v2.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.classic.model.processor;
015
016import ch.qos.logback.classic.LoggerContext;
017import ch.qos.logback.classic.model.ConfigurationModel;
018import ch.qos.logback.core.Context;
019import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil;
020import ch.qos.logback.core.model.Model;
021import ch.qos.logback.core.model.processor.ModelHandlerBase;
022import ch.qos.logback.core.model.processor.ModelHandlerException;
023import ch.qos.logback.core.model.processor.ModelInterpretationContext;
024import ch.qos.logback.core.status.OnConsoleStatusListener;
025import ch.qos.logback.core.util.ContextUtil;
026import ch.qos.logback.core.util.Duration;
027import ch.qos.logback.core.util.OptionHelper;
028import ch.qos.logback.core.util.StatusListenerConfigHelper;
029
030import static ch.qos.logback.core.model.ModelConstants.DEBUG_SYSTEM_PROPERTY_KEY;
031import static ch.qos.logback.core.model.ModelConstants.NULL_STR;
032import static java.lang.Boolean.FALSE;
033
034/**
035 * In 1.3.9/1.49, ConfigurationModelHandler has been reduced in functionality and no
036 * longer initiates a reconfiguration task. This change was justified by the need
037 * to remove java.xml reachability. See also https://jira.qos.ch/browse/LOGBACK-1717
038 *
039 * <p>
040 * See {@link ConfigurationModelHandlerFull} subclass offering configuration
041 * reloading support.
042 * </p>
043 */
044public class ConfigurationModelHandler extends ModelHandlerBase {
045
046    static final Duration SCAN_PERIOD_DEFAULT = Duration.buildByMinutes(1);
047
048    protected Boolean scanning = null;
049
050    public ConfigurationModelHandler(Context context) {
051        super(context);
052    }
053
054    static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext mic) {
055        return new ConfigurationModelHandler(context);
056    }
057
058    protected Class<ConfigurationModel> getSupportedModelClass() {
059        return ConfigurationModel.class;
060    }
061
062    @Override
063    public void handle(ModelInterpretationContext mic, Model model) {
064
065        ConfigurationModel configurationModel = (ConfigurationModel) model;
066
067        // See LOGBACK-527 (the system property is looked up first). Thus, it overrides
068        // the equivalent property in the config file. This reversal of scope priority
069        // is justified by the use case: the admin trying to chase rogue config file
070        String debugAttrib = OptionHelper.getSystemProperty(DEBUG_SYSTEM_PROPERTY_KEY, null);
071        if (debugAttrib == null) {
072            debugAttrib = mic.subst(configurationModel.getDebugStr());
073        }
074        
075
076        if (!(OptionHelper.isNullOrEmptyOrAllSpaces(debugAttrib) || debugAttrib.equalsIgnoreCase(FALSE.toString())
077                || debugAttrib.equalsIgnoreCase(NULL_STR))) {
078            StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener());
079        }
080
081        // It is hard to gauge at this stage which URL ares watchable
082        // However, we know for sure if the user wants scanning or not
083        this.scanning = scanAttrToBoolean(configurationModel);
084
085        mic.setTopScanBoolean(scanning);
086
087        printScanMessage(scanning);
088
089        if (scanning == Boolean.TRUE) {
090            ConfigurationWatchListUtil.registerNewConfigurationWatchListWithContext(getContext());
091            ConfigurationWatchListUtil.setMainWatchURL(context, mic.getTopURL());
092        }
093
094        LoggerContext lc = (LoggerContext) context;
095        boolean packagingData = OptionHelper.toBoolean(mic.subst(configurationModel.getPackagingDataStr()),
096                LoggerContext.DEFAULT_PACKAGING_DATA);
097        lc.setPackagingDataEnabled(packagingData);
098
099        ContextUtil contextUtil = new ContextUtil(context);
100        contextUtil.addGroovyPackages(lc.getFrameworkPackages());
101
102
103    }
104
105    void printScanMessage(Boolean scanning) {
106        if (scanning == null) {
107            addInfo("Scan attribute not set or set to unrecognized value.");
108            return;
109        }
110        if (scanning) {
111            addInfo("Scan attribute set to true. Will scan for configuration file changes.");
112        } else  {
113            addInfo("Scan attribute set to false.");
114        }
115    }
116
117
118    @Override
119    public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException {
120        //ConfigurationModel configurationModel = (ConfigurationModel) model;
121
122    }
123
124    /**
125     * Converts the scan string attribute of the given model to a Boolean value.
126     *
127     * <p>If the provided model is an instance of {@code ConfigurationModel}, the scan string is retrieved
128     * and converted to a {@code Boolean}. If the provided model is not a {@code ConfigurationModel},
129     * the method returns {@code null}.
130     * </p>
131     *
132     * @param model the model object, which may be an instance of {@code ConfigurationModel}
133     * @return a {@code Boolean} corresponding to the scan string attribute if the model is
134     *         an instance of {@code ConfigurationModel}, or {@code null} otherwise
135     *
136     * @since 1.5.27
137     */
138    private Boolean scanAttrToBoolean(Model model) {
139        if(model instanceof ConfigurationModel) {
140            ConfigurationModel configurationModel = (ConfigurationModel) model;
141            String scanStr = configurationModel.getScanStr();
142            return OptionHelper.toBooleanObject(scanStr);
143        } else {
144            return null;
145        }
146
147    }
148}