support for default "conversionService" bean in an ApplicationContext; revised formatting package, now integrated with DataBinder and AnnotationMethodHandlerAdapter; revised AccessControlContext access from BeanFactory

This commit is contained in:
Juergen Hoeller
2009-08-24 13:30:42 +00:00
parent 9f9f2349cd
commit fee838a65e
33 changed files with 846 additions and 320 deletions

View File

@@ -36,9 +36,11 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@@ -326,6 +328,24 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
return null;
}
public TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException {
try {
PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName);
if (pd != null) {
if (pd.getReadMethod() != null) {
return new TypeDescriptor(new MethodParameter(pd.getReadMethod(), -1));
}
else if (pd.getWriteMethod() != null) {
return new TypeDescriptor(new MethodParameter(pd.getWriteMethod(), 0));
}
}
}
catch (InvalidPropertyException ex) {
// Consider as not determinable.
}
return null;
}
public boolean isReadableProperty(String propertyName) {
try {
PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName);

View File

@@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -86,6 +87,14 @@ public class DirectFieldAccessor extends AbstractPropertyAccessor {
return null;
}
public TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException {
Field field = this.fieldMap.get(propertyName);
if (field != null) {
return new TypeDescriptor(field);
}
return null;
}
@Override
public Object getPropertyValue(String propertyName) throws BeansException {
Field field = this.fieldMap.get(propertyName);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package org.springframework.beans;
import java.util.Map;
import org.springframework.core.convert.TypeDescriptor;
/**
* Common interface for classes that can access named properties
* (such as bean properties of an object or fields in an object)
@@ -86,6 +88,17 @@ public interface PropertyAccessor {
*/
Class getPropertyType(String propertyName) throws BeansException;
/**
* Return a type descriptor for the specified property.
* @param propertyName the property to check
* (may be a nested path and/or an indexed/mapped property)
* @return the property type for the particular property,
* or <code>null</code> if not determinable
* @throws InvalidPropertyException if there is no such property or
* if the property isn't readable
*/
TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;
/**
* Get the current value of the specified property.
* @param propertyName the name of the property to get the value of

View File

@@ -20,6 +20,7 @@ import java.beans.PropertyDescriptor;
import java.beans.PropertyEditor;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
@@ -203,8 +204,18 @@ class TypeConverterDelegate {
convertedValue = convertToTypedMap((Map) convertedValue, propertyName, methodParam);
}
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
String strValue = ((String) convertedValue).trim();
if (requiredType.isEnum() && "".equals(strValue)) {
try {
Constructor strCtor = requiredType.getConstructor(String.class);
return (T) BeanUtils.instantiateClass(strCtor, convertedValue);
}
catch (NoSuchMethodException ex) {
// proceed with field lookup
if (logger.isTraceEnabled()) {
logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);
}
}
String trimmedValue = ((String) convertedValue).trim();
if (requiredType.isEnum() && "".equals(trimmedValue)) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
@@ -212,7 +223,7 @@ class TypeConverterDelegate {
// with values defined as static fields. Resulting value still needs
// to be checked, hence we don't return it right away.
try {
Field enumField = requiredType.getField(strValue);
Field enumField = requiredType.getField(trimmedValue);
convertedValue = enumField.get(null);
}
catch (Throwable ex) {

View File

@@ -17,6 +17,7 @@
package org.springframework.beans.factory.config;
import java.beans.PropertyEditor;
import java.security.AccessControlContext;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
@@ -249,6 +250,12 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single
*/
Scope getRegisteredScope(String scopeName);
/**
* Provides a security access control context relevant to this factory.
* @return the applicable AccessControlContext (never <code>null</code>)
*/
AccessControlContext getAccessControlContext();
/**
* Copy all relevant configuration from the given other factory.
* <p>Should include all standard configuration settings as well as

View File

@@ -150,6 +150,9 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
/** Map from scope identifier String to corresponding Scope */
private final Map<String, Scope> scopes = new HashMap<String, Scope>();
/** Security context used when running with a SecurityManager */
private SecurityContextProvider securityContextProvider;
/** Map from bean name to merged RootBeanDefinition */
private final Map<String, RootBeanDefinition> mergedBeanDefinitions =
new ConcurrentHashMap<String, RootBeanDefinition>();
@@ -161,9 +164,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
new NamedThreadLocal<Object>("Prototype beans currently in creation");
/** security context used when running with a Security Manager */
private volatile SecurityContextProvider securityProvider = new SimpleSecurityContextProvider();
/**
* Create a new AbstractBeanFactory.
*/
@@ -761,6 +761,26 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
return this.scopes.get(scopeName);
}
/**
* Set the security context provider for this bean factory. If a security manager
* is set, interaction with the user code will be executed using the privileged
* of the provided security context.
*/
public void setSecurityContextProvider(SecurityContextProvider securityProvider) {
this.securityContextProvider = securityProvider;
}
/**
* Delegate the creation of the access control context to the
* {@link #setSecurityContextProvider SecurityContextProvider}.
*/
@Override
public AccessControlContext getAccessControlContext() {
return (this.securityContextProvider != null ?
this.securityContextProvider.getAccessControlContext() :
AccessController.getContext());
}
public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
Assert.notNull(otherFactory, "BeanFactory must not be null");
setBeanClassLoader(otherFactory.getBeanClassLoader());
@@ -776,6 +796,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
this.hasDestructionAwareBeanPostProcessors = this.hasDestructionAwareBeanPostProcessors ||
otherAbstractFactory.hasDestructionAwareBeanPostProcessors;
this.scopes.putAll(otherAbstractFactory.scopes);
this.securityContextProvider = otherAbstractFactory.securityContextProvider;
}
else {
setTypeConverter(otherFactory.getTypeConverter());
@@ -1436,37 +1457,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
}
}
}
/**
* {@inheritDoc}
*
* Delegate the creation of the security context to {@link #getSecurityContextProvider()}.
*/
@Override
protected AccessControlContext getAccessControlContext() {
SecurityContextProvider provider = getSecurityContextProvider();
return (provider != null ? provider.getAccessControlContext(): AccessController.getContext());
}
/**
* Return the security context provider for this bean factory.
*
* @return
*/
public SecurityContextProvider getSecurityContextProvider() {
return securityProvider;
}
/**
* Set the security context provider for this bean factory. If a security manager
* is set, interaction with the user code will be executed using the privileged
* of the provided security context.
*
* @param securityProvider
*/
public void setSecurityContextProvider(SecurityContextProvider securityProvider) {
this.securityProvider = securityProvider;
}
//---------------------------------------------------------------------
// Abstract methods to be implemented by subclasses
@@ -1526,4 +1517,5 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
*/
protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args)
throws BeanCreationException;
}

