You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by do...@apache.org on 2003/04/16 03:15:05 UTC
cvs commit: avalon-phoenix/src/java/org/apache/avalon/phoenix/components/configuration/merger ConfigurationMerger.java ConfigurationSplitter.java Constants.java
donaldp 2003/04/15 18:15:05
Modified: src/java/org/apache/avalon/phoenix/components/configuration
FileSystemPersistentConfigurationRepository.java
Added: src/java/org/apache/avalon/phoenix/components/configuration/merger
ConfigurationMerger.java ConfigurationSplitter.java
Constants.java
Log:
Make phoenix use own copy of merger so we
don't have to rely on libraries that don't
have proper unit testing. Make it easier to
get closer to 100% coverage of phoenix
codebase.
Revision Changes Path
1.22 +1 -3 avalon-phoenix/src/java/org/apache/avalon/phoenix/components/configuration/FileSystemPersistentConfigurationRepository.java
Index: FileSystemPersistentConfigurationRepository.java
===================================================================
RCS file: /home/cvs/avalon-phoenix/src/java/org/apache/avalon/phoenix/components/configuration/FileSystemPersistentConfigurationRepository.java,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -r1.21 -r1.22
--- FileSystemPersistentConfigurationRepository.java 8 Apr 2003 20:29:54 -0000 1.21
+++ FileSystemPersistentConfigurationRepository.java 16 Apr 2003 01:15:04 -0000 1.22
@@ -54,7 +54,6 @@
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
-
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;
import org.apache.avalon.excalibur.io.FileUtil;
@@ -68,10 +67,9 @@
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.phoenix.components.configuration.merger.ConfigurationMerger;
import org.apache.avalon.phoenix.components.util.PropertyUtil;
import org.apache.avalon.phoenix.interfaces.ConfigurationRepository;
-import org.apache.excalibur.configuration.merged.ConfigurationMerger;
-
import org.xml.sax.SAXException;
/**
1.1 avalon-phoenix/src/java/org/apache/avalon/phoenix/components/configuration/merger/ConfigurationMerger.java
Index: ConfigurationMerger.java
===================================================================
/*
============================================================================
The Apache Software License, Version 1.1
============================================================================
Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without modifica-
tion, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The end-user documentation included with the redistribution, if any, must
include the following acknowledgment: "This product includes software
developed by the Apache Software Foundation (http://www.apache.org/)."
Alternately, this acknowledgment may appear in the software itself, if
and wherever such third-party acknowledgments normally appear.
4. The names "Jakarta", "Avalon", "Excalibur" and "Apache Software Foundation"
must not be used to endorse or promote products derived from this software
without prior written permission. For written permission, please contact
apache@apache.org.
5. Products derived from this software may not be called "Apache", nor may
"Apache" appear in their name, without prior written permission of the
Apache Software Foundation.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This software consists of voluntary contributions made by many individuals
on behalf of the Apache Software Foundation. For more information on the
Apache Software Foundation, please see <http://www.apache.org/>.
*/
package org.apache.avalon.phoenix.components.configuration.merger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
/**
* The ConfigurationMerger will take a Configuration object and layer it over another.
*
* It will use special attributes on the layer's children to control how children
* of the layer and base are combined. In order for a child of the layer to be merged with a
* child of the base, the following must hold true:
* <ol>
* <li>The child in the <b>layer</b> Configuration has an attribute named
* <code>phoenix-configuration:merge</code> and its value is equal to a boolean
* <code>TRUE</code>
* </li>
* <li>There must be a single child in both the layer and base with the same getName() <b>OR</b>
* there exists an attribute named <code>phoenix-configuration:key-attribute</code>
* that names an attribute that exists on both the layer and base that can be used to match
* multiple children of the same getName()
* </li>
* </ol>
*
* @see org.apache.excalibur.configuration.merged.ConfigurationSplitter
* @author <a href="mailto:proyal@apache.org">Peter Royal</a>
*/
public class ConfigurationMerger
{
/**
* Merge two configurations.
*
* @param layer Configuration to <i>layer</i> over the base
* @param base Configuration <i>layer</i> will be merged with
*
* @return Result of merge
*
* @exception org.apache.avalon.framework.configuration.ConfigurationException if unable to merge
*/
public static Configuration merge( final Configuration layer, final Configuration base )
throws ConfigurationException
{
final DefaultConfiguration merged =
new DefaultConfiguration( base.getName(),
"Merged [layer: " + layer.getLocation()
+ ", base: " + base.getLocation() + "]" );
copyAttributes( base, merged );
copyAttributes( layer, merged );
mergeChildren( layer, base, merged );
merged.setValue( getValue( layer, base ) );
merged.makeReadOnly();
return merged;
}
private static void mergeChildren( final Configuration layer,
final Configuration base,
final DefaultConfiguration merged )
throws ConfigurationException
{
final Configuration[] lc = layer.getChildren();
final Configuration[] bc = base.getChildren();
final Set baseUsed = new HashSet();
for( int i = 0; i < lc.length; i++ )
{
final Configuration mergeWith = getMergePartner( lc[ i ], layer, base );
if( null == mergeWith )
{
merged.addChild( lc[ i ] );
}
else
{
merged.addChild( merge( lc[ i ], mergeWith ) );
baseUsed.add( mergeWith );
}
}
for( int i = 0; i < bc.length; i++ )
{
if( !baseUsed.contains( bc[ i ] ) )
{
merged.addChild( bc[ i ] );
}
}
}
private static Configuration getMergePartner( final Configuration toMerge,
final Configuration layer,
final Configuration base )
throws ConfigurationException
{
if( toMerge.getAttributeAsBoolean( Constants.MERGE_ATTR, false ) )
{
final String keyAttribute = toMerge.getAttribute( Constants.KEY_ATTR, null );
final String keyvalue =
keyAttribute == null ? null : toMerge.getAttribute( keyAttribute );
final Configuration[] layerKids = match( layer,
toMerge.getName(),
keyAttribute,
keyvalue );
final Configuration[] baseKids = match( base,
toMerge.getName(),
keyAttribute,
keyvalue );
if( layerKids.length == 1 && baseKids.length == 1 )
{
return baseKids[ 0 ];
}
else
{
throw new ConfigurationException( "Unable to merge configuration item, "
+ "multiple matches on child or base [name: "
+ toMerge.getName() + "]" );
}
}
return null;
}
private static String getValue( final Configuration layer, final Configuration base )
{
try
{
return layer.getValue();
}
catch( ConfigurationException e )
{
return base.getValue( null );
}
}
private static void copyAttributes( final Configuration source,
final DefaultConfiguration dest )
throws ConfigurationException
{
final String[] names = source.getAttributeNames();
for( int i = 0; i < names.length; i++ )
{
if( !names[ i ].startsWith( Constants.MERGE_METADATA_PREFIX ) )
{
dest.setAttribute( names[ i ], source.getAttribute( names[ i ] ) );
}
}
}
/**
* Return all occurance of a configuration child containing the supplied attribute name.
* @param config the configuration
* @param element the name of child elements to select from the configuration
* @param attribute the attribute name to filter (null will match any attribute name)
* @return an array of configuration instances matching the query
*/
public static Configuration[] match( final Configuration config,
final String element,
final String attribute )
{
return match( config, element, attribute, null );
}
/**
* Return occurance of a configuration child containing the supplied attribute name and value.
* @param config the configuration
* @param element the name of child elements to select from the configuration
* @param attribute the attribute name to filter (null will match any attribute name )
* @param value the attribute value to match (null will match any attribute value)
* @return an array of configuration instances matching the query
*/
public static Configuration[] match( final Configuration config,
final String element,
final String attribute,
final String value )
{
final ArrayList list = new ArrayList();
final Configuration[] children = config.getChildren( element );
for( int i = 0; i < children.length; i++ )
{
if( null == attribute )
{
list.add( children[ i ] );
}
else
{
String v = children[ i ].getAttribute( attribute, null );
if( v != null )
{
if( ( value == null ) || v.equals( value ) )
{
// it's a match
list.add( children[ i ] );
}
}
}
}
return (Configuration[])list.toArray( new Configuration[ list.size() ] );
}
}
1.1 avalon-phoenix/src/java/org/apache/avalon/phoenix/components/configuration/merger/ConfigurationSplitter.java
Index: ConfigurationSplitter.java
===================================================================
/*
============================================================================
The Apache Software License, Version 1.1
============================================================================
Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without modifica-
tion, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The end-user documentation included with the redistribution, if any, must
include the following acknowledgment: "This product includes software
developed by the Apache Software Foundation (http://www.apache.org/)."
Alternately, this acknowledgment may appear in the software itself, if
and wherever such third-party acknowledgments normally appear.
4. The names "Jakarta", "Avalon", "Excalibur" and "Apache Software Foundation"
must not be used to endorse or promote products derived from this software
without prior written permission. For written permission, please contact
apache@apache.org.
5. Products derived from this software may not be called "Apache", nor may
"Apache" appear in their name, without prior written permission of the
Apache Software Foundation.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This software consists of voluntary contributions made by many individuals
on behalf of the Apache Software Foundation. For more information on the
Apache Software Foundation, please see <http://www.apache.org/>.
*/
package org.apache.avalon.phoenix.components.configuration.merger;
import java.util.HashSet;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.avalon.framework.configuration.ConfigurationUtil;
/**
* The ConfigurationSplitter will take two Configuration objects and calculate the
* differences between them.
*
* The resulting Configuration will contain special attribute that can be used by the
* ConfigurationMerger to reconstruct the original Configuration
*
* @see ConfigurationMerger
* @author <a href="mailto:proyal@apache.org">Peter Royal</a>
*/
public class ConfigurationSplitter
{
/**
* Split a configuration, given a merged configuration and a base configuration.
* Functionally equivalent to producing a <i>diff</i> between the merged and base.
*
* @param merged Configuration that is a combination of the <i>result</i> and
* the <i>base</i> param
* @param base Configuration that when merged with the <i>result</i> will yield the
* <i>merged</i> param
*
* @return a Configuration that when merged with the <i>base</i> will yeild the
* <i>merged</i>
*
* @throws ConfigurationException if unable to split
*/
public static Configuration split( final Configuration merged, final Configuration base )
throws ConfigurationException
{
final DefaultConfiguration layer = doSplit( merged, base, false );
layer.makeReadOnly();
return layer;
}
private static DefaultConfiguration doSplit( final Configuration merged,
final Configuration base,
final boolean isMerged )
throws ConfigurationException
{
final DefaultConfiguration layer =
new DefaultConfiguration( base.getName(),
"Merged [merged: " + merged.getLocation()
+ ", base: " + base.getLocation() + "]" );
copyAttributes( layer, merged, base );
copyValue( layer, merged, base );
copyChildren( layer, merged, base );
if( isMerged )
{
layer.setAttribute( Constants.MERGE_ATTR, "true" );
}
return layer;
}
private static DefaultConfiguration doSplit( final Configuration merged,
final Configuration base,
final String keyAttr )
throws ConfigurationException
{
final DefaultConfiguration layer = doSplit( merged, base, true );
if( null != keyAttr )
{
layer.setAttribute( Constants.KEY_ATTR, keyAttr );
}
return layer;
}
private static void copyChildren( final DefaultConfiguration layer,
final Configuration merged,
final Configuration base )
throws ConfigurationException
{
final Configuration[] kids = merged.getChildren();
for( int i = 0; i < kids.length; i++ )
{
final Configuration mergedChild = kids[ i ];
final String name = mergedChild.getName();
final Configuration[] mc = merged.getChildren( name );
final Configuration[] bc = base.getChildren( name );
Configuration mergedWith;
String keyAttr = null;
if( bc.length > mc.length )
{
throw new UnsupportedOperationException( "Unable to mask children from base "
+ "in layer" );
}
else if( bc.length == 0 )
{
mergedWith = null;
}
else if( mc.length == 1 && bc.length == 1 )
{
mergedWith = bc[ 0 ];
}
else //we know that mc.length > 1 here, so find the "key" attribute
{
keyAttr = findUniqueAttributeName( mc );
final String keyValue = mergedChild.getAttribute( keyAttr );
final Configuration[] matches =
ConfigurationMerger.match( base, name, keyAttr, keyValue );
if( matches.length == 1 )
{
mergedWith = matches[ 0 ];
}
else
{
throw new ConfigurationException( "Multiple children in base with name '"
+ name + "' and attr '" + keyAttr
+ " = " + keyValue + "'" );
}
}
if( null == mergedWith )
{
layer.addChild( kids[ i ] );
}
else if( !ConfigurationUtil.equals( kids[ i ], mergedWith ) )
{
final DefaultConfiguration layerChild = doSplit( kids[ i ], mergedWith, keyAttr );
layerChild.makeReadOnly();
layer.addChild( layerChild );
}
}
}
private static String findUniqueAttributeName( final Configuration[] c )
throws ConfigurationException
{
final HashSet testedAttributes = new HashSet();
String uniqueAttr = null;
for( int i = 0; i < c.length; i++ )
{
final String[] attrs = c[ i ].getAttributeNames();
for( int j = 0; j < attrs.length; j++ )
{
final String attr = attrs[ j ];
if( !testedAttributes.contains( attr )
&& isUniqueAttribute( attr, c[ i ].getAttribute( attr ), i, c ) )
{
if( null == uniqueAttr )
{
uniqueAttr = attr;
}
else
{
throw new ConfigurationException( "Multiple unique attributes for child "
+ "[name: " + c[ 0 ].getName()
+ ", unique1: " + uniqueAttr
+ ", unique2: " + attr + "]" );
}
}
else
{
testedAttributes.add( attr );
}
}
}
if( null == uniqueAttr )
{
throw new ConfigurationException( "Unable to find unique attribute for "
+ "children of name: " + c[ 0 ].getName() );
}
return uniqueAttr;
}
private static boolean isUniqueAttribute( final String attr,
final String value,
final int currentConfig,
final Configuration[] c )
{
for( int i = 0; i < c.length; i++ )
{
if( i != currentConfig )
{
try
{
if( value.equals( c[ i ].getAttribute( attr ) ) )
{
return false;
}
}
catch( ConfigurationException e )
{
return false;
}
}
}
return true;
}
private static void copyValue( final DefaultConfiguration layer,
final Configuration merged,
final Configuration base )
{
final String value = merged.getValue( null );
if( null != value )
{
try
{
final String baseValue = base.getValue();
if( !value.equals( baseValue ) )
{
layer.setValue( value );
}
}
catch( ConfigurationException e )
{
layer.setValue( value );
}
}
}
private static void copyAttributes( final DefaultConfiguration layer,
final Configuration merged,
final Configuration base )
throws ConfigurationException
{
final String[] mergedAttr = merged.getAttributeNames();
for( int i = 0; i < mergedAttr.length; i++ )
{
final String value = merged.getAttribute( mergedAttr[ i ] );
try
{
final String baseValue = base.getAttribute( mergedAttr[ i ] );
if( !value.equals( baseValue ) )
{
layer.setAttribute( mergedAttr[ i ], value );
}
}
catch( ConfigurationException e )
{
//not in base add to layer
layer.setAttribute( mergedAttr[ i ], value );
}
}
}
}
1.1 avalon-phoenix/src/java/org/apache/avalon/phoenix/components/configuration/merger/Constants.java
Index: Constants.java
===================================================================
/*
============================================================================
The Apache Software License, Version 1.1
============================================================================
Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without modifica-
tion, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The end-user documentation included with the redistribution, if any, must
include the following acknowledgment: "This product includes software
developed by the Apache Software Foundation (http://www.apache.org/)."
Alternately, this acknowledgment may appear in the software itself, if
and wherever such third-party acknowledgments normally appear.
4. The names "Jakarta", "Avalon", "Excalibur" and "Apache Software Foundation"
must not be used to endorse or promote products derived from this software
without prior written permission. For written permission, please contact
apache@apache.org.
5. Products derived from this software may not be called "Apache", nor may
"Apache" appear in their name, without prior written permission of the
Apache Software Foundation.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This software consists of voluntary contributions made by many individuals
on behalf of the Apache Software Foundation. For more information on the
Apache Software Foundation, please see <http://www.apache.org/>.
*/
package org.apache.avalon.phoenix.components.configuration.merger;
/**
* Constants used by this package
*
* @author <a href="mailto:proyal@apache.org">Peter Royal</a>
*/
interface Constants
{
String MERGE_METADATA_PREFIX = "excalibur-configuration:";
String MERGE_ATTR = MERGE_METADATA_PREFIX + "merge";
String KEY_ATTR = MERGE_METADATA_PREFIX + "key-attribute";
}
---------------------------------------------------------------------
To unsubscribe, e-mail: cvs-unsubscribe@avalon.apache.org
For additional commands, e-mail: cvs-help@avalon.apache.org