001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, 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.joran.action;
015
016import java.io.FileInputStream;
017import java.io.FileNotFoundException;
018import java.io.IOException;
019import java.io.InputStream;
020import java.net.URL;
021import java.util.Properties;
022
023import org.xml.sax.Attributes;
024
025import ch.qos.logback.core.joran.action.ActionUtil.Scope;
026import ch.qos.logback.core.joran.spi.InterpretationContext;
027import ch.qos.logback.core.pattern.util.RegularEscapeUtil;
028import ch.qos.logback.core.util.Loader;
029import ch.qos.logback.core.util.OptionHelper;
030
031/**
032 * This class serves as a base for other actions, which similar to the ANT
033 * <property> task which add/set properties of a given object.
034 * 
035 * This action sets new substitution properties in the logging context by name,
036 * value pair, or adds all the properties passed in "file" or "resource"
037 * attribute.
038 * 
039 * @author Ceki Gülcü
040 */
041public class PropertyAction extends Action {
042
043    static final String RESOURCE_ATTRIBUTE = "resource";
044
045    static String INVALID_ATTRIBUTES = "In <property> element, either the \"file\" attribute alone, or "
046                    + "the \"resource\" element alone, or both the \"name\" and \"value\" attributes must be set.";
047
048    /**
049     * Set a new property for the execution context by name, value pair, or adds
050     * all the properties found in the given file.
051     * 
052     */
053    public void begin(InterpretationContext ec, String localName, Attributes attributes) {
054
055        if ("substitutionProperty".equals(localName)) {
056            addWarn("[substitutionProperty] element has been deprecated. Please use the [property] element instead.");
057        }
058
059        String name = attributes.getValue(NAME_ATTRIBUTE);
060        String value = attributes.getValue(VALUE_ATTRIBUTE);
061        String scopeStr = attributes.getValue(SCOPE_ATTRIBUTE);
062
063        Scope scope = ActionUtil.stringToScope(scopeStr);
064
065        if (checkFileAttributeSanity(attributes)) {
066            String file = attributes.getValue(FILE_ATTRIBUTE);
067            file = ec.subst(file);
068            try {
069                FileInputStream istream = new FileInputStream(file);
070                loadAndSetProperties(ec, istream, scope);
071            } catch (FileNotFoundException e) {
072                addError("Could not find properties file [" + file + "].");
073            } catch (IOException e1) {
074                addError("Could not read properties file [" + file + "].", e1);
075            }
076        } else if (checkResourceAttributeSanity(attributes)) {
077            String resource = attributes.getValue(RESOURCE_ATTRIBUTE);
078            resource = ec.subst(resource);
079            URL resourceURL = Loader.getResourceBySelfClassLoader(resource);
080            if (resourceURL == null) {
081                addError("Could not find resource [" + resource + "].");
082            } else {
083                try {
084                    InputStream istream = resourceURL.openStream();
085                    loadAndSetProperties(ec, istream, scope);
086                } catch (IOException e) {
087                    addError("Could not read resource file [" + resource + "].", e);
088                }
089            }
090        } else if (checkValueNameAttributesSanity(attributes)) {
091            value = RegularEscapeUtil.basicEscape(value);
092            // now remove both leading and trailing spaces
093            value = value.trim();
094            value = ec.subst(value);
095            ActionUtil.setProperty(ec, name, value, scope);
096
097        } else {
098            addError(INVALID_ATTRIBUTES);
099        }
100    }
101
102    void loadAndSetProperties(InterpretationContext ec, InputStream istream, Scope scope) throws IOException {
103        Properties props = new Properties();
104        props.load(istream);
105        istream.close();
106        ActionUtil.setProperties(ec, props, scope);
107    }
108
109    boolean checkFileAttributeSanity(Attributes attributes) {
110        String file = attributes.getValue(FILE_ATTRIBUTE);
111        String name = attributes.getValue(NAME_ATTRIBUTE);
112        String value = attributes.getValue(VALUE_ATTRIBUTE);
113        String resource = attributes.getValue(RESOURCE_ATTRIBUTE);
114
115        return !(OptionHelper.isEmpty(file)) && (OptionHelper.isEmpty(name) && OptionHelper.isEmpty(value) && OptionHelper.isEmpty(resource));
116    }
117
118    boolean checkResourceAttributeSanity(Attributes attributes) {
119        String file = attributes.getValue(FILE_ATTRIBUTE);
120        String name = attributes.getValue(NAME_ATTRIBUTE);
121        String value = attributes.getValue(VALUE_ATTRIBUTE);
122        String resource = attributes.getValue(RESOURCE_ATTRIBUTE);
123
124        return !(OptionHelper.isEmpty(resource)) && (OptionHelper.isEmpty(name) && OptionHelper.isEmpty(value) && OptionHelper.isEmpty(file));
125    }
126
127    boolean checkValueNameAttributesSanity(Attributes attributes) {
128        String file = attributes.getValue(FILE_ATTRIBUTE);
129        String name = attributes.getValue(NAME_ATTRIBUTE);
130        String value = attributes.getValue(VALUE_ATTRIBUTE);
131        String resource = attributes.getValue(RESOURCE_ATTRIBUTE);
132
133        return (!(OptionHelper.isEmpty(name) || OptionHelper.isEmpty(value)) && (OptionHelper.isEmpty(file) && OptionHelper.isEmpty(resource)));
134    }
135
136    public void end(InterpretationContext ec, String name) {
137    }
138
139    public void finish(InterpretationContext ec) {
140    }
141}