View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.config.builder.impl;
18  
19  import java.io.IOException;
20  import java.io.OutputStream;
21  import java.io.StringWriter;
22  import java.lang.reflect.Constructor;
23  import java.util.List;
24  import java.util.Map;
25  import javax.xml.stream.XMLOutputFactory;
26  import javax.xml.stream.XMLStreamException;
27  import javax.xml.stream.XMLStreamWriter;
28  
29  import org.apache.logging.log4j.Level;
30  import org.apache.logging.log4j.core.Filter;
31  import org.apache.logging.log4j.core.LoggerContext;
32  import org.apache.logging.log4j.core.config.Configuration;
33  import org.apache.logging.log4j.core.config.ConfigurationException;
34  import org.apache.logging.log4j.core.config.ConfigurationSource;
35  import org.apache.logging.log4j.core.config.LoggerConfig;
36  import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
37  import org.apache.logging.log4j.core.config.builder.api.AppenderRefComponentBuilder;
38  import org.apache.logging.log4j.core.config.builder.api.Component;
39  import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder;
40  import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
41  import org.apache.logging.log4j.core.config.builder.api.CustomLevelComponentBuilder;
42  import org.apache.logging.log4j.core.config.builder.api.FilterComponentBuilder;
43  import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder;
44  import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder;
45  import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder;
46  import org.apache.logging.log4j.core.config.builder.api.ScriptComponentBuilder;
47  import org.apache.logging.log4j.core.config.builder.api.ScriptFileComponentBuilder;
48  import org.apache.logging.log4j.core.util.Throwables;
49  
50  /**
51   * @param <T> The BuiltConfiguration type.
52   * @since 2.4
53   */
54  public class DefaultConfigurationBuilder<T extends BuiltConfiguration> implements ConfigurationBuilder<T> {
55  
56      private static final String INDENT = "  ";
57      private static final String EOL = System.lineSeparator();
58      
59      private final Component root = new Component();
60      private Component loggers;
61      private Component appenders;
62      private Component filters;
63      private Component properties;
64      private Component customLevels;
65      private Component scripts;
66      private final Class<T> clazz;
67      private ConfigurationSource source;
68      private int monitorInterval;
69      private Level level;
70      private String verbosity;
71      private String destination;
72      private String packages;
73      private String shutdownFlag;
74      private String advertiser;
75      private LoggerContext loggerContext;
76      private String name;
77  
78      @SuppressWarnings("unchecked")
79      public DefaultConfigurationBuilder() {
80          this((Class<T>) BuiltConfiguration.class);
81          root.addAttribute("name", "Built");
82      }
83  
84      public DefaultConfigurationBuilder(final Class<T> clazz) {
85          if (clazz == null) {
86              throw new IllegalArgumentException("A Configuration class must be provided");
87          }
88          this.clazz = clazz;
89          final List<Component> components = root.getComponents();
90          properties = new Component("Properties");
91          components.add(properties);
92          scripts = new Component("Scripts");
93          components.add(scripts);
94          customLevels = new Component("CustomLevels");
95          components.add(customLevels);
96          filters = new Component("Filters");
97          components.add(filters);
98          appenders = new Component("Appenders");
99          components.add(appenders);
100         loggers = new Component("Loggers");
101         components.add(loggers);
102     }
103 
104     protected ConfigurationBuilder<T> add(final Component parent, final ComponentBuilder<?> builder) {
105         parent.getComponents().add(builder.build());
106         return this;
107     }
108 
109     @Override
110     public ConfigurationBuilder<T> add(final AppenderComponentBuilder builder) {
111         return add(appenders, builder);
112     }
113 
114     @Override
115     public ConfigurationBuilder<T> add(final CustomLevelComponentBuilder builder) {
116         return add(customLevels, builder);
117     }
118 
119     @Override
120     public ConfigurationBuilder<T> add(final FilterComponentBuilder builder) {
121         return add(filters, builder);
122     }
123 
124     @Override
125     public ConfigurationBuilder<T> add(final ScriptComponentBuilder builder) {
126         return add(scripts, builder);
127     }
128 
129     @Override
130     public ConfigurationBuilder<T> add(final ScriptFileComponentBuilder builder) {
131         return add(scripts, builder);
132     }
133 
134     @Override
135     public ConfigurationBuilder<T> add(final LoggerComponentBuilder builder) {
136         return add(loggers, builder);
137     }
138 
139     @Override
140     public ConfigurationBuilder<T> add(final RootLoggerComponentBuilder builder) {
141         for (final Component c : loggers.getComponents()) {
142             if (c.getPluginType().equals(LoggerConfig.ROOT)) {
143                 throw new ConfigurationException("Root Logger was previously defined");
144             }
145         }
146         return add(loggers, builder);
147     }
148 
149     @Override
150     public ConfigurationBuilder<T> addProperty(final String key, final String value) {
151         properties.addComponent(newComponent(key, "Property", value).build());
152         return this;
153     }
154 
155     @Override
156     public T build() {
157         return build(true);
158     }
159 
160     @Override
161     public T build(final boolean initialize) {
162         T configuration;
163         try {
164             if (source == null) {
165                 source = ConfigurationSource.NULL_SOURCE;
166             }
167             final Constructor<T> constructor = clazz.getConstructor(LoggerContext.class, ConfigurationSource.class, Component.class);
168             configuration = constructor.newInstance(loggerContext, source, root);
169             configuration.setMonitorInterval(monitorInterval);
170             configuration.getRootNode().getAttributes().putAll(root.getAttributes());
171             if (name != null) {
172                 configuration.setName(name);
173             }
174             if (level != null) {
175                 configuration.getStatusConfiguration().withStatus(level);
176             }
177             if (verbosity != null) {
178                 configuration.getStatusConfiguration().withVerbosity(verbosity);
179             }
180             if (destination != null) {
181                 configuration.getStatusConfiguration().withDestination(destination);
182             }
183             if (packages != null) {
184                 configuration.setPluginPackages(packages);
185             }
186             if (shutdownFlag != null) {
187                 configuration.setShutdownHook(shutdownFlag);
188             }
189             if (advertiser != null) {
190                 configuration.createAdvertiser(advertiser, source);
191             }
192         } catch (final Exception ex) {
193             throw new IllegalArgumentException("Invalid Configuration class specified", ex);
194         }
195         configuration.getStatusConfiguration().initialize();
196         if (initialize) {
197             configuration.initialize();
198         }
199         return configuration;
200     }
201 
202     @Override
203     public void writeXmlConfiguration(final OutputStream output) throws IOException {
204         try {
205             final XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(output);
206             writeXmlConfiguration(xmlWriter);
207             xmlWriter.close();
208         } catch (final XMLStreamException e) {
209             if (e.getNestedException() instanceof IOException) {
210                 throw (IOException)e.getNestedException();
211             }
212             Throwables.rethrow(e);
213         }
214     }
215 
216     @Override
217     public String toXmlConfiguration() {
218         final StringWriter sw = new StringWriter();
219         try {
220             final XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(sw);
221             writeXmlConfiguration(xmlWriter);
222             xmlWriter.close();
223         } catch (final XMLStreamException e) {
224             Throwables.rethrow(e);
225         }
226         return sw.toString();
227     }
228 
229     private void writeXmlConfiguration(final XMLStreamWriter xmlWriter) throws XMLStreamException {
230         xmlWriter.writeStartDocument();
231         xmlWriter.writeCharacters(EOL);
232 
233         xmlWriter.writeStartElement("Configuration");
234         if (name != null) {
235             xmlWriter.writeAttribute("name", name);
236         }
237         if (level != null) {
238             xmlWriter.writeAttribute("status", level.name());
239         }
240         if (verbosity != null) {
241             xmlWriter.writeAttribute("verbose", verbosity);
242         }
243         if (destination != null) {
244             xmlWriter.writeAttribute("dest", destination);
245         }
246         if (packages != null) {
247             xmlWriter.writeAttribute("packages", packages);
248         }
249         if (shutdownFlag != null) {
250             xmlWriter.writeAttribute("shutdownHook", shutdownFlag);
251         }
252         if (advertiser != null) {
253             xmlWriter.writeAttribute("advertiser", advertiser);
254         }
255         if (monitorInterval > 0) {
256             xmlWriter.writeAttribute("monitorInterval", String.valueOf(monitorInterval));
257         }
258 
259         xmlWriter.writeCharacters(EOL);
260 
261         writeXmlSection(xmlWriter, properties);
262         writeXmlSection(xmlWriter, scripts);
263         writeXmlSection(xmlWriter, customLevels);
264         if (filters.getComponents().size() == 1) {
265             writeXmlComponent(xmlWriter, filters.getComponents().get(0), 1);
266         } else if (filters.getComponents().size() > 1) {
267             writeXmlSection(xmlWriter, filters);
268         }
269         writeXmlSection(xmlWriter, appenders);
270         writeXmlSection(xmlWriter, loggers);
271 
272         xmlWriter.writeEndElement(); // "Configuration"
273         xmlWriter.writeCharacters(EOL);
274 
275         xmlWriter.writeEndDocument();
276     }
277 
278     private void writeXmlSection(final XMLStreamWriter xmlWriter, final Component component) throws XMLStreamException {
279         if (!component.getAttributes().isEmpty() || !component.getComponents().isEmpty() || component.getValue() != null) {
280             writeXmlComponent(xmlWriter, component, 1);
281         }
282     }
283 
284     private void writeXmlComponent(final XMLStreamWriter xmlWriter, final Component component, final int nesting) throws XMLStreamException {
285         if (!component.getComponents().isEmpty() || component.getValue() != null) {
286             writeXmlIndent(xmlWriter, nesting);
287             xmlWriter.writeStartElement(component.getPluginType());
288             writeXmlAttributes(xmlWriter, component);
289             if (!component.getComponents().isEmpty()) {
290                 xmlWriter.writeCharacters(EOL);
291             }
292             for (final Component subComponent : component.getComponents()) {
293                 writeXmlComponent(xmlWriter, subComponent, nesting + 1);
294             }
295             if (component.getValue() != null) {
296                 xmlWriter.writeCharacters(component.getValue());
297             }
298             if (!component.getComponents().isEmpty()) {
299                 writeXmlIndent(xmlWriter, nesting);
300             }
301             xmlWriter.writeEndElement();
302         } else {
303             writeXmlIndent(xmlWriter, nesting);
304             xmlWriter.writeEmptyElement(component.getPluginType());
305             writeXmlAttributes(xmlWriter, component);
306         }
307         xmlWriter.writeCharacters(EOL);
308     }
309 
310     private void writeXmlIndent(final XMLStreamWriter xmlWriter, final int nesting) throws XMLStreamException {
311         for (int i = 0; i < nesting; i++) {
312             xmlWriter.writeCharacters(INDENT);
313         }
314     }
315 
316     private void writeXmlAttributes(final XMLStreamWriter xmlWriter, final Component component) throws XMLStreamException {
317         for (final Map.Entry<String, String> attribute : component.getAttributes().entrySet()) {
318             xmlWriter.writeAttribute(attribute.getKey(), attribute.getValue());
319         }
320     }
321 
322     @Override
323     public ScriptComponentBuilder newScript(final String name, final String language, final String text) {
324         return new DefaultScriptComponentBuilder(this, name, language, text);
325     }
326 
327 
328     @Override
329     public ScriptFileComponentBuilder newScriptFile(final String path) {
330         return new DefaultScriptFileComponentBuilder(this, path, path);
331     }
332 
333     @Override
334     public ScriptFileComponentBuilder newScriptFile(final String name, final String path) {
335         return new DefaultScriptFileComponentBuilder(this, name, path);
336     }
337 
338     @Override
339     public AppenderComponentBuilder newAppender(final String name, final String type) {
340         return new DefaultAppenderComponentBuilder(this, name, type);
341     }
342 
343     @Override
344     public AppenderRefComponentBuilder newAppenderRef(final String ref) {
345         return new DefaultAppenderRefComponentBuilder(this, ref);
346     }
347 
348     @Override
349     public LoggerComponentBuilder newAsyncLogger(final String name, final Level level) {
350         return new DefaultLoggerComponentBuilder(this, name, level.toString(), "AsyncLogger");
351     }
352 
353     @Override
354     public LoggerComponentBuilder newAsyncLogger(final String name, final Level level, final boolean includeLocation) {
355         return new DefaultLoggerComponentBuilder(this, name, level.toString(), "AsyncLogger", includeLocation);
356     }
357 
358     @Override
359     public LoggerComponentBuilder newAsyncLogger(final String name, final String level) {
360         return new DefaultLoggerComponentBuilder(this, name, level, "AsyncLogger");
361     }
362 
363     @Override
364     public LoggerComponentBuilder newAsyncLogger(final String name, final String level, final boolean includeLocation) {
365         return new DefaultLoggerComponentBuilder(this, name, level, "AsyncLogger");
366     }
367 
368     @Override
369     public RootLoggerComponentBuilder newAsyncRootLogger(final Level level) {
370         return new DefaultRootLoggerComponentBuilder(this, level.toString(), "AsyncRoot");
371     }
372 
373     @Override
374     public RootLoggerComponentBuilder newAsyncRootLogger(final Level level, final boolean includeLocation) {
375         return new DefaultRootLoggerComponentBuilder(this, level.toString(), "AsyncRoot", includeLocation);
376     }
377 
378     @Override
379     public RootLoggerComponentBuilder newAsyncRootLogger(final String level) {
380         return new DefaultRootLoggerComponentBuilder(this, level, "AsyncRoot");
381     }
382 
383     @Override
384     public RootLoggerComponentBuilder newAsyncRootLogger(final String level, final boolean includeLocation) {
385         return new DefaultRootLoggerComponentBuilder(this, level, "AsyncRoot", includeLocation);
386     }
387 
388 
389     @Override
390     public <B extends ComponentBuilder<B>> ComponentBuilder<B> newComponent(final String type) {
391         return new DefaultComponentBuilder<>(this, type);
392     }
393 
394     @Override
395     public <B extends ComponentBuilder<B>> ComponentBuilder<B> newComponent(final String name, final String type) {
396         return new DefaultComponentBuilder<>(this, name, type);
397     }
398 
399     @Override
400     public <B extends ComponentBuilder<B>> ComponentBuilder<B> newComponent(final String name, final String type,
401                                                                             final String value) {
402         return new DefaultComponentBuilder<>(this, name, type, value);
403     }
404 
405 
406     @Override
407     public CustomLevelComponentBuilder newCustomLevel(final String name, final int level) {
408         return new DefaultCustomLevelComponentBuilder(this, name, level);
409     }
410 
411     @Override
412     public FilterComponentBuilder newFilter(final String type, final Filter.Result onMatch,
413                                             final Filter.Result onMisMatch) {
414         return new DefaultFilterComponentBuilder(this, type, onMatch.name(), onMisMatch.name());
415     }
416 
417     @Override
418     public FilterComponentBuilder newFilter(final String type, final String onMatch, final String onMisMatch) {
419         return new DefaultFilterComponentBuilder(this, type, onMatch, onMisMatch);
420     }
421 
422     @Override
423     public LayoutComponentBuilder newLayout(final String type) {
424         return new DefaultLayoutComponentBuilder(this, type);
425     }
426 
427 
428     @Override
429     public LoggerComponentBuilder newLogger(final String name, final Level level) {
430         return new DefaultLoggerComponentBuilder(this, name, level.toString());
431     }
432 
433     @Override
434     public LoggerComponentBuilder newLogger(final String name, final Level level, final boolean includeLocation) {
435         return new DefaultLoggerComponentBuilder(this, name, level.toString(), includeLocation);
436     }
437 
438     @Override
439     public LoggerComponentBuilder newLogger(final String name, final String level) {
440         return new DefaultLoggerComponentBuilder(this, name, level);
441     }
442 
443     @Override
444     public LoggerComponentBuilder newLogger(final String name, final String level, final boolean includeLocation) {
445         return new DefaultLoggerComponentBuilder(this, name, level, includeLocation);
446     }
447 
448     @Override
449     public RootLoggerComponentBuilder newRootLogger(final Level level) {
450         return new DefaultRootLoggerComponentBuilder(this, level.toString());
451     }
452 
453     @Override
454     public RootLoggerComponentBuilder newRootLogger(final Level level, final boolean includeLocation) {
455         return new DefaultRootLoggerComponentBuilder(this, level.toString(), includeLocation);
456     }
457 
458     @Override
459     public RootLoggerComponentBuilder newRootLogger(final String level) {
460         return new DefaultRootLoggerComponentBuilder(this, level);
461     }
462 
463     @Override
464     public RootLoggerComponentBuilder newRootLogger(final String level, final boolean includeLocation) {
465         return new DefaultRootLoggerComponentBuilder(this, level, includeLocation);
466     }
467 
468     @Override
469     public ConfigurationBuilder<T> setAdvertiser(final String advertiser) {
470         this.advertiser = advertiser;
471         return this;
472     }
473 
474     /**
475      * Set the name of the configuration.
476      *
477      * @param name the name of the {@link Configuration}. By default is {@code "Assembled"}.
478      * @return this builder instance
479      */
480     @Override
481     public ConfigurationBuilder<T> setConfigurationName(final String name) {
482         this.name = name;
483         return this;
484     }
485 
486     /**
487      * Set the ConfigurationSource.
488      *
489      * @param configurationSource the {@link ConfigurationSource}
490      * @return this builder instance
491      */
492     @Override
493     public ConfigurationBuilder<T> setConfigurationSource(final ConfigurationSource configurationSource) {
494         source = configurationSource;
495         return this;
496     }
497 
498     @Override
499     public ConfigurationBuilder<T> setMonitorInterval(final String intervalSeconds) {
500         monitorInterval = Integer.parseInt(intervalSeconds);
501         return this;
502     }
503 
504     @Override
505     public ConfigurationBuilder<T> setPackages(final String packages) {
506         this.packages = packages;
507         return this;
508     }
509 
510     @Override
511     public ConfigurationBuilder<T> setShutdownHook(final String flag) {
512         this.shutdownFlag = flag;
513         return this;
514     }
515 
516     @Override
517     public ConfigurationBuilder<T> setStatusLevel(final Level level) {
518         this.level = level;
519         return this;
520     }
521 
522     @Override
523     public ConfigurationBuilder<T> setVerbosity(final String verbosity) {
524         this.verbosity = verbosity;
525         return this;
526     }
527 
528     @Override
529     public ConfigurationBuilder<T> setDestination(final String destination) {
530         this.destination = destination;
531         return this;
532     }
533 
534     @Override
535     public void setLoggerContext(final LoggerContext loggerContext) {
536         this.loggerContext = loggerContext;
537     }
538 
539     @Override
540     public ConfigurationBuilder<T> addRootProperty(final String key, final String value) {
541         root.getAttributes().put(key, value);
542         return this;
543     }
544 
545 }