diff --git a/pom.xml b/pom.xml
index 61ab7fdf..68ec204a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -307,6 +307,11 @@
redback-common-jpa
${project.version}
+
+ org.apache.archiva.redback
+ redback-common-configuration-api
+ ${project.version}
+
javax.servlet
servlet-api
@@ -648,6 +653,18 @@
jaxb-api
2.3.0
+
+
+ commons-beanutils
+ commons-beanutils
+ ${commons-beanutils.version}
+
+
+ commons-logging
+ commons-logging
+
+
+
diff --git a/redback-common/pom.xml b/redback-common/pom.xml
index 11391a9e..7bc42771 100644
--- a/redback-common/pom.xml
+++ b/redback-common/pom.xml
@@ -35,5 +35,6 @@
redback-common-ldap
redback-common-test-resources
redback-common-jpa
+ redback-common-configuration
\ No newline at end of file
diff --git a/redback-common/redback-common-configuration/pom.xml b/redback-common/redback-common-configuration/pom.xml
new file mode 100644
index 00000000..f1e8cf34
--- /dev/null
+++ b/redback-common/redback-common-configuration/pom.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+ redback-common
+ org.apache.archiva.redback
+ 3.0.0-SNAPSHOT
+
+ 4.0.0
+
+ redback-common-configuration
+ pom
+
+ redback-common-configuration-api
+ redback-common-configuration-acc2
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ true
+
+
+
+
+
+
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-acc2/pom.xml b/redback-common/redback-common-configuration/redback-common-configuration-acc2/pom.xml
new file mode 100644
index 00000000..0783bc95
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-acc2/pom.xml
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+ redback-common-configuration
+ org.apache.archiva.redback
+ 3.0.0-SNAPSHOT
+
+ 4.0.0
+
+ redback-common-configuration-acc2
+
+
+
+ org.apache.archiva.redback
+ redback-common-configuration-api
+
+
+ org.apache.commons
+ commons-configuration2
+ 2.4
+
+
+ commons-beanutils
+ commons-beanutils-core
+
+
+
+
+
+ commons-beanutils
+ commons-beanutils
+
+
+ org.springframework
+ spring-context
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+ junit
+ junit
+ compile
+
+
+
+ javax.annotation
+ jsr250-api
+
+
+
+ javax.inject
+ javax.inject
+
+
+
+ org.springframework
+ spring-core
+ compile
+
+
+ org.springframework
+ spring-context
+ compile
+
+
+ org.springframework
+ spring-context-support
+ compile
+
+
+ org.springframework
+ spring-beans
+ compile
+
+
+ org.springframework
+ spring-test
+ compile
+
+
+
+
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/main/java/org/apache/archiva/redback/common/config/acc2/CommonsConfigurationRegistry.java b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/main/java/org/apache/archiva/redback/common/config/acc2/CommonsConfigurationRegistry.java
new file mode 100644
index 00000000..8fe30d96
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/main/java/org/apache/archiva/redback/common/config/acc2/CommonsConfigurationRegistry.java
@@ -0,0 +1,610 @@
+package org.apache.archiva.redback.common.config.acc2;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.redback.common.config.api.ConfigRegistry;
+import org.apache.archiva.redback.common.config.api.RegistryException;
+import org.apache.archiva.redback.common.config.api.RegistryListener;
+import org.apache.commons.configuration2.CombinedConfiguration;
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.FileBasedConfiguration;
+import org.apache.commons.configuration2.PropertiesConfiguration;
+import org.apache.commons.configuration2.SystemConfiguration;
+import org.apache.commons.configuration2.XMLConfiguration;
+import org.apache.commons.configuration2.builder.BasicConfigurationBuilder;
+import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
+import org.apache.commons.configuration2.builder.combined.CombinedConfigurationBuilder;
+import org.apache.commons.configuration2.builder.fluent.Parameters;
+import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
+import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
+import org.apache.commons.configuration2.interpol.DefaultLookups;
+import org.apache.commons.configuration2.interpol.InterpolatorSpecification;
+import org.apache.commons.configuration2.io.ClasspathLocationStrategy;
+import org.apache.commons.configuration2.io.FileHandler;
+import org.apache.commons.configuration2.io.FileSystem;
+import org.apache.commons.configuration2.io.FileSystemLocationStrategy;
+import org.apache.commons.configuration2.tree.DefaultExpressionEngine;
+import org.apache.commons.configuration2.tree.DefaultExpressionEngineSymbols;
+import org.apache.commons.configuration2.tree.NodeCombiner;
+import org.apache.commons.configuration2.tree.UnionCombiner;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.regex.Pattern;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+/**
+ * Implementation of the registry component using
+ * Commons Configuration. The use of Commons Configuration
+ * enables a variety of sources to be used, including XML files, properties, JNDI, JDBC, etc.
+ *
+ * The component can be configured using the {@link #properties} configuration item, the content of which should take
+ * the format of an input to the Commons Configuration
+ * configuration
+ * builder.
+ */
+@Service( "acc2-configuration" )
+public class CommonsConfigurationRegistry
+ implements ConfigRegistry
+{
+ /**
+ * The combined configuration instance that houses the registry.
+ */
+ private Configuration configuration;
+
+ private Logger logger = LoggerFactory.getLogger( getClass() );
+
+ private String propertyDelimiter = ".";
+
+ /**
+ * The configuration properties for the registry. This should take the format of an input to the Commons
+ * Configuration
+ * configuration
+ * builder.
+ */
+ private String properties;
+
+
+ public CommonsConfigurationRegistry()
+ {
+ // default constructor
+ logger.debug( "empty constructor" );
+ }
+
+ public CommonsConfigurationRegistry( Configuration configuration )
+ {
+ if ( configuration == null )
+ {
+ throw new NullPointerException( "configuration can not be null" );
+ }
+
+ this.configuration = configuration;
+ }
+
+ public String dump()
+ {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append( "Configuration Dump." );
+ for ( Iterator i = configuration.getKeys(); i.hasNext(); )
+ {
+ String key = (String) i.next();
+ Object value = configuration.getProperty( key );
+ buffer.append( "\n\"" ).append( key ).append( "\" = \"" ).append( value ).append( "\"" );
+ }
+ return buffer.toString();
+ }
+
+ public boolean isEmpty()
+ {
+ return configuration.isEmpty();
+ }
+
+ public ConfigRegistry getSubset( String key )
+ {
+ return new CommonsConfigurationRegistry( configuration.subset( key ) );
+ }
+
+ public List getList( String key )
+ {
+ return configuration.getList(String.class, key );
+ }
+
+ public List getSubsetList( String key )
+ {
+ List subsets = new ArrayList<>();
+
+ boolean done = false;
+ do
+ {
+ ConfigRegistry registry = getSubset( key + "(" + subsets.size() + ")" );
+ if ( !registry.isEmpty() )
+ {
+ subsets.add( registry );
+ }
+ else
+ {
+ done = true;
+ }
+ }
+ while ( !done );
+
+ return subsets;
+ }
+
+ @Override
+ public ConfigRegistry getSource( String name )
+ {
+ return null;
+ }
+
+ public Map getProperties( String key )
+ {
+ Configuration configuration = this.configuration.subset( key );
+
+ Map properties = new TreeMap<>();
+ Iterator cfgIter = configuration.getKeys( );
+ String property;
+ while ( cfgIter.hasNext() )
+ {
+ property = cfgIter.next();
+ List l = configuration.getList( String.class, property );
+ String value = String.join(",", l);
+ properties.put( property, value );
+ }
+ return properties;
+ }
+
+ public void save()
+ throws RegistryException
+ {
+ if ( configuration instanceof FileBasedConfiguration )
+ {
+ FileBasedConfiguration fileConfiguration = (FileBasedConfiguration) configuration;
+ try
+ {
+ new FileHandler( fileConfiguration ).save();
+ }
+ catch ( ConfigurationException e )
+ {
+ throw new RegistryException( e.getMessage(), e );
+ }
+ }
+ else
+ {
+ throw new RegistryException( "Can only save file-based configurations" );
+ }
+ }
+
+ @Override
+ public void registerChangeListener( RegistryListener listener, Pattern... filter )
+ {
+
+ }
+
+ @Override
+ public boolean unregisterChangeListener( RegistryListener listener )
+ {
+ return false;
+ }
+
+
+ public Collection getKeys()
+ {
+ Set keys = new HashSet();
+
+ for ( Iterator i = configuration.getKeys(); i.hasNext(); )
+ {
+ String key = i.next();
+
+ int index = key.indexOf( '.' );
+ if ( index < 0 )
+ {
+ keys.add( key );
+ }
+ else
+ {
+ keys.add( key.substring( 0, index ) );
+ }
+ }
+
+ return keys;
+ }
+
+ public Collection getFullKeys()
+ {
+ Set keys = new HashSet();
+
+ for ( Iterator i = configuration.getKeys(); i.hasNext(); )
+ {
+ keys.add( i.next() );
+ }
+
+ return keys;
+ }
+
+ public void remove( String key )
+ {
+ configuration.clearProperty( key );
+ }
+
+ public void removeSubset( String key )
+ {
+ // create temporary list since removing a key will modify the iterator from configuration
+ List keys = new ArrayList();
+ for ( Iterator i = configuration.getKeys( key ); i.hasNext(); )
+ {
+ keys.add( i.next() );
+ }
+
+ for ( Iterator i = keys.iterator(); i.hasNext(); )
+ {
+ configuration.clearProperty( (String) i.next() );
+ }
+ }
+
+ public String getString( String key )
+ {
+ return configuration.getString( key );
+ }
+
+ public String getString( String key, String defaultValue )
+ {
+ return configuration.getString( key, defaultValue );
+ }
+
+ public void setString( String key, String value )
+ {
+ configuration.setProperty( key, value );
+ }
+
+ public int getInt( String key )
+ {
+ return configuration.getInt( key );
+ }
+
+ public int getInt( String key, int defaultValue )
+ {
+ return configuration.getInt( key, defaultValue );
+ }
+
+ public void setInt( String key, int value )
+ {
+ configuration.setProperty( key, Integer.valueOf( value ) );
+ }
+
+ public boolean getBoolean( String key )
+ {
+ return configuration.getBoolean( key );
+ }
+
+ public boolean getBoolean( String key, boolean defaultValue )
+ {
+ return configuration.getBoolean( key, defaultValue );
+ }
+
+ public void setBoolean( String key, boolean value )
+ {
+ configuration.setProperty( key, Boolean.valueOf( value ) );
+ }
+
+ public void addConfigurationFromResource( String name, String resource )
+ throws RegistryException
+ {
+ addConfigurationFromResource( name, resource, null );
+ }
+
+ public void addConfigurationFromResource( String name, String resource, String prefix )
+ throws RegistryException
+ {
+ if (configuration instanceof CombinedConfiguration)
+ {
+ String atPrefix = StringUtils.isEmpty( prefix ) ? null : prefix;
+ CombinedConfiguration configuration = (CombinedConfiguration) this.configuration;
+ if ( resource.endsWith( ".properties" ) )
+ {
+ try
+ {
+ logger.debug( "Loading properties configuration from classloader resource: {}", resource );
+ FileBasedConfigurationBuilder builder = new FileBasedConfigurationBuilder<>( PropertiesConfiguration.class )
+ .configure( new Parameters().properties()
+ .setLocationStrategy( new ClasspathLocationStrategy() )
+ .setFileName( resource ) );
+ configuration.addConfiguration( builder.getConfiguration() , name, atPrefix );
+ }
+ catch ( ConfigurationException e )
+ {
+ throw new RegistryException(
+ "Unable to add configuration from resource '" + resource + "': " + e.getMessage( ), e );
+ }
+ }
+ else if ( resource.endsWith( ".xml" ) )
+ {
+ try
+ {
+ logger.debug( "Loading XML configuration from classloader resource: {}", resource );
+ FileBasedConfigurationBuilder builder = new FileBasedConfigurationBuilder<>( XMLConfiguration.class )
+ .configure( new Parameters().xml()
+ .setLocationStrategy( new ClasspathLocationStrategy() )
+ .setFileName( resource ) );
+ configuration.addConfiguration( builder.getConfiguration(), name, atPrefix );
+ }
+ catch ( ConfigurationException e )
+ {
+ throw new RegistryException(
+ "Unable to add configuration from resource '" + resource + "': " + e.getMessage( ), e );
+ }
+ }
+ else
+ {
+ throw new RegistryException(
+ "Unable to add configuration from resource '" + resource + "': unrecognised type" );
+ }
+ } else {
+ throw new RegistryException( "The underlying configuration object is not a combined configuration " );
+ }
+ }
+
+ @Override
+ public void addConfigurationFromFile( String name, Path file ) throws RegistryException
+ {
+ addConfigurationFromFile( name, file, "" );
+ }
+
+ public void addConfigurationFromFile( String name, Path file, String prefix )
+ throws RegistryException
+ {
+ if (this.configuration instanceof CombinedConfiguration)
+ {
+ String atPrefix = StringUtils.isEmpty( prefix ) ? null : prefix;
+ CombinedConfiguration configuration = (CombinedConfiguration) this.configuration;
+ String fileName = file.getFileName().toString();
+ if ( fileName.endsWith( ".properties" ) )
+ {
+ try
+ {
+ logger.debug( "Loading properties configuration from file: {}", file );
+ FileBasedConfigurationBuilder builder = new FileBasedConfigurationBuilder<>( PropertiesConfiguration.class )
+ .configure( new Parameters().properties()
+ .setLocationStrategy( new FileSystemLocationStrategy() )
+ .setFile( file.toFile() ) );
+ configuration.addConfiguration( builder.getConfiguration() , name, atPrefix );
+ }
+ catch ( ConfigurationException e )
+ {
+ throw new RegistryException(
+ "Unable to add configuration from file '" + file.getFileName( ) + "': " + e.getMessage( ), e );
+ }
+ }
+ else if ( fileName.endsWith( ".xml" ) )
+ {
+ try
+ {
+ logger.debug( "Loading XML configuration from file: {}", file );
+ FileBasedConfigurationBuilder builder = new FileBasedConfigurationBuilder<>( XMLConfiguration.class )
+ .configure( new Parameters().xml()
+ .setLocationStrategy( new ClasspathLocationStrategy() )
+ .setFile( file.toFile()) );
+ configuration.addConfiguration( builder.getConfiguration(), name, atPrefix );
+ }
+ catch ( ConfigurationException e )
+ {
+ throw new RegistryException(
+ "Unable to add configuration from file '" + file.getFileName( ) + "': " + e.getMessage( ), e );
+ }
+ }
+ else
+ {
+ throw new RegistryException(
+ "Unable to add configuration from file '" + file.getFileName( ) + "': unrecognised type" );
+ }
+ } else {
+ throw new RegistryException( "The underlying configuration is not a combined configuration object." );
+ }
+ }
+
+ class StringFileSystem extends FileSystem {
+
+ String content;
+ String encoding = "UTF-8";
+
+ StringFileSystem(String content) {
+ this.content = content;
+ }
+
+ @Override
+ public InputStream getInputStream( URL url ) throws ConfigurationException
+ {
+ try
+ {
+ return new ByteArrayInputStream( content.getBytes(encoding) );
+ }
+ catch ( UnsupportedEncodingException e )
+ {
+ logger.error("Bad encoding for FileSystem");
+ throw new ConfigurationException( "Bad encoding specified" );
+ }
+ }
+
+ @Override
+ public OutputStream getOutputStream( URL url ) throws ConfigurationException
+ {
+ return new ByteArrayOutputStream( 0 );
+ }
+
+ @Override
+ public OutputStream getOutputStream( File file ) throws ConfigurationException
+ {
+ return new ByteArrayOutputStream( 0 );
+ }
+
+ @Override
+ public String getPath( File file, URL url, String basePath, String fileName )
+ {
+ return basePath+"/"+fileName;
+ }
+
+ @Override
+ public String getBasePath( String path )
+ {
+ return path;
+ }
+
+ @Override
+ public String getFileName( String path )
+ {
+ return path;
+ }
+
+ @Override
+ public URL locateFromURL( String basePath, String fileName )
+ {
+ try
+ {
+ return new URL("file://"+getPath(null, null, basePath, fileName));
+ }
+ catch ( MalformedURLException e )
+ {
+ // ignore
+ return null;
+ }
+ }
+
+ @Override
+ public URL getURL( String basePath, String fileName ) throws MalformedURLException
+ {
+ try
+ {
+ return new URL("file://"+getPath(null, null, basePath, fileName));
+ }
+ catch ( MalformedURLException e )
+ {
+ // ignore
+ return null;
+ }
+ }
+
+ }
+
+ @PostConstruct
+ public void initialize()
+ throws RegistryException
+ {
+ try
+ {
+ CombinedConfiguration configuration;
+ if ( StringUtils.isNotBlank( properties ) )
+ {
+ System.out.println("Configuration");
+ System.out.println(properties);
+ Parameters params = new Parameters();
+ DefaultExpressionEngineSymbols symbols = new DefaultExpressionEngineSymbols.Builder(DefaultExpressionEngineSymbols.DEFAULT_SYMBOLS )
+ .setPropertyDelimiter( propertyDelimiter )
+ .setIndexStart( "(" )
+ .setIndexEnd( ")" )
+ .setEscapedDelimiter( "\\"+propertyDelimiter )
+ . create( );
+ DefaultExpressionEngine expressionEngine = new DefaultExpressionEngine( symbols );
+ ConfigurationInterpolator interpolator = ConfigurationInterpolator.fromSpecification( new InterpolatorSpecification.Builder().withDefaultLookup( DefaultLookups.SYSTEM_PROPERTIES.getLookup() ).create() );
+ System.out.println(interpolator.getDefaultLookups().stream( ).map(p -> p.toString()).collect( Collectors.joining( ",")));
+ String interpolatedProps = interpolator.interpolate( properties ).toString();
+ logger.debug( "Loading configuration into commons-configuration, xml {}", interpolatedProps );
+ FileSystem fs = new StringFileSystem( interpolatedProps );
+ FileBasedConfigurationBuilder cfgBuilder =
+ new FileBasedConfigurationBuilder<>(
+ XMLConfiguration.class)
+ .configure(params.xml()
+ .setInterpolator( interpolator )
+ .setFileSystem( fs )
+ .setFileName( "config.xml")
+ .setListDelimiterHandler(
+ new DefaultListDelimiterHandler(','))
+ .setExpressionEngine( expressionEngine )
+ .setThrowExceptionOnMissing(false));
+
+ CombinedConfigurationBuilder builder = new CombinedConfigurationBuilder().
+ configure(params.combined().setDefinitionBuilder( cfgBuilder ));
+
+ configuration = builder.getConfiguration();
+
+ // interpolation as plexus did it before
+
+ //configuration.set
+ }
+ else
+ {
+ logger.debug( "Creating a default configuration - no configuration was provided" );
+ NodeCombiner combiner = new UnionCombiner();
+ configuration = new CombinedConfiguration(combiner);
+ }
+
+ configuration.addConfiguration( new SystemConfiguration(), "SystemProperties" );
+
+ this.configuration = configuration;
+ }
+ catch ( ConfigurationException e )
+ {
+ throw new RuntimeException( e.getMessage(), e );
+ }
+ }
+
+ public void setProperties( String properties )
+ {
+ this.properties = properties;
+ }
+
+ public ConfigRegistry getSection( String name )
+ {
+ CombinedConfiguration combinedConfiguration = (CombinedConfiguration) configuration;
+ Configuration configuration = combinedConfiguration.getConfiguration( name );
+ return configuration == null ? null : new CommonsConfigurationRegistry( configuration );
+ }
+
+ public String getPropertyDelimiter()
+ {
+ return propertyDelimiter;
+ }
+
+ public void setPropertyDelimiter( String propertyDelimiter )
+ {
+ this.propertyDelimiter = propertyDelimiter;
+ }
+}
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/main/resources/META-INF/spring-context.xml b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/main/resources/META-INF/spring-context.xml
new file mode 100755
index 00000000..4a924fbe
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/main/resources/META-INF/spring-context.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/AbstractRegistryTest.java b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/AbstractRegistryTest.java
new file mode 100644
index 00000000..fa6d6371
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/AbstractRegistryTest.java
@@ -0,0 +1,189 @@
+package org.apache.archiva.redback.common.config.acc2;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import junit.framework.TestCase;
+import org.apache.archiva.redback.common.config.api.ConfigRegistry;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import javax.inject.Inject;
+import java.util.NoSuchElementException;
+
+/**
+ * @author Olivier Lamy
+ *
+ * @since 8 feb. 07
+ */
+@RunWith( value = SpringJUnit4ClassRunner.class )
+@ContextConfiguration( locations = { "classpath*:/META-INF/spring-context.xml", "classpath*:/spring-context.xml" } )
+public abstract class AbstractRegistryTest
+ extends TestCase
+{
+
+ @Inject
+ protected ApplicationContext applicationContext;
+
+ public abstract String getRoleHint();
+
+ public ConfigRegistry getRegistry()
+ throws Exception
+ {
+ return getRegistry( getRoleHint() );
+ }
+
+ public ConfigRegistry getRegistry( String name )
+ throws Exception
+ {
+ ConfigRegistry registry = applicationContext.getBean( name, ConfigRegistry.class );
+ registry.initialize();
+ return registry;
+ }
+
+ @Test
+ public void testInt()
+ throws Exception
+ {
+ ConfigRegistry registry = getRegistry();
+ assertEquals( "not 2 ", 2, registry.getInt( "two" ) );
+ }
+
+ @Test
+ public void testIntUnknown()
+ throws Exception
+ {
+ ConfigRegistry registry = getRegistry();
+ try
+ {
+ registry.getInt( "unknown" );
+ assertTrue( "no NoSuchElementException", false );
+ }
+ catch ( NoSuchElementException e )
+ {
+ // cool it works
+ }
+ }
+
+ @Test
+ public void testString()
+ throws Exception
+ {
+ ConfigRegistry registry = getRegistry();
+ assertEquals( "not foo ", "foo", registry.getString( "string" ) );
+ }
+
+ @Test
+ public void testStringUnknown()
+ throws Exception
+ {
+ ConfigRegistry registry = getRegistry();
+ String value = registry.getString( "unknown" );
+ assertNull( "unknow not null", value );
+
+ }
+
+ @Test
+ public void testBoolean()
+ throws Exception
+ {
+ ConfigRegistry registry = getRegistry();
+ assertEquals( "not true ", true, registry.getBoolean( "boolean" ) );
+ }
+
+ @Test
+ public void testBooleanUnknown()
+ throws Exception
+ {
+ ConfigRegistry registry = getRegistry();
+ try
+ {
+ registry.getBoolean( "unknown" );
+ assertTrue( "no NoSuchElementException", false );
+ }
+ catch ( NoSuchElementException e )
+ {
+ // cool it works
+ }
+ }
+
+ @Test
+ public void testIsNotEmpty()
+ throws Exception
+ {
+ assertFalse( getRegistry().isEmpty() );
+ }
+
+ @Test
+ public void testGetSubRegistry()
+ throws Exception
+ {
+ assertNotNull( getRegistry().getSubset( "subOne" ) );
+ }
+
+ @Test
+ public void testgetSubsetValues()
+ throws Exception
+ {
+ ConfigRegistry sub = getRegistry().getSubset( "subOne" );
+ assertNotNull( sub );
+ assertEquals( "entryOne", sub.getString( "firstEntry" ) );
+ assertEquals( "entryTwo", sub.getString( "secondEntry" ) );
+ }
+
+ @Test
+ public void testgetSubsetEmpty()
+ throws Exception
+ {
+ ConfigRegistry registry = getRegistry();
+ assertNotNull( registry.getSubset( "none" ) );
+ assertTrue( registry.getSubset( "none" ).isEmpty() );
+
+ }
+
+ @Test
+ public void testSetBoolean()
+ throws Exception
+ {
+ ConfigRegistry registry = getRegistry();
+ registry.setBoolean( "keyTrue", true );
+ assertTrue( registry.getBoolean( "keyTrue" ) );
+ }
+
+ @Test
+ public void testSetInt()
+ throws Exception
+ {
+ ConfigRegistry registry = getRegistry();
+ registry.setInt( "keyInt", 3 );
+ assertEquals( 3, registry.getInt( "keyInt" ) );
+ }
+
+ @Test
+ public void testSetString()
+ throws Exception
+ {
+ ConfigRegistry registry = getRegistry();
+ registry.setString( "what", "zorglub" );
+ assertEquals( "zorglub", registry.getString( "what" ) );
+ }
+}
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/CommonsConfigurationRegistryTest.java b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/CommonsConfigurationRegistryTest.java
new file mode 100644
index 00000000..a1c4e591
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/CommonsConfigurationRegistryTest.java
@@ -0,0 +1,422 @@
+package org.apache.archiva.redback.common.config.acc2;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.redback.common.config.api.ConfigRegistry;
+import org.apache.archiva.redback.common.config.api.RegistryException;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+/**
+ * Test the commons configuration registry.
+ */
+public class CommonsConfigurationRegistryTest
+ extends AbstractRegistryTest
+{
+ private ConfigRegistry registry;
+
+ private final Logger logger = LoggerFactory.getLogger( CommonsConfigurationRegistryTest.class );
+
+
+ private static final int INT_TEST_VALUE = 8080;
+
+ public String getRoleHint()
+ {
+ return "builder";
+ }
+
+ @Test
+ public void requirementTest() {
+ assertNotNull(System.getProperty("basedir"));
+ assertTrue( System.getProperty( "basedir" ).length()>0 );
+ assertNotNull(System.getProperty("user.dir"));
+ assertTrue( System.getProperty( "user.dir" ).length()>0 );
+
+ }
+
+ @Test
+ public void testDefaultConfiguration()
+ throws Exception
+ {
+ registry = getRegistry( "default" );
+
+ assertEquals( "Check system property override", System.getProperty( "user.dir" ),
+ registry.getString( "user.dir" ) );
+ assertEquals( "Check system property", System.getProperty( "user.home" ), registry.getString( "user.home" ) );
+ assertNull( "Check other properties are not loaded", registry.getString( "test.value" ) );
+ }
+
+ @Test
+ public void testBuilderConfiguration()
+ throws Exception
+ {
+ registry = getRegistry( "builder" );
+
+ assertEquals( "Check system property override", "new user dir", registry.getString( "user.dir" ) );
+ assertEquals( "Check system property default", System.getProperty( "user.home" ),
+ registry.getString( "user.home" ) );
+ assertEquals( "Check other properties are loaded", "foo", registry.getString( "test.value" ) );
+ assertEquals( "Check other properties are loaded", 1, registry.getInt( "test.number" ) );
+ assertTrue( "Check other properties are loaded", registry.getBoolean( "test.boolean" ) );
+ }
+
+ @Test
+ public void testDump()
+ throws Exception
+ {
+ registry = getRegistry( "default" );
+
+ String dump = registry.dump();
+ assertTrue( dump.startsWith( "Configuration Dump.\n\"" ) );
+ }
+
+ @Test
+ public void testDefaults()
+ throws Exception
+ {
+ registry = getRegistry( "builder" );
+
+ assertNull( "Check getString returns null", registry.getString( "foo" ) );
+ assertEquals( "Check getString returns default", "bar", registry.getString( "foo", "bar" ) );
+
+ try
+ {
+ registry.getInt( "foo" );
+ fail();
+ }
+ catch ( NoSuchElementException e )
+ {
+ // success
+ }
+
+ assertEquals( "Check getInt returns default", INT_TEST_VALUE, registry.getInt( "foo", INT_TEST_VALUE ) );
+
+ try
+ {
+ registry.getBoolean( "foo" );
+ fail();
+ }
+ catch ( NoSuchElementException e )
+ {
+ // success
+ }
+
+ assertTrue( "Check getBoolean returns default", registry.getBoolean( "foo", true ) );
+ }
+
+ @Test
+ public void testInterpolation()
+ throws Exception
+ {
+ registry = getRegistry( "builder" );
+
+ assertEquals( "Check system property interpolation", System.getProperty( "user.home" ) + "/.m2/repository",
+ registry.getString( "repository" ) );
+
+ assertEquals( "Check configuration value interpolation", "foo/bar",
+ registry.getString( "test.interpolation" ) );
+ }
+
+ @Test
+ public void testAddConfigurationXmlFile()
+ throws Exception
+ {
+ registry = getRegistry( "default" );
+
+ registry.addConfigurationFromFile( "test.xml", Paths.get( "src/test/resources/test.xml" ) );
+ System.out.println(registry.dump());
+ assertEquals( "Check system property default", System.getProperty( "user.dir" ),
+ registry.getString( "user.dir" ) );
+ assertEquals( "Check other properties are loaded", "foo", registry.getString( "test.value" ) );
+ }
+
+ @Test
+ public void testAddConfigurationPropertiesFile()
+ throws Exception
+ {
+ registry = getRegistry( "default" );
+
+ registry.addConfigurationFromFile(
+ "test.properties", Paths.get("src/test/resources/test.properties" ) );
+
+ assertEquals( "Check system property default", System.getProperty( "user.dir" ),
+ registry.getString( "user.dir" ) );
+ assertEquals( "Check other properties are loaded", "baz", registry.getString( "foo.bar" ) );
+ assertNull( "Check other properties are not loaded", registry.getString( "test.value" ) );
+ }
+
+ @Test
+ public void testAddConfigurationXmlResource()
+ throws Exception
+ {
+ registry = getRegistry( "default" );
+
+ registry.addConfigurationFromResource( "test.xml-r", "test.xml" );
+
+ assertEquals( "Check system property default", System.getProperty( "user.dir" ),
+ registry.getString( "user.dir" ) );
+ assertEquals( "Check other properties are loaded", "foo", registry.getString( "test.value" ) );
+ }
+
+ @Test
+ public void testAddConfigurationPropertiesResource()
+ throws Exception
+ {
+ registry = getRegistry( "default" );
+
+ registry.addConfigurationFromResource( "test.properties-r", "test.properties" );
+
+ assertEquals( "Check system property default", System.getProperty( "user.dir" ),
+ registry.getString( "user.dir" ) );
+ assertEquals( "Check other properties are loaded", "baz", registry.getString( "foo.bar" ) );
+ assertNull( "Check other properties are not loaded", registry.getString( "test.value" ) );
+ }
+
+ @Test
+ public void testAddConfigurationUnrecognisedType()
+ throws Exception
+ {
+ registry = getRegistry( "default" );
+
+ try
+ {
+ registry.addConfigurationFromResource( "test.foo", "test.foo" );
+ fail();
+ }
+ catch ( RegistryException e )
+ {
+ // success
+ }
+
+ try
+ {
+ registry.addConfigurationFromFile( "test.foo-file",
+ Paths.get( "src/test/resources/test.foo" ) );
+ fail();
+ }
+ catch ( RegistryException e )
+ {
+ // success
+ }
+ }
+
+ @Test
+ public void testIsEmpty()
+ throws Exception
+ {
+ registry = getRegistry( "default" );
+
+ assertFalse( registry.isEmpty() );
+ assertTrue( registry.getSubset( "foo" ).isEmpty() );
+ }
+
+ @Test
+ public void testGetSubset()
+ throws Exception
+ {
+ registry = getRegistry( "builder" );
+
+ ConfigRegistry registry = this.registry.getSubset( "test" );
+ assertEquals( "Check other properties are loaded", "foo", registry.getString( "value" ) );
+ assertEquals( "Check other properties are loaded", 1, registry.getInt( "number" ) );
+ assertTrue( "Check other properties are loaded", registry.getBoolean( "boolean" ) );
+ }
+
+ @Test
+ public void testGetSubsetList()
+ throws Exception
+ {
+ registry = getRegistry( "builder" );
+
+ List list = registry.getSubsetList( "objects.object" );
+ assertEquals( 2, list.size() );
+ ConfigRegistry r = (ConfigRegistry) list.get( 0 );
+ assertTrue( "bar".equals( r.getString( "foo" ) ) || "baz".equals( r.getString( "foo" ) ) );
+ r = (ConfigRegistry) list.get( 1 );
+ assertTrue( "bar".equals( r.getString( "foo" ) ) || "baz".equals( r.getString( "foo" ) ) );
+ }
+
+ @Test
+ public void testGetProperties()
+ throws Exception
+ {
+ registry = getRegistry( "builder" );
+
+ Map properties = registry.getProperties( "properties" );
+ assertEquals( 2, properties.size() );
+ assertEquals( "bar", properties.get( "foo" ) );
+ assertEquals( "baz", properties.get( "bar" ) );
+ }
+
+ @Test
+ public void testGetList()
+ throws Exception
+ {
+ registry = getRegistry( "builder" );
+
+ List list = registry.getList( "strings.string" );
+ assertEquals( 3, list.size() );
+ assertEquals( "s1", list.get( 0 ) );
+ assertEquals( "s2", list.get( 1 ) );
+ assertEquals( "s3", list.get( 2 ) );
+ }
+
+ @Test
+ public void testGetSection()
+ throws Exception
+ {
+ registry = getRegistry( "builder" );
+
+ ConfigRegistry registry = this.registry.getSource( "properties" );
+ assertNull( registry.getString( "test.value" ) );
+ assertEquals( "baz", registry.getString( "foo.bar" ) );
+ }
+
+ @Test
+ public void testRemoveKey()
+ throws Exception
+ {
+ registry = getRegistry( "builder" );
+
+ ConfigRegistry registry = this.registry.getSource( "properties" );
+ assertEquals( "baz", registry.getString( "foo.bar" ) );
+ registry.remove( "foo.bar" );
+ assertNull( registry.getString( "foo.bar" ) );
+ }
+
+ @Test
+ public void testRemoveSubset()
+ throws Exception
+ {
+ registry = getRegistry( "builder" );
+
+ registry.removeSubset( "strings" );
+ assertEquals( Collections.EMPTY_LIST, registry.getList( "strings.string" ) );
+
+ ConfigRegistry registry = this.registry.getSource( "properties" );
+ assertEquals( "baz", registry.getString( "foo.bar" ) );
+ registry.remove( "foo" );
+ assertEquals( "baz", registry.getString( "foo.bar" ) );
+ registry.removeSubset( "foo" );
+ assertNull( registry.getString( "foo.bar" ) );
+ }
+
+/* TODO: for 1.4
+ public void testGetForcedCreateByName()
+ throws Exception
+ {
+ registry = (Registry) lookup( Registry.class.getName(), "forceCreate" );
+
+ String testFile = getTestFile( "target/foo-forced" ).getAbsolutePath();
+ assertFalse( FileUtils.fileExists( testFile ) );
+
+ assertNotNull( registry.getSection( "foo" ) );
+ }
+*/
+
+ @Test
+ public void testGetDontForceCreateByName()
+ throws Exception
+ {
+ registry = getRegistry( "noForceCreate" );
+
+ assertNull( registry.getSource( "foo" ) );
+ }
+
+ @Test
+ public void testSaveSection()
+ throws Exception
+ {
+ Path src = Paths.get( "src/test/resources/test-save.xml" );
+ Path dest = Paths.get( "target/test-classes/test-save.xml" );
+ Files.copy( src, dest );
+
+ registry = getRegistry( "test-save" );
+
+ ConfigRegistry registry = this.registry.getSource( "org.codehaus.plexus.registry" );
+ assertEquals( "check list elements", Arrays.asList( new String[]{ "1", "2", "3" } ),
+ registry.getList( "listElements.listElement" ) );
+
+ registry.remove( "listElements.listElement(1)" );
+ registry.save();
+
+ // @TODO: Migrate test implementation to commons config 2.4
+// XMLConfiguration configuration = new XMLConfiguration( dest );
+// assertEquals( Arrays.asList( new String[]{ "1", "3" } ), configuration.getList( "listElements.listElement" ) );
+//
+// // file in ${basedir}/target/conf/shared.xml
+// Registry section = this.registry.getSection( "org.apache.maven.shared.app.user" );
+// section.setString( "foo", "zloug" );
+// section.save();
+//
+// configuration = new XMLConfiguration( new File( "target/conf/shared.xml" ) );
+// assertNotNull( configuration.getString( "foo" ) );
+
+ }
+
+
+ // TODO: Migrate implementation to commons configuration 2.4
+// @Test
+// public void test_listener()
+// throws Exception
+// {
+// registry = getRegistry( "default" );
+//
+// int listenerSize = CommonsConfigurationRegistry.class.cast( registry ).getChangeListenersSize();
+//
+// MockChangeListener mockChangeListener = new MockChangeListener();
+//
+// registry.addChangeListener( mockChangeListener );
+//
+// registry.addChangeListener( new MockChangeListener() );
+//
+// assertEquals( listenerSize + 2, CommonsConfigurationRegistry.class.cast( registry ).getChangeListenersSize() );
+//
+// registry.removeChangeListener( mockChangeListener );
+//
+// assertEquals( listenerSize + 1, CommonsConfigurationRegistry.class.cast( registry ).getChangeListenersSize() );
+// }
+//
+// private static class MockChangeListener
+// implements RegistryListener
+// {
+// @Override
+// public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
+// {
+// // no op
+// }
+//
+// @Override
+// public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
+// {
+// // no op
+// }
+// }
+}
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/Component.java b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/Component.java
new file mode 100644
index 00000000..916d2e66
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/Component.java
@@ -0,0 +1,60 @@
+package org.apache.archiva.redback.common.config.acc2;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Test component.
+ */
+public interface Component
+{
+ String ROLE = Component.class.getName();
+
+ String getKey( );
+
+ Properties getProperties( );
+
+ String getConfigKey( );
+
+ Properties getConfigProperties( );
+
+ Map getMap( );
+
+ List getList( );
+
+ Map getConfigMap( );
+
+ List getConfigList( );
+
+ Properties getMergeProperties( );
+
+ int getNumber( );
+
+ int getConfigNumber( );
+
+ Nested getNested( );
+
+ Nested getConfigNested( );
+
+ List getMergeList( );
+}
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/DefaultComponent.java b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/DefaultComponent.java
new file mode 100644
index 00000000..723e7513
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/DefaultComponent.java
@@ -0,0 +1,128 @@
+package org.apache.archiva.redback.common.config.acc2;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Test component.
+ */
+public class DefaultComponent
+ implements Component
+{
+ private String key;
+
+ private Properties properties;
+
+ private Map map;
+
+ private List list;
+
+ private String configKey;
+
+ private Properties configProperties;
+
+ private Map configMap;
+
+ private List configList;
+
+ private Properties mergeProperties;
+
+ private int number;
+
+ private int configNumber;
+
+ private Nested nested;
+
+ private Nested configNested;
+
+ private List mergeList;
+
+ public List getMergeList()
+ {
+ return mergeList;
+ }
+
+ public Nested getConfigNested()
+ {
+ return configNested;
+ }
+
+ public Nested getNested()
+ {
+ return nested;
+ }
+
+ public int getNumber()
+ {
+ return number;
+ }
+
+ public int getConfigNumber()
+ {
+ return configNumber;
+ }
+
+ public String getConfigKey()
+ {
+ return configKey;
+ }
+
+ public Properties getConfigProperties()
+ {
+ return configProperties;
+ }
+
+ public String getKey()
+ {
+ return key;
+ }
+
+ public Properties getProperties()
+ {
+ return properties;
+ }
+
+ public Map getMap()
+ {
+ return map;
+ }
+
+ public List getList()
+ {
+ return list;
+ }
+
+ public Map getConfigMap()
+ {
+ return configMap;
+ }
+
+ public List getConfigList()
+ {
+ return configList;
+ }
+
+ public Properties getMergeProperties()
+ {
+ return mergeProperties;
+ }
+}
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/Nested.java b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/Nested.java
new file mode 100644
index 00000000..6e54dce9
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/java/org/apache/archiva/redback/common/config/acc2/Nested.java
@@ -0,0 +1,33 @@
+package org.apache.archiva.redback.common.config.acc2;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Nested class for testing.
+ */
+public class Nested
+{
+ private String foo;
+
+ public String getFoo()
+ {
+ return foo;
+ }
+}
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/META-INF/spring-context.xml b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/META-INF/spring-context.xml
new file mode 100755
index 00000000..8476963e
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/META-INF/spring-context.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/spring-context.xml b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/spring-context.xml
new file mode 100755
index 00000000..895ea07b
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/spring-context.xml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
\ No newline at end of file
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/test-save.xml b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/test-save.xml
new file mode 100644
index 00000000..76667ada
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/test-save.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ 1
+ 2
+ 3
+
+
\ No newline at end of file
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/test.properties b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/test.properties
new file mode 100644
index 00000000..332952e2
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/test.properties
@@ -0,0 +1,25 @@
+#
+# Copyright 2007 The Codehaus Foundation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+user.dir=new user dir
+foo.bar=baz
+
+# test from plexus-registry tests
+subOne.firstEntry=entryOne
+subOne.secondEntry=entryTwo
+two=2
+string=foo
+boolean=true
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/test.xml b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/test.xml
new file mode 100644
index 00000000..921aed7b
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-acc2/src/test/resources/test.xml
@@ -0,0 +1,42 @@
+
+
+
+
+ foo
+ 1
+ true
+ ${test.value}/bar
+
+ ${user.home}/.m2/repository
+
+
+
+
+
+ bar
+ baz
+
+
+ s1
+ s2
+ s3
+
+
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-api/pom.xml b/redback-common/redback-common-configuration/redback-common-configuration-api/pom.xml
new file mode 100644
index 00000000..28f88cca
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-api/pom.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ redback-common-configuration
+ org.apache.archiva.redback
+ 3.0.0-SNAPSHOT
+
+ 4.0.0
+
+ redback-common-configuration-api
+
+
+
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-api/src/main/java/org/apache/archiva/redback/common/config/api/ConfigRegistry.java b/redback-common/redback-common-configuration/redback-common-configuration-api/src/main/java/org/apache/archiva/redback/common/config/api/ConfigRegistry.java
new file mode 100644
index 00000000..cba0548f
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-api/src/main/java/org/apache/archiva/redback/common/config/api/ConfigRegistry.java
@@ -0,0 +1,264 @@
+package org.apache.archiva.redback.common.config.api;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * The configuration registry is a single source of external configuration.
+ *
+ * It can be used by components to source configuration, knowing that it can be used from within applications
+ * without the information being hard coded into the component.
+ *
+ */
+public interface ConfigRegistry
+{
+
+ /**
+ * Dump the entire registry to a string, for debugging purposes.
+ *
+ * @return the registry contents
+ */
+ String dump( );
+
+ /**
+ * Get a string value from the registry. If not found, null
is returned.
+ *
+ * @param key the key in the registry
+ * @return the value
+ */
+ String getString( String key );
+
+ /**
+ * Get a string value from the registry. If not found, the default value is used.
+ *
+ * @param key the key in the registry
+ * @param defaultValue the default value
+ * @return the value
+ */
+ String getString( String key, String defaultValue );
+
+ /**
+ * Set a string value in the registry.
+ *
+ * @param key the key in the registry
+ * @param value the value to set
+ */
+ void setString( String key, String value );
+
+ /**
+ * Get an integer value from the registry. If not found, an exception is thrown.
+ *
+ * @param key the key in the registry
+ * @return the value
+ * @throws java.util.NoSuchElementException
+ * if the key is not found
+ */
+ int getInt( String key );
+
+ /**
+ * Get an integer value from the registry. If not found, the default value is used.
+ *
+ * @param key the key in the registry
+ * @param defaultValue the default value
+ * @return the value
+ */
+ int getInt( String key, int defaultValue );
+
+ /**
+ * Set an integer value in the registry.
+ *
+ * @param key the key in the registry
+ * @param value the value to set
+ */
+ void setInt( String key, int value );
+
+ /**
+ * Get a boolean value from the registry. If not found, an exception is thrown.
+ *
+ * @param key the key in the registry
+ * @return the value
+ * @throws java.util.NoSuchElementException
+ * if the key is not found
+ */
+ boolean getBoolean( String key );
+
+ /**
+ * Get a boolean value from the registry. If not found, the default value is used.
+ *
+ * @param key the key in the registry
+ * @param defaultValue the default value
+ * @return the value
+ */
+ boolean getBoolean( String key, boolean defaultValue );
+
+ /**
+ * Set a boolean value in the registry.
+ *
+ * @param key the key in the registry
+ * @param value the value to set
+ */
+ void setBoolean( String key, boolean value );
+
+ /**
+ * Load configuration from the given classloader resource.
+ *
+ * @param resource the location to load the configuration from
+ * @throws RegistryException if a problem occurred reading the resource to add to the registry
+ */
+ void addConfigurationFromResource( String name, String resource )
+ throws RegistryException;
+
+ /**
+ * Load configuration from the given classloader resource.
+ *
+ * @param resource the location to load the configuration from
+ * @param prefix the location to add the configuration at in the registry
+ * @throws RegistryException if a problem occurred reading the resource to add to the registry
+ */
+ void addConfigurationFromResource( String name, String resource, String prefix )
+ throws RegistryException;
+
+ /**
+ * Load configuration from the given file.
+ *
+ * @param file the location to load the configuration from
+ * @throws RegistryException if a problem occurred reading the resource to add to the registry
+ */
+ void addConfigurationFromFile( String name, Path file )
+ throws RegistryException;
+
+ /**
+ * Load configuration from the given file.
+ *
+ * @param file the location to load the configuration from
+ * @param prefix the location to add the configuration at in the registry
+ * @throws RegistryException if a problem occurred reading the resource to add to the registry
+ */
+ void addConfigurationFromFile( String name, Path file, String prefix )
+ throws RegistryException;
+
+ /**
+ * Determine if the registry contains any elements.
+ *
+ * @return whether the registry contains any elements
+ */
+ boolean isEmpty( );
+
+ /**
+ * Get a list of strings at the given key in the registry.
+ *
+ * @param key the key to lookup
+ * @return the list of strings
+ */
+ List getList( String key );
+
+ /**
+ * Get the properties at the given key in the registry.
+ *
+ * @param key the key to lookup
+ * @return the properties
+ */
+ Map getProperties( String key );
+
+ /**
+ * Get a subset of the registry, for all keys descended from the given key.
+ *
+ * @param key the key to take the subset from
+ * @return the registry subset
+ */
+ ConfigRegistry getSubset( String key );
+
+ /**
+ * Get a list of subsets of the registry, for all keys descended from the given key.
+ *
+ * @param key the key to take the subsets from
+ * @return the registry subsets
+ */
+ List getSubsetList( String key );
+
+ /**
+ * Get a configuration source part of the registry, identified by the given name. If it doesn't exist, null
will be
+ * returned.
+ *
+ * Configurations can be combined from different sources. This gives the configuration of a specific source.
+ *
+ * @param name The source name of the configuration source.
+ * @return the The config registry object that represents this source part.
+ */
+ ConfigRegistry getSource( String name );
+
+ /**
+ * Save any changes to the registry since it was loaded.
+ *
+ * @throws RegistryException if there was a problem saving the registry
+ * @throws UnsupportedOperationException if the registry is not writable
+ */
+ void save( )
+ throws RegistryException, UnsupportedOperationException;
+
+ /**
+ * Register a change listener for configuration keys that match the given patterns.
+ *
+ * @param listener the listener
+ */
+ void registerChangeListener( RegistryListener listener, Pattern... filter );
+
+ /**
+ * Unregister the change listener for all events.
+ *
+ * @param listener
+ * @return true
if has been removed
+ */
+ boolean unregisterChangeListener( RegistryListener listener );
+
+ /**
+ * Get all the keys in this registry. Keys are only retrieved at a depth of 1.
+ *
+ * @return the set of keys
+ */
+ Collection getKeys( );
+
+ /**
+ * Get all the keys in this registry.
+ * @return the set of keys
+ */
+ Collection getFullKeys( );
+
+ /**
+ * Remove a keyed element from the registry.
+ *
+ * @param key the key to remove
+ */
+ void remove( String key );
+
+ /**
+ * Remove a keyed subset of the registry.
+ *
+ * @param key the subset to remove
+ */
+ void removeSubset( String key );
+
+ void initialize( ) throws RegistryException;
+}
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-api/src/main/java/org/apache/archiva/redback/common/config/api/RegistryException.java b/redback-common/redback-common-configuration/redback-common-configuration-api/src/main/java/org/apache/archiva/redback/common/config/api/RegistryException.java
new file mode 100644
index 00000000..dc747880
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-api/src/main/java/org/apache/archiva/redback/common/config/api/RegistryException.java
@@ -0,0 +1,38 @@
+package org.apache.archiva.redback.common.config.api;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Exception occurring in the registry component.
+ */
+public class RegistryException
+ extends Exception
+{
+ public RegistryException( String message )
+ {
+ super( message );
+ }
+
+ public RegistryException( String message, Throwable cause )
+ {
+ super( message, cause );
+ }
+
+}
diff --git a/redback-common/redback-common-configuration/redback-common-configuration-api/src/main/java/org/apache/archiva/redback/common/config/api/RegistryListener.java b/redback-common/redback-common-configuration/redback-common-configuration-api/src/main/java/org/apache/archiva/redback/common/config/api/RegistryListener.java
new file mode 100644
index 00000000..a76d6dfc
--- /dev/null
+++ b/redback-common/redback-common-configuration/redback-common-configuration-api/src/main/java/org/apache/archiva/redback/common/config/api/RegistryListener.java
@@ -0,0 +1,45 @@
+package org.apache.archiva.redback.common.config.api;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Receives notifications of configuration changes in thre registry.
+ */
+public interface RegistryListener
+{
+ /**
+ * Notify the object that there is about to be a configuration change.
+ *
+ * @param registry the registry that was changed
+ * @param propertyName the property being changed
+ * @param propertyValue the value the property is about to be changed to
+ */
+ void beforeConfigurationChange( ConfigRegistry registry, String propertyName, Object propertyValue );
+
+ /**
+ * Notify the object that there has been a configuration change.
+ *
+ * @param registry the registry that was changed
+ * @param propertyName the property what was changed
+ * @param propertyValue the value the property was changed to
+ * @param oldValue The value the property had before
+ */
+ void afterConfigurationChange( ConfigRegistry registry, String propertyName, Object propertyValue, Object oldValue );
+}