001/*
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2024, 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.core.model.util;
016
017import ch.qos.logback.core.joran.action.ActionUtil;
018import ch.qos.logback.core.model.ModelConstants;
019import ch.qos.logback.core.model.PropertyModel;
020import ch.qos.logback.core.spi.ContextAware;
021import ch.qos.logback.core.spi.ContextAwareBase;
022import ch.qos.logback.core.spi.ContextAwarePropertyContainer;
023import ch.qos.logback.core.spi.PropertyContainer;
024import ch.qos.logback.core.util.ContextUtil;
025import ch.qos.logback.core.util.Loader;
026import ch.qos.logback.core.util.OptionHelper;
027
028import java.io.FileInputStream;
029import java.io.FileNotFoundException;
030import java.io.IOException;
031import java.io.InputStream;
032import java.net.URL;
033import java.util.Properties;
034
035/**
036 * Given a {@link PropertyModel} offers methods to inject properties into a {@link PropertyContainer}.
037 *
038 * @since 1.5.1
039 */
040public class PropertyModelHandlerHelper extends ContextAwareBase {
041
042    public static final String HANDLE_PROPERTY_MODEL_METHOD_NAME = "handlePropertyModel";
043
044    public PropertyModelHandlerHelper(ContextAware declaredOrigin) {
045        super(declaredOrigin);
046    }
047
048    /**
049     * Given a {@link PropertyModel} inject relevant properties into the given {@link ContextAwarePropertyContainer}
050     * parameter.
051     *
052     * @param capcm
053     * @param nameStr
054     * @param valueStr
055     * @param fileStr
056     * @param resourceStr
057     * @param scopeStr
058     *
059     */
060    public void handlePropertyModel(ContextAwarePropertyContainer capcm, String nameStr, String valueStr,
061            String fileStr, String resourceStr, String scopeStr) {
062        PropertyModel propertyModel = new PropertyModel();
063        propertyModel.setName(nameStr);
064        propertyModel.setValue(valueStr);
065        propertyModel.setFile(fileStr);
066        propertyModel.setResource(resourceStr);
067
068        propertyModel.setScopeStr(scopeStr);
069
070        handlePropertyModel(capcm, propertyModel);
071    }
072
073    /**
074     * Given a {@link PropertyModel} inject relevant properties into the given {@link ContextAwarePropertyContainer}
075     * parameter.
076     *
077     * @param capc
078     * @param propertyModel
079     */
080    public void handlePropertyModel(ContextAwarePropertyContainer capc, PropertyModel propertyModel) {
081        ActionUtil.Scope scope = ActionUtil.stringToScope(propertyModel.getScopeStr());
082
083        if (checkFileAttributeSanity(propertyModel)) {
084            String file = propertyModel.getFile();
085            file = capc.subst(file);
086            try (FileInputStream istream = new FileInputStream(file)) {
087                PropertyModelHandlerHelper.loadAndSetProperties(capc, istream, scope);
088            } catch (FileNotFoundException e) {
089                addError("Could not find properties file [" + file + "].");
090            } catch (IOException | IllegalArgumentException e1) { // IllegalArgumentException is thrown in case the file
091                // is badly malformed, i.e a binary.
092                addError("Could not read properties file [" + file + "].", e1);
093            }
094        } else if (checkResourceAttributeSanity(propertyModel)) {
095            String resource = propertyModel.getResource();
096            resource = capc.subst(resource);
097            URL resourceURL = Loader.getResourceBySelfClassLoader(resource);
098            if (resourceURL == null) {
099                addError("Could not find resource [" + resource + "].");
100            } else {
101                try (InputStream istream = resourceURL.openStream();) {
102                    PropertyModelHandlerHelper.loadAndSetProperties(capc, istream, scope);
103                } catch (IOException e) {
104                    addError("Could not read resource file [" + resource + "].", e);
105                }
106            }
107        } else if (checkValueNameAttributesSanity(propertyModel)) {
108            // earlier versions performed Java '\' escapes for '\\' '\t' etc. Howevver, there is no
109            // need to do this. See RegularEscapeUtil.__UNUSED__basicEscape
110            String value = propertyModel.getValue();
111
112            // now remove both leading and trailing spaces
113            value = value.trim();
114            value = capc.subst(value);
115            ActionUtil.setProperty(capc, propertyModel.getName(), value, scope);
116
117        } else {
118            addError(ModelConstants.INVALID_ATTRIBUTES);
119        }
120    }
121
122    public static boolean checkFileAttributeSanity(PropertyModel propertyModel) {
123        String file = propertyModel.getFile();
124        String name = propertyModel.getName();
125        String value = propertyModel.getValue();
126        String resource = propertyModel.getResource();
127
128        return !(OptionHelper.isNullOrEmptyOrAllSpaces(file)) && (OptionHelper.isNullOrEmptyOrAllSpaces(name)
129                && OptionHelper.isNullOrEmptyOrAllSpaces(value) && OptionHelper.isNullOrEmptyOrAllSpaces(resource));
130    }
131
132    public static boolean checkResourceAttributeSanity(PropertyModel propertyModel) {
133        String file = propertyModel.getFile();
134        String name = propertyModel.getName();
135        String value = propertyModel.getValue();
136        String resource = propertyModel.getResource();
137
138        return !(OptionHelper.isNullOrEmptyOrAllSpaces(resource)) && (OptionHelper.isNullOrEmptyOrAllSpaces(name)
139                && OptionHelper.isNullOrEmptyOrAllSpaces(value) && OptionHelper.isNullOrEmptyOrAllSpaces(file));
140    }
141
142    public static boolean checkValueNameAttributesSanity(PropertyModel propertyModel) {
143        String file = propertyModel.getFile();
144        String name = propertyModel.getName();
145        String value = propertyModel.getValue();
146        String resource = propertyModel.getResource();
147        return (!(OptionHelper.isNullOrEmptyOrAllSpaces(name) || OptionHelper.isNullOrEmptyOrAllSpaces(value)) && (
148                OptionHelper.isNullOrEmptyOrAllSpaces(file) && OptionHelper.isNullOrEmptyOrAllSpaces(resource)));
149    }
150
151    /**
152     * Add all the properties found in the argument named 'props' to an InterpretationContext.
153     */
154    static public void setProperty(ContextAwarePropertyContainer capc, String key, String value,
155            ActionUtil.Scope scope) {
156        switch (scope) {
157        case LOCAL:
158            capc.addSubstitutionProperty(key, value);
159            break;
160        case CONTEXT:
161            capc.getContext().putProperty(key, value);
162            break;
163        case SYSTEM:
164            OptionHelper.setSystemProperty(capc, key, value);
165        }
166    }
167
168    /**
169     * Add all the properties found in the argument named 'props' to an InterpretationContext.
170     */
171    static public void setProperties(ContextAwarePropertyContainer capc, Properties props, ActionUtil.Scope scope) {
172        switch (scope) {
173        case LOCAL:
174            capc.addSubstitutionProperties(props);
175            break;
176        case CONTEXT:
177            ContextUtil cu = new ContextUtil(capc.getContext());
178            cu.addProperties(props);
179            break;
180        case SYSTEM:
181            OptionHelper.setSystemProperties(capc, props);
182        }
183    }
184
185    static public void loadAndSetProperties(ContextAwarePropertyContainer capc, InputStream istream,
186            ActionUtil.Scope scope) throws IOException {
187        Properties props = new Properties();
188        props.load(istream);
189        PropertyModelHandlerHelper.setProperties(capc, props, scope);
190    }
191}