001package org.apache.archiva.redback.common.config.acc2;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.archiva.redback.common.config.api.ConfigRegistry;
023import org.apache.archiva.redback.common.config.api.RegistryException;
024import org.apache.archiva.redback.common.config.api.RegistryListener;
025import org.apache.commons.configuration2.*;
026import org.apache.commons.configuration2.builder.ConfigurationBuilder;
027import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
028import org.apache.commons.configuration2.builder.combined.CombinedConfigurationBuilder;
029import org.apache.commons.configuration2.builder.fluent.Parameters;
030import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
031import org.apache.commons.configuration2.event.ConfigurationEvent;
032import org.apache.commons.configuration2.event.EventSource;
033import org.apache.commons.configuration2.ex.ConfigurationException;
034import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
035import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
036import org.apache.commons.configuration2.interpol.DefaultLookups;
037import org.apache.commons.configuration2.interpol.InterpolatorSpecification;
038import org.apache.commons.configuration2.io.ClasspathLocationStrategy;
039import org.apache.commons.configuration2.io.FileHandler;
040import org.apache.commons.configuration2.io.FileLocatorUtils;
041import org.apache.commons.configuration2.io.FileSystem;
042import org.apache.commons.configuration2.tree.DefaultExpressionEngine;
043import org.apache.commons.configuration2.tree.DefaultExpressionEngineSymbols;
044import org.apache.commons.configuration2.tree.NodeCombiner;
045import org.apache.commons.configuration2.tree.UnionCombiner;
046import org.apache.commons.lang3.StringUtils;
047import org.slf4j.Logger;
048import org.slf4j.LoggerFactory;
049import org.springframework.stereotype.Service;
050
051import javax.annotation.PostConstruct;
052import java.io.*;
053import java.net.MalformedURLException;
054import java.net.URL;
055import java.nio.file.Path;
056import java.util.*;
057import java.util.regex.Matcher;
058import java.util.regex.Pattern;
059import java.util.stream.Collectors;
060import java.util.stream.StreamSupport;
061
062/**
063 * Implementation of the registry component using
064 * <a href="https://commons.apache.org/proper/commons-configuration/index.html">Commons Configuration 2</a>.
065 * The use of Commons Configuration enables a variety of sources to be used, including XML files, properties, JNDI, JDBC, etc.
066 * <p/>
067 * The component can be configured using the {@link #combinedConfigurationDefinition} configuration item, the content of which should take
068 * the format of an input to the Commons Configuration
069 * <a href="http://commons.apache.org/commons/configuration/howto_configurationbuilder.html">configuration
070 * builder</a>.
071 */
072@Service( "acc2-configuration" )
073public class CommonsConfigurationRegistry
074    implements ConfigRegistry
075{
076    private static final Pattern DOT_NAME_PATTERN = Pattern.compile( "([^.]+)(\\..*)*" );
077
078    /**
079     * The combined configuration instance that houses the registry.
080     */
081    private Configuration configuration;
082
083
084    private ConfigurationBuilder<? extends Configuration> configurationBuilder;
085    private boolean combined = true;
086    private final Map<String, ConfigurationBuilder<? extends Configuration>> builderMap = new HashMap<>( );
087
088    private final Logger logger = LoggerFactory.getLogger( getClass( ) );
089
090    private String propertyDelimiter = ".";
091
092    private boolean addSystemProperties = false;
093
094    final CfgListener listener = new CfgListener( this );
095
096    /**
097     * The configuration properties for the registry. This should take the format of an input to the Commons
098     * Configuration
099     * <a href="https://commons.apache.org/proper/commons-configuration/userguide/howto_combinedbuilder.html#The_configuration_definition_file">configuration
100     * builder</a>.
101     */
102    private String combinedConfigurationDefinition;
103
104
105    public CommonsConfigurationRegistry( )
106    {
107        // Default constructor
108    }
109
110
111    public CommonsConfigurationRegistry( CombinedConfiguration configuration, CombinedConfigurationBuilder configurationBuilder )
112    {
113        if ( configuration == null )
114        {
115            throw new NullPointerException( "configuration can not be null" );
116        }
117        if ( configurationBuilder == null )
118        {
119            throw new NullPointerException( "configuration builder cannot be null for a combined configuration" );
120        }
121        this.combined = true;
122        setConfiguration(configuration);
123        this.configurationBuilder = configurationBuilder;
124    }
125
126    @SuppressWarnings( "WeakerAccess" )
127    public CommonsConfigurationRegistry( Configuration configuration, ConfigurationBuilder<? extends Configuration> configurationBuilder )
128    {
129        if ( configuration == null )
130        {
131            throw new NullPointerException( "configuration can not be null" );
132        }
133        setConfiguration(configuration);
134        this.configurationBuilder = configurationBuilder;
135        if (configuration instanceof CombinedConfiguration) {
136            this.combined = true;
137        }
138    }
139
140    public void setConfiguration( Configuration configuration )
141    {
142        this.configuration = configuration;
143        if (configuration instanceof EventSource ) {
144            EventSource evs = (EventSource) configuration;
145            evs.removeEventListener( ConfigurationEvent.ANY, listener );
146            evs.addEventListener( ConfigurationEvent.ANY, listener );
147            evs.addEventListener( ConfigurationEvent.SUBNODE_CHANGED, listener );
148        }
149    }
150
151    @Override
152    public String dump( )
153    {
154        StringBuilder buffer = new StringBuilder( "Configuration Dump:\n");
155        Iterable<String> it = () -> configuration.getKeys();
156        return buffer.append(StreamSupport.stream( it.spliterator(), false ).map( k ->
157            "\""+k+"\" = \""+configuration.getProperty( k ).toString() + "\"").collect(Collectors.joining( "\n" ) )).toString();
158    }
159
160    @Override
161    public boolean isEmpty( )
162    {
163        return configuration.isEmpty( );
164    }
165
166    @Override
167    public ConfigRegistry getSubset( String key ) throws RegistryException
168    {
169        if (configuration instanceof BaseHierarchicalConfiguration) {
170            BaseHierarchicalConfiguration cfg = (BaseHierarchicalConfiguration) configuration;
171            if (cfg.containsKey( key ))
172            {
173                try
174                {
175                    return new CommonsConfigurationRegistry( cfg.configurationAt( key, true ), null );
176                } catch ( ConfigurationRuntimeException ex ) {
177                    logger.error("There are multiple entries for the given key");
178                    throw new RegistryException( "Subset for multiple key entries is not possible.");
179                }
180            } else {
181                return new CommonsConfigurationRegistry( cfg.subset( key ), null );
182            }
183        }
184        return new CommonsConfigurationRegistry( configuration.subset( key ), configurationBuilder );
185    }
186
187    @Override
188    public List<String> getList( String key )
189    {
190        List<String> result = configuration.getList( String.class, key );
191        return result == null ? new ArrayList<>( ) : result;
192    }
193
194    @Override
195    public List<ConfigRegistry> getSubsetList( String key ) throws RegistryException
196    {
197
198        if (configuration instanceof BaseHierarchicalConfiguration) {
199            BaseHierarchicalConfiguration cfg = (BaseHierarchicalConfiguration)configuration;
200            return cfg.configurationsAt( key, true ).stream().map(c -> new CommonsConfigurationRegistry( c, null )).collect( Collectors.toList() );
201        } else
202        {
203            List<ConfigRegistry> subsets = new ArrayList<>( );
204            boolean done = false;
205            do
206            {
207                ConfigRegistry registry = null;
208                try
209                {
210                    registry = getSubset( key + "(" + subsets.size( ) + ")" );
211                }
212                catch ( RegistryException e )
213                {
214                    throw new RegistryException( "Could not retrieve subset from key "+key+": "+e.getMessage() );
215                }
216                if ( !registry.isEmpty( ) )
217                {
218                    subsets.add( registry );
219                }
220                else
221                {
222                    done = true;
223                }
224            }
225            while ( !done );
226            return subsets;
227        }
228    }
229
230    @Override
231    public ConfigRegistry getPartOfCombined( String name )
232    {
233        if ( combined )
234        {
235            CombinedConfiguration config = (CombinedConfiguration) configuration;
236            Configuration newCfg = config.getConfiguration( name );
237            ConfigurationBuilder<? extends Configuration> cfgBuilder = null;
238            try
239            {
240                if ( builderMap.containsKey( name ) )
241                {
242                    cfgBuilder = builderMap.get( name );
243                }
244                else
245                {
246                    cfgBuilder = configurationBuilder == null ? null :
247                        ( (CombinedConfigurationBuilder) configurationBuilder ).getNamedBuilder( name );
248                    builderMap.put( name, cfgBuilder );
249                }
250            }
251            catch ( ConfigurationException e )
252            {
253                logger.error( "Could not retrieve configuration builder: " + e.getMessage( ) );
254            }
255            return newCfg == null ? null : new CommonsConfigurationRegistry( newCfg, cfgBuilder );
256        }
257        return null;
258    }
259
260    @Override
261    public Map<String, String> getProperties( String key )
262    {
263        Configuration configuration = this.configuration.subset( key );
264
265        Map<String, String> properties = new TreeMap<>( );
266        Iterator<String> cfgIter = configuration.getKeys( );
267        String property;
268        while ( cfgIter.hasNext( ) )
269        {
270            property = cfgIter.next( );
271            List<String> l = configuration.getList( String.class, property );
272            String value = String.join( ",", l );
273            properties.put( property, value );
274        }
275        return properties;
276    }
277
278    @Override
279    public void save( )
280        throws RegistryException
281    {
282        if ( configuration instanceof FileBasedConfiguration )
283        {
284            FileBasedConfiguration fileConfiguration = (FileBasedConfiguration) configuration;
285            FileHandler fileHandler;
286            if ( configurationBuilder != null && configurationBuilder instanceof FileBasedConfigurationBuilder )
287            {
288                FileBasedConfigurationBuilder cfgBuilder = (FileBasedConfigurationBuilder) configurationBuilder;
289                fileHandler = cfgBuilder.getFileHandler( );
290            }
291            else
292            {
293                fileHandler = new FileHandler( fileConfiguration );
294            }
295            try
296            {
297                fileHandler.save( );
298            }
299            catch ( ConfigurationException e )
300            {
301                throw new RegistryException( e.getMessage( ), e );
302            }
303        }
304        else
305        {
306            throw new RegistryException( "Can only save file-based configurations" );
307        }
308    }
309
310    @Override
311    public void registerChangeListener( RegistryListener listener, String prefix)
312    {
313        this.listener.registerChangeListener(listener, prefix);
314    }
315
316    @Override
317    public boolean unregisterChangeListener( RegistryListener listener )
318    {
319        return this.listener.unregisterChangeListener(listener);
320    }
321
322    @Override
323    public Collection<String> getBaseKeys( )
324    {
325        Iterable<String> iterable = ( ) -> configuration.getKeys( );
326        return StreamSupport.stream( iterable.spliterator( ), true )
327            .map( DOT_NAME_PATTERN::matcher )
328            .filter( Matcher::matches )
329            .map( k -> k.group( 1 ) ).collect( Collectors.toSet( ) );
330
331    }
332
333    @Override
334    public Collection<String> getKeys( )
335    {
336        Iterable<String> iterable = ( ) -> configuration.getKeys( );
337        return StreamSupport.stream( iterable.spliterator( ), true ).collect( Collectors.toSet( ) );
338    }
339
340    @Override
341    public Collection<String> getKeys( String prefix )
342    {
343        Iterable<String> iterable = ( ) -> configuration.getKeys( prefix );
344        return StreamSupport.stream( iterable.spliterator( ), true ).collect( Collectors.toSet( ) );
345    }
346
347    @Override
348    public void remove( String key )
349    {
350        configuration.clearProperty( key );
351    }
352
353    @Override
354    public void removeSubset( String key )
355    {
356        getKeys( key ).forEach( k -> configuration.clearProperty( k ) );
357    }
358
359    @Override
360    public Object getValue( String key ) {
361        return configuration.getProperty(  key );
362    }
363
364    @Override
365    public String getString( String key )
366    {
367        return configuration.getString( key );
368    }
369
370    @Override
371    public String getString( String key, String defaultValue )
372    {
373        return configuration.getString( key, defaultValue );
374    }
375
376    @Override
377    public void setString( String key, String value )
378    {
379        configuration.setProperty( key, value );
380    }
381
382    @Override
383    public int getInt( String key )
384    {
385        return configuration.getInt( key );
386    }
387
388    @Override
389    public int getInt( String key, int defaultValue )
390    {
391        return configuration.getInt( key, defaultValue );
392    }
393
394    @Override
395    public void setInt( String key, int value )
396    {
397        configuration.setProperty( key, value );
398    }
399
400    @Override
401    public boolean getBoolean( String key )
402    {
403        return configuration.getBoolean( key );
404    }
405
406    @Override
407    public boolean getBoolean( String key, boolean defaultValue )
408    {
409        return configuration.getBoolean( key, defaultValue );
410    }
411
412    @Override
413    public void setBoolean( String key, boolean value )
414    {
415        configuration.setProperty( key, value );
416    }
417
418    @Override
419    public void addConfigurationFromResource( String name, String resource )
420        throws RegistryException
421    {
422        addConfigurationFromResource( name, resource, null );
423    }
424
425    @Override
426    public void addConfigurationFromResource( String name, String resource, String prefix )
427        throws RegistryException
428    {
429        if ( configuration instanceof CombinedConfiguration )
430        {
431            String atPrefix = StringUtils.isEmpty( prefix ) ? null : prefix;
432            CombinedConfiguration configuration = (CombinedConfiguration) this.configuration;
433            if ( resource.endsWith( ".properties" ) )
434            {
435                try
436                {
437                    logger.debug( "Loading properties configuration from classloader resource: {}", resource );
438                    FileBasedConfigurationBuilder<PropertiesConfiguration> builder = new FileBasedConfigurationBuilder<>( PropertiesConfiguration.class )
439                        .configure( new Parameters( ).properties( )
440                            .setLocationStrategy( new ClasspathLocationStrategy( ) )
441                            .setFileName( resource ) );
442                    builderMap.put( name, builder );
443                    configuration.addConfiguration( builder.getConfiguration( ), name, atPrefix );
444                }
445                catch ( ConfigurationException e )
446                {
447                    throw new RegistryException(
448                        "Unable to add configuration from resource '" + resource + "': " + e.getMessage( ), e );
449                }
450            }
451            else if ( resource.endsWith( ".xml" ) )
452            {
453                try
454                {
455                    logger.debug( "Loading XML configuration from classloader resource: {}", resource );
456                    FileBasedConfigurationBuilder<XMLConfiguration> builder = new FileBasedConfigurationBuilder<>( XMLConfiguration.class )
457                        .configure( new Parameters( ).xml( )
458                            .setLocationStrategy( new ClasspathLocationStrategy( ) )
459                            .setFileName( resource ) );
460                    builderMap.put( name, builder );
461                    configuration.addConfiguration( builder.getConfiguration( ), name, atPrefix );
462                }
463                catch ( ConfigurationException e )
464                {
465                    throw new RegistryException(
466                        "Unable to add configuration from resource '" + resource + "': " + e.getMessage( ), e );
467                }
468            }
469            else
470            {
471                throw new RegistryException(
472                    "Unable to add configuration from resource '" + resource + "': unrecognised type" );
473            }
474        }
475        else
476        {
477            throw new RegistryException( "The underlying configuration object is not a combined configuration " );
478        }
479    }
480
481    @Override
482    public void addConfigurationFromFile( String name, Path file ) throws RegistryException
483    {
484        addConfigurationFromFile( name, file, "" );
485    }
486
487    @Override
488    public void addConfigurationFromFile( String name, Path file, String prefix )
489        throws RegistryException
490    {
491        if ( this.configuration instanceof CombinedConfiguration )
492        {
493            String atPrefix = StringUtils.isEmpty( prefix ) ? null : prefix;
494            CombinedConfiguration configuration = (CombinedConfiguration) this.configuration;
495            String fileName = file.getFileName( ).toString( );
496            if ( fileName.endsWith( ".properties" ) )
497            {
498                try
499                {
500                    logger.debug( "Loading properties configuration from file: {}", file );
501                    FileBasedConfigurationBuilder<PropertiesConfiguration> builder = new FileBasedConfigurationBuilder<>( PropertiesConfiguration.class )
502                        .configure( new Parameters( ).properties( )
503                            .setFileSystem( FileLocatorUtils.DEFAULT_FILE_SYSTEM )
504                            .setLocationStrategy( FileLocatorUtils.DEFAULT_LOCATION_STRATEGY )
505                            .setFile( file.toFile( ) ) );
506                    // builder is needed for save
507                    builderMap.put( name, builder );
508                    configuration.addConfiguration( builder.getConfiguration( ), name, atPrefix );
509                }
510                catch ( ConfigurationException e )
511                {
512                    throw new RegistryException(
513                        "Unable to add configuration from file '" + file.getFileName( ) + "': " + e.getMessage( ), e );
514                }
515            }
516            else if ( fileName.endsWith( ".xml" ) )
517            {
518                try
519                {
520                    logger.debug( "Loading XML configuration from file: {}", file );
521                    FileBasedConfigurationBuilder<XMLConfiguration> builder = new FileBasedConfigurationBuilder<>( XMLConfiguration.class )
522                        .configure( new Parameters( ).xml( )
523                            .setFileSystem( FileLocatorUtils.DEFAULT_FILE_SYSTEM )
524                            .setLocationStrategy( FileLocatorUtils.DEFAULT_LOCATION_STRATEGY )
525                            .setFile( file.toFile( ) ) );
526                    builderMap.put( name, builder );
527                    configuration.addConfiguration( builder.getConfiguration( ), name, atPrefix );
528                }
529                catch ( ConfigurationException e )
530                {
531                    throw new RegistryException(
532                        "Unable to add configuration from file '" + file.getFileName( ) + "': " + e.getMessage( ), e );
533                }
534            }
535            else
536            {
537                throw new RegistryException(
538                    "Unable to add configuration from file '" + file.getFileName( ) + "': unrecognised type" );
539            }
540        }
541        else
542        {
543            throw new RegistryException( "The underlying configuration is not a combined configuration object." );
544        }
545    }
546
547    /**
548     * This is a dummy FileSystem needed to load the CombinedConfiguration declaration from a String.
549     */
550    class StringFileSystem extends FileSystem
551    {
552
553        final String content;
554        String encoding = "UTF-8";
555
556        StringFileSystem( String content )
557        {
558            this.content = content;
559        }
560
561
562        @SuppressWarnings( "unused" )
563        StringFileSystem( String encoding, String content )
564        {
565            this.encoding = encoding;
566            this.content = content;
567        }
568
569        @Override
570        public InputStream getInputStream( URL url ) throws ConfigurationException
571        {
572            try
573            {
574                return new ByteArrayInputStream( content.getBytes( encoding ) );
575            }
576            catch ( UnsupportedEncodingException e )
577            {
578                logger.error( "Bad encoding for FileSystem" );
579                throw new ConfigurationException( "Bad encoding specified" );
580            }
581        }
582
583        @Override
584        public OutputStream getOutputStream( URL url )
585        {
586            return new ByteArrayOutputStream( 0 );
587        }
588
589        @Override
590        public OutputStream getOutputStream( File file )
591        {
592            return new ByteArrayOutputStream( 0 );
593        }
594
595        @Override
596        public String getPath( File file, URL url, String basePath, String fileName )
597        {
598            return basePath + "/" + fileName;
599        }
600
601        @Override
602        public String getBasePath( String path )
603        {
604            return path;
605        }
606
607        @Override
608        public String getFileName( String path )
609        {
610            return path;
611        }
612
613        @Override
614        public URL locateFromURL( String basePath, String fileName )
615        {
616            try
617            {
618                return new URL( "file://" + getPath( null, null, basePath, fileName ) );
619            }
620            catch ( MalformedURLException e )
621            {
622                // ignore
623                return null;
624            }
625        }
626
627        @Override
628        public URL getURL( String basePath, String fileName ) throws MalformedURLException
629        {
630            return new URL( "file://" + getPath( null, null, basePath, fileName ) );
631        }
632
633    }
634
635    @Override
636    @PostConstruct
637    public void initialize( )
638        throws RegistryException
639    {
640        try
641        {
642            CombinedConfiguration configuration;
643            if ( StringUtils.isNotBlank( combinedConfigurationDefinition ) )
644            {
645                String interpolatedProps;
646                Parameters params = new Parameters( );
647                DefaultExpressionEngineSymbols symbols = new DefaultExpressionEngineSymbols.Builder( DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS )
648                    .setPropertyDelimiter( propertyDelimiter )
649                    .setIndexStart( "(" )
650                    .setIndexEnd( ")" )
651                    .setEscapedDelimiter( "\\" + propertyDelimiter )
652                    .create( );
653                DefaultExpressionEngine expressionEngine = new DefaultExpressionEngine( symbols );
654
655                // It allows to use system properties in the XML declaration.
656
657                ConfigurationInterpolator interpolator = ConfigurationInterpolator.fromSpecification( new InterpolatorSpecification.Builder( ).withDefaultLookup( DefaultLookups.SYSTEM_PROPERTIES.getLookup( ) ).create( ) );
658                interpolatedProps = interpolator.interpolate( combinedConfigurationDefinition ).toString( );
659                logger.debug( "Loading configuration into commons-configuration, xml {}", interpolatedProps );
660                // This is the builder configuration for the XML declaration, that contains the definition
661                // for the sources that are used for the CombinedConfiguration.
662                FileSystem fs = new StringFileSystem( interpolatedProps );
663                FileBasedConfigurationBuilder<XMLConfiguration> cfgBuilder =
664                    new FileBasedConfigurationBuilder<>(
665                        XMLConfiguration.class )
666                        .configure( params.xml( )
667                            .setFileSystem( fs )
668                            .setFileName( "config.xml" )
669                            .setListDelimiterHandler(
670                                new DefaultListDelimiterHandler( ',' ) )
671                            .setExpressionEngine( expressionEngine )
672                            .setThrowExceptionOnMissing( false ) );
673
674                CombinedConfigurationBuilder builder = new CombinedConfigurationBuilder( ).
675                    configure( params.combined( ).setDefinitionBuilder( cfgBuilder ) );
676                // The builder is needed later for saving of the file parts in the combined configuration.
677                this.configurationBuilder = builder;
678                configuration = builder.getConfiguration( );
679
680
681            }
682            else
683            {
684                logger.debug( "Creating a default configuration - no configuration was provided" );
685                NodeCombiner combiner = new UnionCombiner( );
686                configuration = new CombinedConfiguration( combiner );
687                this.configurationBuilder = null;
688            }
689
690            // In the end, we add the system properties to the combined configuration
691            if ( addSystemProperties )
692            {
693                configuration.addConfiguration( new SystemConfiguration( ), "SystemProperties" );
694            }
695
696            this.configuration = configuration;
697        }
698        catch ( ConfigurationException e )
699        {
700            logger.error( "Fatal error, while reading the configuration definition: " + e.getMessage( ) );
701            logger.error( "The definition was:" );
702            logger.error( combinedConfigurationDefinition );
703            throw new RegistryException( e.getMessage( ), e );
704        }
705    }
706
707    public void setCombinedConfigurationDefinition( String combinedConfigurationDefinition )
708    {
709        this.combinedConfigurationDefinition = combinedConfigurationDefinition;
710    }
711
712    public String getPropertyDelimiter( )
713    {
714        return propertyDelimiter;
715    }
716
717    public void setPropertyDelimiter( String propertyDelimiter )
718    {
719        this.propertyDelimiter = propertyDelimiter;
720    }
721
722
723    public ConfigurationBuilder<? extends Configuration> getConfigurationBuilder( )
724    {
725        return configurationBuilder;
726    }
727
728    /**
729     * Returns true, if the system properties are added to the base configuration. Otherwise system properties
730     * can still be interpolated by ${sys:var} syntax.
731     *
732     * @return True, if system properties are added to the configuration root
733     */
734    public boolean isAddSystemProperties( )
735    {
736        return addSystemProperties;
737    }
738
739    /**
740     * Set to true, if the system properties should be added to the base configuration.
741     * If set to false, system properties are no direct part of the configuration.
742     *
743     * @param addSystemProperties True, or false.
744     */
745    public void setAddSystemProperties( boolean addSystemProperties )
746    {
747        this.addSystemProperties = addSystemProperties;
748    }
749}