diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/store/DefaultPropertyStore.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/store/DefaultPropertyStore.java new file mode 100644 index 000000000..b512c9f72 --- /dev/null +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/store/DefaultPropertyStore.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) The Apache Software Foundation. All rights reserved. + * + * This software is published under the terms of the Apache Software License + * version 1.1, a copy of which has been included with this distribution in + * the LICENSE.txt file. + */ +package org.apache.myrmidon.components.store; + +import java.io.File; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; +import org.apache.avalon.excalibur.i18n.ResourceManager; +import org.apache.avalon.excalibur.i18n.Resources; +import org.apache.myrmidon.api.TaskContext; +import org.apache.myrmidon.api.TaskException; +import org.apache.myrmidon.interfaces.model.DefaultNameValidator; +import org.apache.myrmidon.interfaces.model.NameValidator; +import org.apache.myrmidon.interfaces.store.PropertyStore; + +/** + * This is the Default implementation of PropertyStore. It follows + * the following rules; + * + * + * + * @author Peter Donald + * @version $Revision$ $Date$ + * @see PropertyStore + */ +public class DefaultPropertyStore + implements PropertyStore +{ + private final static Resources REZ = + ResourceManager.getPackageResources( DefaultPropertyStore.class ); + + /** + * The parent store (may be null). + */ + private final PropertyStore m_parent; + + /** + * The name validator to check property names against. + */ + private final NameValidator m_validator; + + /** + * The underlying map where propertys are actually stored. + */ + private final Map m_contextData = new Hashtable(); + + /** + * Construct a PropertyStore with no parent and + * default name-validator. + */ + public DefaultPropertyStore() + { + this( null, null ); + } + + /** + * Construct a PropertyStore with specified parent. + * + * @param parent the parent PropertyStore (may be null) + */ + public DefaultPropertyStore( final PropertyStore parent, + final NameValidator validator ) + { + m_parent = parent; + + NameValidator candidateValidator = validator; + if( null == candidateValidator ) + { + candidateValidator = createDefaultNameValidator(); + } + + m_validator = candidateValidator; + + } + + /** + * Set the property with specified name to specified value. + * The specific implementation will apply various rules + * before setting the property. + * + * @param name the name of property + * @param value the value of property + * @throws Exception if property can not be set + */ + public void setProperty( final String name, final Object value ) + throws Exception + { + checkPropertyName( name ); + checkPropertyValid( name, value ); + + if ( value == null ) + { + m_contextData.remove( name ); + } + else + { + m_contextData.put( name, value ); + } + } + + /** + * Return true if the specified property is set. + * + * @param name the name of property + */ + public boolean isPropertySet( final String name ) + { + try + { + final Object value = getProperty( name ); + if( null != value ) + { + return true; + } + } + catch( Exception e ) + { + } + return false; + } + + /** + * Retrieve the value of specified property. + * Will return null if no such property exists. + * + * @param name the name of the property + * @return the value of the property, or null if no such property + * @throws Exception if theres an error retrieving property, such + * as an invalid property name + */ + public Object getProperty( String name ) + throws Exception + { + Object value = m_contextData.get( name ); + if( value == null && m_parent != null ) + { + value = m_parent.getProperty( name ); + } + return value; + } + + /** + * Retrieve a copy of all the properties that are "in-scope" + * for store. + * + * @return a copy of all the properties that are "in-scope" + * for store. + * @throws Exception if theres an error retrieving propertys + */ + public Map getProperties() + throws Exception + { + final Map properties = new HashMap(); + if( m_parent != null ) + { + properties.putAll( m_parent.getProperties() ); + } + properties.putAll( m_contextData ); + return properties; + } + + /** + * Return a child PropertyStore with specified name. + * This is to allow support for scoped stores. However a + * store may choose to be unscoped and just return a + * reference to itself. + * + * @param name the name of child store + * @return the child store + * @throws Exception if theres an error creating child store + */ + public PropertyStore createChildStore( final String name ) + throws Exception + { + final DefaultPropertyStore store = new DefaultPropertyStore( this, m_validator ); + + final String newName = getProperty( TaskContext.NAME ) + "." + name; + store.setProperty( TaskContext.NAME, newName ); + + return store; + } + + /** + * Checks that the supplied property name is valid. + */ + private void checkPropertyName( final String name ) + throws TaskException + { + try + { + m_validator.validate( name ); + } + catch( Exception e ) + { + String message = REZ.getString( "bad-property-name.error" ); + throw new TaskException( message, e ); + } + } + + /** + * Make sure property is valid if it is one of the "magic" properties. + * + * @param name the name of property + * @param value the value of proeprty + * @exception TaskException if an error occurs + */ + private void checkPropertyValid( final String name, final Object value ) + throws TaskException + { + if( TaskContext.BASE_DIRECTORY.equals( name ) && !( value instanceof File ) ) + { + final String message = + REZ.getString( "bad-property.error", + TaskContext.BASE_DIRECTORY, + File.class.getName() ); + throw new TaskException( message ); + } + else if( TaskContext.NAME.equals( name ) && !( value instanceof String ) ) + { + final String message = + REZ.getString( "bad-property.error", + TaskContext.NAME, + String.class.getName() ); + throw new TaskException( message ); + } + } + + /** + * Create an instance of the default the name validator. + * + * @return the default NameValidator + */ + private static NameValidator createDefaultNameValidator() + { + final DefaultNameValidator defaultValidator = new DefaultNameValidator(); + defaultValidator.setAllowInternalWhitespace( false ); + defaultValidator.setAdditionalInternalCharacters( "_-.+" ); + return defaultValidator; + } +} diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/store/Resources.properties b/proposal/myrmidon/src/java/org/apache/myrmidon/components/store/Resources.properties new file mode 100644 index 000000000..835067974 --- /dev/null +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/store/Resources.properties @@ -0,0 +1,5 @@ +unknown-prop.error=Unknown property {0}. +bad-property.error=Property {0} must have a value of type {1}. +bad-property-name.error=Invalid property name. +null-resolved-value.error=Value "{0}" resolved to null. +bad-resolve.error=Unable to resolve value "{0}". diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultTaskContext.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultTaskContext.java index a71e1f497..83498b3a1 100644 --- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultTaskContext.java +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultTaskContext.java @@ -8,19 +8,18 @@ package org.apache.myrmidon.components.workspace; import java.io.File; -import java.util.Hashtable; import java.util.Map; -import java.util.HashMap; import org.apache.avalon.excalibur.i18n.ResourceManager; import org.apache.avalon.excalibur.i18n.Resources; import org.apache.avalon.excalibur.io.FileUtil; import org.apache.avalon.framework.logger.Logger; +import org.apache.avalon.framework.service.DefaultServiceManager; import org.apache.avalon.framework.service.ServiceException; import org.apache.avalon.framework.service.ServiceManager; import org.apache.myrmidon.api.TaskContext; import org.apache.myrmidon.api.TaskException; -import org.apache.myrmidon.interfaces.model.DefaultNameValidator; import org.apache.myrmidon.interfaces.property.PropertyResolver; +import org.apache.myrmidon.interfaces.store.PropertyStore; /** * Default implementation of TaskContext. @@ -34,33 +33,35 @@ public class DefaultTaskContext private static final Resources REZ = ResourceManager.getPackageResources( DefaultTaskContext.class ); - // Property name validator allows digits, but no internal whitespace. - private static DefaultNameValidator c_propertyNameValidator = - new DefaultNameValidator(); - - static - { - c_propertyNameValidator.setAllowInternalWhitespace( false ); - c_propertyNameValidator.setAdditionalInternalCharacters( "_-.+" ); - } - - private final Map m_contextData = new Hashtable(); - private final TaskContext m_parent; private final ServiceManager m_serviceManager; private final Logger m_logger; + private final PropertyStore m_store; private PropertyResolver m_propertyResolver; /** * Constructor that takes both parent context and a service directory. */ - public DefaultTaskContext( final TaskContext parent, - final ServiceManager serviceManager, - final Logger logger ) + public DefaultTaskContext( final ServiceManager serviceManager, + final Logger logger, + final PropertyStore store ) throws TaskException { - m_parent = parent; m_serviceManager = serviceManager; m_logger = logger; + m_store = store; + + if( null == m_serviceManager ) + { + throw new NullPointerException( "serviceManager" ); + } + if( null == m_logger ) + { + throw new NullPointerException( "logger" ); + } + if( null == m_store ) + { + throw new NullPointerException( "store" ); + } } /** @@ -96,8 +97,8 @@ public class DefaultTaskContext public Object getService( final Class serviceClass ) throws TaskException { - // Try this context first final String name = serviceClass.getName(); + //Note that this will chain up to parent ServiceManagers (if any) if( null != m_serviceManager && m_serviceManager.hasService( name ) ) { try @@ -110,12 +111,6 @@ public class DefaultTaskContext } } - // Try parent - if( null != m_parent ) - { - return m_parent.getService( serviceClass ); - } - // Not found final String message = REZ.getString( "bad-find-service.error", name ); throw new TaskException( message ); @@ -179,12 +174,14 @@ public class DefaultTaskContext */ public Object getProperty( final String name ) { - Object value = m_contextData.get( name ); - if( value == null && m_parent != null ) + try + { + return m_store.getProperty( name ); + } + catch( final Exception e ) { - value = m_parent.getProperty( name ); + return null; } - return value; } /** @@ -193,14 +190,16 @@ public class DefaultTaskContext * @return the map of all property names to values */ public Map getProperties() + throws TaskException { - Map props = new HashMap(); - if( m_parent != null ) + try + { + return m_store.getProperties(); + } + catch( final Exception e ) { - props.putAll( m_parent.getProperties() ); + throw new TaskException( e.getMessage(), e ); } - props.putAll( m_contextData ); - return props; } /** @@ -212,16 +211,13 @@ public class DefaultTaskContext public void setProperty( final String name, final Object value ) throws TaskException { - checkPropertyName( name ); - checkPropertyValid( name, value ); - - if ( value == null ) + try { - m_contextData.remove( name ); + m_store.setProperty( name, value ); } - else + catch( final Exception e ) { - m_contextData.put( name, value ); + throw new TaskException( e.getMessage(), e ); } } @@ -390,53 +386,19 @@ public class DefaultTaskContext */ public TaskContext createSubContext( final String name ) throws TaskException - { - final DefaultTaskContext context = - new DefaultTaskContext( this, m_serviceManager, m_logger ); - - context.setProperty( TaskContext.NAME, getName() + "." + name ); - context.setProperty( TaskContext.BASE_DIRECTORY, getBaseDirectory() ); - - return context; - } - - /** - * Checks that the supplied property name is valid. - */ - private void checkPropertyName( final String name ) throws TaskException { try { - c_propertyNameValidator.validate( name ); - } - catch( Exception e ) - { - String message = REZ.getString( "bad-property-name.error" ); - throw new TaskException( message, e ); - } - } + final PropertyStore store = m_store.createChildStore( name ); + final DefaultServiceManager serviceManager = + new DefaultServiceManager( m_serviceManager ); + final Logger logger = m_logger.getChildLogger( name ); - /** - * Make sure property is valid if it is one of the "magic" properties. - * - * @param name the name of property - * @param value the value of proeprty - * @exception TaskException if an error occurs - */ - private void checkPropertyValid( final String name, final Object value ) - throws TaskException - { - if( BASE_DIRECTORY.equals( name ) && !( value instanceof File ) ) - { - final String message = - REZ.getString( "bad-property.error", BASE_DIRECTORY, File.class.getName() ); - throw new TaskException( message ); + return new DefaultTaskContext( serviceManager, logger, store ); } - else if( NAME.equals( name ) && !( value instanceof String ) ) + catch( final Exception e ) { - final String message = - REZ.getString( "bad-property.error", NAME, String.class.getName() ); - throw new TaskException( message ); + throw new TaskException( e.getMessage(), e ); } } } diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultWorkspace.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultWorkspace.java index 1c4ae8e22..e71d84c4d 100644 --- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultWorkspace.java +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultWorkspace.java @@ -37,7 +37,9 @@ import org.apache.myrmidon.interfaces.model.Target; import org.apache.myrmidon.interfaces.model.TypeLib; import org.apache.myrmidon.interfaces.type.TypeManager; import org.apache.myrmidon.interfaces.workspace.Workspace; +import org.apache.myrmidon.interfaces.store.PropertyStore; import org.apache.myrmidon.listeners.ProjectListener; +import org.apache.myrmidon.components.store.DefaultPropertyStore; /** * This is the default implementation of Workspace. @@ -56,7 +58,7 @@ public class DefaultWorkspace private ProjectListenerSupport m_listenerSupport = new ProjectListenerSupport(); private ServiceManager m_serviceManager; private Parameters m_parameters; - private TaskContext m_baseContext; + private PropertyStore m_baseStore; private HashMap m_entries = new HashMap(); private TypeManager m_typeManager; private Deployer m_deployer; @@ -105,7 +107,7 @@ public class DefaultWorkspace public void initialize() throws Exception { - m_baseContext = createBaseContext(); + m_baseStore = createBaseStore(); } /** @@ -128,22 +130,22 @@ public class DefaultWorkspace m_listenerSupport.projectFinished( project.getProjectName() ); } - private TaskContext createBaseContext() - throws TaskException + private PropertyStore createBaseStore() + throws Exception { - final TaskContext context = new DefaultTaskContext( null, null, null ); + final DefaultPropertyStore store = new DefaultPropertyStore(); final String[] names = m_parameters.getNames(); for( int i = 0; i < names.length; i++ ) { final String value = m_parameters.getParameter( names[ i ], null ); - context.setProperty( names[ i ], value ); + store.setProperty( names[ i ], value ); } //Add system properties so that they overide user-defined properties - addToContext( context, System.getProperties() ); + addToStore( store, System.getProperties() ); - return context; + return store; } private File findTypeLib( final String libraryName ) @@ -250,9 +252,12 @@ public class DefaultWorkspace final Logger logger = new RoutingLogger( getLogger(), m_listenerSupport ); - // Create and configure the context + //TODO: Put this in Execution Frame + final PropertyStore store = m_baseStore.createChildStore(""); + + // Create and configure the context final DefaultTaskContext context = - new DefaultTaskContext( m_baseContext, serviceManager, logger ); + new DefaultTaskContext( serviceManager, logger, store ); context.setProperty( TaskContext.BASE_DIRECTORY, project.getBaseDirectory() ); final DefaultExecutionFrame frame = @@ -467,13 +472,13 @@ public class DefaultWorkspace } /** - * Helper method to add values to a context + * Helper method to add values to a store. * - * @param context the context + * @param store the store * @param map the map of names->values */ - private void addToContext( final TaskContext context, final Map map ) - throws TaskException + private void addToStore( final PropertyStore store, final Map map ) + throws Exception { final Iterator keys = map.keySet().iterator(); @@ -481,7 +486,7 @@ public class DefaultWorkspace { final String key = (String)keys.next(); final Object value = map.get( key ); - context.setProperty( key, value ); + store.setProperty( key, value ); } } } diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/model/NameValidator.java b/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/model/NameValidator.java index d754a50e4..ecf0209fb 100644 --- a/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/model/NameValidator.java +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/model/NameValidator.java @@ -17,8 +17,10 @@ public interface NameValidator { /** * Validates the supplied name, failing if it is not. + * * @param name The name to be validated. * @throws Exception is the supplied name is not valid. */ - void validate( String name ) throws Exception; + void validate( String name ) + throws Exception; } diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/store/PropertyStore.java b/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/store/PropertyStore.java new file mode 100644 index 000000000..1a66864d5 --- /dev/null +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/store/PropertyStore.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) The Apache Software Foundation. All rights reserved. + * + * This software is published under the terms of the Apache Software License + * version 1.1, a copy of which has been included with this distribution in + * the LICENSE.txt file. + */ +package org.apache.myrmidon.interfaces.store; + +import java.util.Map; + +/** + * This component stores and manages properties. It is also + * responsible for instituting the various policies regarding + * propertys. ie It will enforce rules regarding + * + * + * + * @author Peter Donald + * @version $Revision$ $Date$ + */ +public interface PropertyStore +{ + /** Role name for this interface. */ + String ROLE = PropertyStore.class.getName(); + + /** + * Set the property with specified name to specified value. + * The specific implementation will apply various rules + * before setting the property. + * + * @param name the name of property + * @param value the value of property + * @throws Exception if property can not be set + */ + void setProperty( String name, Object value ) + throws Exception; + + /** + * Return true if the specified property is set. + * + * @param name the name of property + */ + boolean isPropertySet( String name ); + + /** + * Retrieve the value of specified property. + * Will return null if no such property exists. + * + * @param name the name of the property + * @return the value of the property, or null if no such property + * @throws Exception if theres an error retrieving property, such + * as an invalid property name + */ + Object getProperty( String name ) + throws Exception; + + /** + * Retrieve a copy of all the properties that are "in-scope" + * for store. + * + * @return a copy of all the properties that are "in-scope" + * for store. + * @throws Exception if theres an error retrieving propertys + */ + Map getProperties() + throws Exception; + + /** + * Return a child PropertyStore with specified name. + * This is to allow support for scoped stores. However a + * store may choose to be unscoped and just return a + * reference to itself. + * + * @param name the name of child store + * @return the child store + * @throws Exception if theres an error creating child store + */ + PropertyStore createChildStore( String name ) + throws Exception; +} diff --git a/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/test/DefaultConfigurerTestCase.java b/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/test/DefaultConfigurerTestCase.java index bf723f5aa..eced05dfc 100644 --- a/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/test/DefaultConfigurerTestCase.java +++ b/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/test/DefaultConfigurerTestCase.java @@ -15,6 +15,7 @@ import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.configuration.DefaultConfiguration; import org.apache.myrmidon.api.TaskContext; import org.apache.myrmidon.components.AbstractComponentTest; +import org.apache.myrmidon.components.store.DefaultPropertyStore; import org.apache.myrmidon.components.configurer.DefaultConfigurer; import org.apache.myrmidon.components.configurer.test.data.ConfigTestAttributeConvert; import org.apache.myrmidon.components.configurer.test.data.ConfigTestConfigAdder; @@ -76,7 +77,9 @@ public class DefaultConfigurerTestCase m_configurer = (Configurer)getServiceManager().lookup( Configurer.ROLE ); // Setup a context - m_context = new DefaultTaskContext( null, getServiceManager(), getLogger() ); + final DefaultPropertyStore store = new DefaultPropertyStore(); + m_context = + new DefaultTaskContext( getServiceManager(), getLogger(), store ); final File baseDir = new File( "." ).getAbsoluteFile(); m_context.setProperty( TaskContext.BASE_DIRECTORY, baseDir ); } diff --git a/proposal/myrmidon/src/test/org/apache/myrmidon/components/property/test/AbstractPropertyResolverTestCase.java b/proposal/myrmidon/src/test/org/apache/myrmidon/components/property/test/AbstractPropertyResolverTestCase.java index ec2039ff5..74d959cd2 100644 --- a/proposal/myrmidon/src/test/org/apache/myrmidon/components/property/test/AbstractPropertyResolverTestCase.java +++ b/proposal/myrmidon/src/test/org/apache/myrmidon/components/property/test/AbstractPropertyResolverTestCase.java @@ -11,9 +11,11 @@ import java.io.File; import java.util.Date; import org.apache.aut.converter.lib.ObjectToStringConverter; import org.apache.avalon.excalibur.i18n.Resources; +import org.apache.avalon.framework.service.DefaultServiceManager; import org.apache.myrmidon.api.TaskContext; import org.apache.myrmidon.api.TaskException; import org.apache.myrmidon.components.AbstractComponentTest; +import org.apache.myrmidon.components.store.DefaultPropertyStore; import org.apache.myrmidon.components.workspace.DefaultTaskContext; import org.apache.myrmidon.interfaces.property.PropertyResolver; @@ -40,7 +42,9 @@ public abstract class AbstractPropertyResolverTestCase { m_resolver = (PropertyResolver)getServiceManager().lookup( PropertyResolver.ROLE ); - m_context = new DefaultTaskContext( null, null, getLogger() ); + final DefaultPropertyStore store = new DefaultPropertyStore(); + final DefaultServiceManager serviceManager = new DefaultServiceManager(); + m_context = new DefaultTaskContext( serviceManager, getLogger(), store ); m_context.setProperty( "intProp", new Integer( 333 ) ); m_context.setProperty( "stringProp", "String property" );