001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.config.properties;
018
019import org.apache.logging.log4j.Level;
020import org.apache.logging.log4j.core.config.ConfigurationException;
021import org.apache.logging.log4j.core.config.ConfigurationFactory;
022import org.apache.logging.log4j.core.config.ConfigurationSource;
023import org.apache.logging.log4j.core.config.LoggerConfig;
024import org.apache.logging.log4j.core.config.Order;
025import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
026import org.apache.logging.log4j.core.config.builder.api.AppenderRefComponentBuilder;
027import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder;
028import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
029import org.apache.logging.log4j.core.config.builder.api.FilterComponentBuilder;
030import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder;
031import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder;
032import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder;
033import org.apache.logging.log4j.core.config.builder.api.ScriptComponentBuilder;
034import org.apache.logging.log4j.core.config.builder.api.ScriptFileComponentBuilder;
035import org.apache.logging.log4j.core.config.plugins.Plugin;
036import org.apache.logging.log4j.util.PropertiesUtil;
037import org.apache.logging.log4j.util.Strings;
038
039import java.io.IOException;
040import java.io.InputStream;
041import java.util.Properties;
042
043/**
044 * Creates a PropertiesConfiguration from a properties file.
045 * 
046 * @since 2.4
047 */
048@Plugin(name = "PropertiesConfigurationFactory", category = ConfigurationFactory.CATEGORY)
049@Order(8)
050public class PropertiesConfigurationFactory extends ConfigurationFactory {
051    private static final String ADVERTISER_KEY = "advertiser";
052    private static final String STATUS_KEY = "status";
053    private static final String SHUTDOWN_HOOK = "shutdownHook";
054    private static final String VERBOSE = "verbose";
055    private static final String PACKAGES = "packages";
056    private static final String CONFIG_NAME = "name";
057    private static final String MONITOR_INTERVAL = "monitorInterval";
058    private static final String CONFIG_TYPE = "type";
059
060    @Override
061    protected String[] getSupportedTypes() {
062        return new String[] {".properties"};
063    }
064
065    @Override
066    public PropertiesConfiguration getConfiguration(ConfigurationSource source) {
067        final InputStream configStream = source.getInputStream();
068        Properties properties = new Properties();
069        try {
070            properties.load(configStream);
071        } catch (IOException ioe) {
072            throw new ConfigurationException("Unable to load " + source.toString(), ioe);
073        }
074        ConfigurationBuilder<PropertiesConfiguration> builder = newConfigurationBuilder(PropertiesConfiguration.class);
075        String value = properties.getProperty(STATUS_KEY);
076        if (value != null) {
077            builder.setStatusLevel(Level.toLevel(value, Level.ERROR));
078        } else {
079            builder.setStatusLevel(Level.ERROR);
080        }
081        value = properties.getProperty(SHUTDOWN_HOOK);
082        if (value != null) {
083            builder.setShutdownHook(value);
084        }
085        value = properties.getProperty(VERBOSE);
086        if (value != null) {
087            builder.setVerbosity(value);
088        }
089        value = properties.getProperty(PACKAGES);
090        if (value != null) {
091            builder.setPackages(value);
092        }
093        value = properties.getProperty(CONFIG_NAME);
094        if (value != null) {
095            builder.setConfigurationName(value);
096        }
097        value = properties.getProperty(MONITOR_INTERVAL);
098        if (value != null) {
099            builder.setMonitorInterval(value);
100        }
101        value = properties.getProperty(ADVERTISER_KEY);
102        if (value != null) {
103            builder.setAdvertiser(value);
104        }
105        Properties props = PropertiesUtil.extractSubset(properties, "property");
106        for (String key : props.stringPropertyNames()) {
107            builder.addProperty(key, props.getProperty(key));
108        }
109
110        String scriptProp = properties.getProperty("scripts");
111        if (scriptProp != null) {
112            String[] scriptNames = scriptProp.split(",");
113            for (String scriptName : scriptNames) {
114                String name = scriptName.trim();
115                Properties scriptProps = PropertiesUtil.extractSubset(properties, "script." + name);
116                String type = scriptProps.getProperty("type");
117                if (type == null) {
118                    throw new ConfigurationException("No type provided for script - must be Script or ScriptFile");
119                }
120                scriptProps.remove("type");
121                if (type.equalsIgnoreCase("script")) {
122                    builder.add(createScript(builder, name, scriptProps));
123                } else {
124                    builder.add(createScriptFile(builder, name, scriptProps));
125                }
126            }
127        }
128
129        Properties levelProps = PropertiesUtil.extractSubset(properties, "customLevel");
130        if (levelProps.size() > 0) {
131            for (String key : levelProps.stringPropertyNames()) {
132                builder.add(builder.newCustomLevel(key, Integer.parseInt(props.getProperty(key))));
133            }
134        }
135
136        String filterProp = properties.getProperty("filters");
137        if (filterProp != null) {
138            String[] filterNames = filterProp.split(",");
139            for (String filterName : filterNames) {
140                String name = filterName.trim();
141                builder.add(createFilter(builder, name, PropertiesUtil.extractSubset(properties, "filter." + name)));
142            }
143        }
144        String appenderProp = properties.getProperty("appenders");
145        if (appenderProp != null) {
146            String[] appenderNames = appenderProp.split(",");
147            for (String appenderName : appenderNames) {
148                String name = appenderName.trim();
149                builder.add(createAppender(builder, name, PropertiesUtil.extractSubset(properties, "appender." +
150                        name)));
151            }
152        }
153        String loggerProp = properties.getProperty("loggers");
154        if (loggerProp != null) {
155            String[] loggerNames = loggerProp.split(",");
156            for (String loggerName : loggerNames) {
157                String name = loggerName.trim();
158                if (!name.equals(LoggerConfig.ROOT)) {
159                    builder.add(createLogger(builder, name, PropertiesUtil.extractSubset(properties, "logger." +
160                            name)));
161                }
162            }
163        }
164
165        props = PropertiesUtil.extractSubset(properties, "rootLogger");
166        if (props.size() > 0) {
167            builder.add(createRootLogger(builder, props));
168        }
169
170        return builder.build();
171    }
172
173
174    private ScriptComponentBuilder createScript(ConfigurationBuilder<PropertiesConfiguration> builder, String key,
175                                                Properties properties) {
176        String name = properties.getProperty("name");
177        if (name != null) {
178            properties.remove("name");
179        }
180        String language = properties.getProperty("language");
181        if (language!= null) {
182            properties.remove("language");
183        }
184        String text = properties.getProperty("text");
185        if (text != null) {
186            properties.remove("text");
187        }
188        ScriptComponentBuilder scriptBuilder = builder.newScript(name, language, text);
189        processRemainingProperties(scriptBuilder, key, properties);
190        return scriptBuilder;
191    }
192
193    private ScriptFileComponentBuilder createScriptFile(ConfigurationBuilder<PropertiesConfiguration> builder, String key,
194                                                Properties properties) {
195        String name = properties.getProperty("name");
196        if (name != null) {
197            properties.remove("name");
198        }
199        String path = properties.getProperty("path");
200        if (path != null) {
201            properties.remove("path");
202        }
203        ScriptFileComponentBuilder scriptFileBuilder = builder.newScriptFile(name, path);
204        processRemainingProperties(scriptFileBuilder, key, properties);
205        return scriptFileBuilder;
206    }
207
208
209    private AppenderComponentBuilder createAppender(ConfigurationBuilder<PropertiesConfiguration> builder, String key,
210            Properties properties) {
211        String name = properties.getProperty(CONFIG_NAME);
212        if (Strings.isEmpty(name)) {
213            throw new ConfigurationException("No name attribute provided for Appender " + key);
214        }
215        properties.remove(CONFIG_NAME);
216        String type = properties.getProperty(CONFIG_TYPE);
217        if (Strings.isEmpty(type)) {
218            throw new ConfigurationException("No type attribute provided for Appender " + key);
219        }
220        properties.remove(CONFIG_TYPE);
221        AppenderComponentBuilder appenderBuilder = builder.newAppender(name, type);
222        String filters = properties.getProperty("filters");
223        if (filters != null) {
224            properties.remove("filters");
225            String[] filterNames = filters.split(",");
226            for (String filterName : filterNames) {
227                filterName = filterName.trim();
228                Properties filterProps = PropertiesUtil.extractSubset(properties, "filter." + filterName);
229                appenderBuilder.add(createFilter(builder, filterName, filterProps));
230            }
231        }
232        Properties layoutProps = PropertiesUtil.extractSubset(properties, "layout");
233        if (layoutProps.size() > 0) {
234            appenderBuilder.add(createLayout(builder, name, layoutProps));
235        }
236
237        processRemainingProperties(appenderBuilder, name, properties);
238        return appenderBuilder;
239    }
240
241    private FilterComponentBuilder createFilter(ConfigurationBuilder<PropertiesConfiguration> builder, String key,
242            Properties properties) {
243        String type = properties.getProperty(CONFIG_TYPE);
244        if (Strings.isEmpty(type)) {
245            throw new ConfigurationException("No type attribute provided for Appender " + key);
246        }
247        properties.remove(CONFIG_TYPE);
248        String onMatch = properties.getProperty("onMatch");
249        if (onMatch != null) {
250            properties.remove("onMatch");
251        }
252        String onMisMatch = properties.getProperty("onMisMatch");
253        if (onMisMatch != null) {
254            properties.remove("onMisMatch");
255        }
256        FilterComponentBuilder filterBuilder = builder.newFilter(type, onMatch, onMisMatch);
257        processRemainingProperties(filterBuilder, key, properties);
258        return filterBuilder;
259    }
260
261    private AppenderRefComponentBuilder createAppenderRef(ConfigurationBuilder<PropertiesConfiguration> builder,
262            String key, Properties properties) {
263        String ref = properties.getProperty("ref");
264        if (Strings.isEmpty(ref)) {
265            throw new ConfigurationException("No ref attribute provided for AppenderRef " + key);
266        }
267        properties.remove("ref");
268        AppenderRefComponentBuilder appenderRefBuilder = builder.newAppenderRef(ref);
269        String level = properties.getProperty("level");
270        if (!Strings.isEmpty(level)) {
271            appenderRefBuilder.addAttribute("level", level);
272        }
273        String filters = properties.getProperty("filters");
274        if (filters != null) {
275            properties.remove("filters");
276            String[] filterNames = filters.split(",");
277            for (String filterName : filterNames) {
278                filterName = filterName.trim();
279                Properties filterProps = PropertiesUtil.extractSubset(properties, "filter." + filterName);
280                appenderRefBuilder.add(createFilter(builder, filterName, filterProps));
281            }
282        }
283        return appenderRefBuilder;
284    }
285
286    private LoggerComponentBuilder createLogger(ConfigurationBuilder<PropertiesConfiguration> builder, String key,
287            Properties properties) {
288        String name = properties.getProperty(CONFIG_NAME);
289        if (Strings.isEmpty(name)) {
290            throw new ConfigurationException("No name attribute provided for Logger " + key);
291        }
292        properties.remove(CONFIG_NAME);
293        String level = properties.getProperty("level");
294        if (level != null) {
295            properties.remove("level");
296        }
297        LoggerComponentBuilder loggerBuilder;
298        String type = properties.getProperty(CONFIG_TYPE);
299        if (type != null) {
300            if (type.equalsIgnoreCase("asyncLogger")) {
301                loggerBuilder = builder.newAsyncLogger(name, level);
302            } else {
303                throw new ConfigurationException("Unknown Logger type " + type + " for Logger " + name);
304            }
305        } else {
306            loggerBuilder = builder.newLogger(name, level);
307        }
308        String appenderRefs = properties.getProperty("appenderRefs");
309        if (appenderRefs != null) {
310            properties.remove("appenderRefs");
311            String[] refNames = appenderRefs.split(",");
312            for (String appenderRef : refNames) {
313                appenderRef = appenderRef.trim();
314                Properties refProps = PropertiesUtil.extractSubset(properties, "appenderRef." + appenderRef);
315                loggerBuilder.add(createAppenderRef(builder, appenderRef, refProps));
316            }
317        }
318        String filters = properties.getProperty("filters");
319        if (filters != null) {
320            properties.remove("filters");
321            String[] filterNames = filters.split(",");
322            for (String filterName : filterNames) {
323                filterName = filterName.trim();
324                Properties filterProps = PropertiesUtil.extractSubset(properties, "filter." + filterName);
325                loggerBuilder.add(createFilter(builder, filterName, filterProps));
326            }
327        }
328        String additivity = properties.getProperty("additivity");
329        if (!Strings.isEmpty(additivity)) {
330            loggerBuilder.addAttribute("additivity", additivity);
331        }
332        return loggerBuilder;
333    }
334
335    private RootLoggerComponentBuilder createRootLogger(ConfigurationBuilder<PropertiesConfiguration> builder,
336            Properties properties) {
337        String level = properties.getProperty("level");
338        if (level != null) {
339            properties.remove("level");
340        }
341        RootLoggerComponentBuilder loggerBuilder;
342        String type = properties.getProperty(CONFIG_TYPE);
343        if (type != null) {
344            if (type.equalsIgnoreCase("asyncRoot")) {
345                loggerBuilder = builder.newAsyncRootLogger(level);
346            } else {
347                throw new ConfigurationException("Unknown Logger type for root logger" + type);
348            }
349        } else {
350            loggerBuilder = builder.newRootLogger(level);
351        }
352        String appenderRefs = properties.getProperty("appenderRefs");
353        if (appenderRefs != null) {
354            properties.remove("appenderRefs");
355            String[] refNames = appenderRefs.split(",");
356            for (String appenderRef : refNames) {
357                appenderRef = appenderRef.trim();
358                Properties refProps = PropertiesUtil.extractSubset(properties, "appenderRef." + appenderRef);
359                loggerBuilder.add(createAppenderRef(builder, appenderRef, refProps));
360            }
361        }
362        String filters = properties.getProperty("filters");
363        if (filters != null) {
364            properties.remove("filters");
365            String[] filterNames = filters.split(",");
366            for (String filterName : filterNames) {
367                filterName = filterName.trim();
368                Properties filterProps = PropertiesUtil.extractSubset(properties, "filter." + filterName);
369                loggerBuilder.add(createFilter(builder, filterName, filterProps));
370            }
371        }
372        return loggerBuilder;
373    }
374
375    private LayoutComponentBuilder createLayout(ConfigurationBuilder<PropertiesConfiguration> builder,
376            String appenderName, Properties properties) {
377        String type = properties.getProperty(CONFIG_TYPE);
378        if (Strings.isEmpty(type)) {
379            throw new ConfigurationException("No type attribute provided for Layout on Appender " + appenderName);
380        }
381        properties.remove(CONFIG_TYPE);
382        LayoutComponentBuilder layoutBuilder = builder.newLayout(type);
383        processRemainingProperties(layoutBuilder, appenderName, properties);
384        return layoutBuilder;
385    }
386
387    private <B extends ComponentBuilder<B>> ComponentBuilder<B> createComponent(ComponentBuilder<?> parent, String key,
388            Properties properties) {
389        String name = properties.getProperty(CONFIG_NAME);
390        if (name != null) {
391            properties.remove(CONFIG_NAME);
392        }
393        String type = properties.getProperty(CONFIG_TYPE);
394        if (Strings.isEmpty(type)) {
395            throw new ConfigurationException("No type attribute provided for component " + key);
396        }
397        properties.remove(CONFIG_TYPE);
398        ComponentBuilder<B> componentBuilder = parent.getBuilder().newComponent(name, type);
399        processRemainingProperties(componentBuilder, name, properties);
400        return componentBuilder;
401    }
402
403    private void processRemainingProperties(ComponentBuilder<?> builder, String name, Properties properties) {
404        while (properties.size() > 0) {
405            String propertyName = properties.stringPropertyNames().iterator().next();
406
407            int index = propertyName.indexOf('.');
408            if (index > 0) {
409                String prefix = propertyName.substring(0, index);
410                Properties componentProperties = PropertiesUtil.extractSubset(properties, prefix);
411                builder.addComponent(createComponent(builder, prefix, componentProperties));
412            } else {
413                builder.addAttribute(propertyName, properties.getProperty(propertyName));
414                properties.remove(propertyName);
415            }
416        }
417    }
418}