git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@272257 13f79535-47bb-0310-9956-ffa450edef68master
@@ -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; | |||
* | |||
* <ul> | |||
* <li>The property names must pass DefaultNameValidator checks</li> | |||
* <li>The store is mutable</li> | |||
* <li>If the key is TaskContext.NAME then value must be a string.</li> | |||
* <li>If the key is TaskContext.BASE_DIRECTORY then value must be a key.</li> | |||
* </ul> | |||
* | |||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||
* @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 <code>true</code> 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; | |||
} | |||
} |
@@ -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}". |
@@ -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 ); | |||
} | |||
} | |||
} |
@@ -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 ); | |||
} | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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 | |||
* | |||
* <ul> | |||
* <li>Valid property names?</li> | |||
* <li>Are propertys mutable?</li> | |||
* <li>Are propertys scoped?</li> | |||
* <li>Is mapping between name and value correct?</li> | |||
* </ul> | |||
* | |||
* @author <a href="mailto:peter@apache.org">Peter Donald</a> | |||
* @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 <code>true</code> 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; | |||
} |
@@ -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 ); | |||
} | |||
@@ -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" ); | |||