View File

@@ -212,10 +212,10 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
* Returns the security context for this bean factory. If a security manager
* is set, interaction with the user code will be executed using the privileged
* of the security context returned by this method.
*
* @return
* @see AccessController#getContext()
*/
protected AccessControlContext getAccessControlContext() {
return AccessController.getContext();
}
}
}

View File

@@ -20,15 +20,16 @@ import java.security.AccessControlContext;
/**
* Provider of the security context of the code running inside the bean factory.
*
*
* @author Costin Leau
* @since 3.0
*/
public interface SecurityContextProvider {
/**
* Provides a security access control context relevant to a bean factory.
*
* @return bean factory security control context
*/
AccessControlContext getAccessControlContext();
}

View File

@@ -20,18 +20,19 @@ import java.security.AccessControlContext;
import java.security.AccessController;
/**
* Simple #SecurityContextProvider implementation.
* Simple {@link SecurityContextProvider} implementation.
*
* @author Costin Leau
* @since 3.0
*/
public class SimpleSecurityContextProvider implements SecurityContextProvider {
private final AccessControlContext acc;
/**
* Constructs a new <code>SimpleSecurityContextProvider</code> instance.
*
* The security context will be retrieved on each call from the current
* Construct a new <code>SimpleSecurityContextProvider</code> instance.
* <p>The security context will be retrieved on each call from the current
* thread.
*/
public SimpleSecurityContextProvider() {
@@ -39,20 +40,19 @@ public class SimpleSecurityContextProvider implements SecurityContextProvider {
}
/**
* Constructs a new <code>SimpleSecurityContextProvider</code> instance.
*
* If the given control context is null, the security context will be
* Construct a new <code>SimpleSecurityContextProvider</code> instance.
* <p>If the given control context is null, the security context will be
* retrieved on each call from the current thread.
*
* @param acc access control context (can be <code>null</code>)
* @see AccessController#getContext()
* @param acc
* access control context (can be null)
*/
public SimpleSecurityContextProvider(AccessControlContext acc) {
this.acc = acc;
}
public AccessControlContext getAccessControlContext() {
return (acc == null ? AccessController.getContext() : acc);
return (this.acc != null ? acc : AccessController.getContext());
}
}