001package ch.qos.logback.core.model.processor;
002
003import java.io.FileInputStream;
004import java.io.FileNotFoundException;
005import java.io.IOException;
006import java.io.InputStream;
007import java.net.URL;
008import java.util.Properties;
009
010import ch.qos.logback.core.Context;
011import ch.qos.logback.core.joran.action.ActionUtil;
012import ch.qos.logback.core.joran.action.ActionUtil.Scope;
013import ch.qos.logback.core.model.Model;
014import ch.qos.logback.core.model.ModelUtil;
015import ch.qos.logback.core.model.PropertyModel;
016import ch.qos.logback.core.pattern.util.RegularEscapeUtil;
017import ch.qos.logback.core.util.Loader;
018import ch.qos.logback.core.util.OptionHelper;
019
020public class PropertyModelHandler extends ModelHandlerBase {
021
022    public static final String INVALID_ATTRIBUTES = "In <property> element, either the \"file\" attribute alone, or "
023            + "the \"resource\" element alone, or both the \"name\" and \"value\" attributes must be set.";
024
025    public PropertyModelHandler(Context context) {
026        super(context);
027    }
028
029    static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) {
030        return new PropertyModelHandler(context);
031    }
032
033    @Override
034    protected Class<PropertyModel> getSupportedModelClass() {
035        return PropertyModel.class;
036    }
037
038    @Override
039    public void handle(ModelInterpretationContext interpretationContext, Model model) {
040
041        PropertyModel propertyModel = (PropertyModel) model;
042
043        Scope scope = ActionUtil.stringToScope(propertyModel.getScopeStr());
044
045        if (checkFileAttributeSanity(propertyModel)) {
046            String file = propertyModel.getFile();
047            file = interpretationContext.subst(file);
048            try (FileInputStream istream = new FileInputStream(file)) {
049                loadAndSetProperties(interpretationContext, istream, scope);
050            } catch (FileNotFoundException e) {
051                addError("Could not find properties file [" + file + "].");
052            } catch (IOException|IllegalArgumentException e1) { // IllegalArgumentException is thrown in case the file
053                                                                // is badly malformed, i.e a binary.
054                addError("Could not read properties file [" + file + "].", e1);
055            }
056        } else if (checkResourceAttributeSanity(propertyModel)) {
057            String resource = propertyModel.getResource();
058            resource = interpretationContext.subst(resource);
059            URL resourceURL = Loader.getResourceBySelfClassLoader(resource);
060            if (resourceURL == null) {
061                addError("Could not find resource [" + resource + "].");
062            } else {
063                try ( InputStream istream = resourceURL.openStream();) {
064                    loadAndSetProperties(interpretationContext, istream, scope);
065                } catch (IOException e) {
066                    addError("Could not read resource file [" + resource + "].", e);
067                }
068            }
069        } else if (checkValueNameAttributesSanity(propertyModel)) {
070            // earlier versions performed Java '\' escapes for '\\' '\t' etc. Howevver, there is no
071            // need to do this. See RegularEscapeUtil.__UNUSED__basicEscape
072            String value = propertyModel.getValue();
073
074            // now remove both leading and trailing spaces
075            value = value.trim();
076            value = interpretationContext.subst(value);
077            ActionUtil.setProperty(interpretationContext, propertyModel.getName(), value, scope);
078
079        } else {
080            addError(INVALID_ATTRIBUTES);
081        }
082    }
083
084    void loadAndSetProperties(ModelInterpretationContext mic, InputStream istream, Scope scope) throws IOException {
085        Properties props = new Properties();
086        props.load(istream);
087        ModelUtil.setProperties(mic, props, scope);
088    }
089
090    boolean checkFileAttributeSanity(PropertyModel propertyModel) {
091        String file = propertyModel.getFile();
092        String name = propertyModel.getName();
093        String value = propertyModel.getValue();
094        String resource = propertyModel.getResource();
095
096        return !(OptionHelper.isNullOrEmpty(file)) && (OptionHelper.isNullOrEmpty(name)
097                && OptionHelper.isNullOrEmpty(value) && OptionHelper.isNullOrEmpty(resource));
098    }
099
100    boolean checkResourceAttributeSanity(PropertyModel propertyModel) {
101        String file = propertyModel.getFile();
102        String name = propertyModel.getName();
103        String value = propertyModel.getValue();
104        String resource = propertyModel.getResource();
105
106        return !(OptionHelper.isNullOrEmpty(resource)) && (OptionHelper.isNullOrEmpty(name)
107                && OptionHelper.isNullOrEmpty(value) && OptionHelper.isNullOrEmpty(file));
108    }
109
110    boolean checkValueNameAttributesSanity(PropertyModel propertyModel) {
111        String file = propertyModel.getFile();
112        String name = propertyModel.getName();
113        String value = propertyModel.getValue();
114        String resource = propertyModel.getResource();
115        return (!(OptionHelper.isNullOrEmpty(name) || OptionHelper.isNullOrEmpty(value))
116                && (OptionHelper.isNullOrEmpty(file) && OptionHelper.isNullOrEmpty(resource)));
117    }
118
119}