You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by ma...@apache.org on 2010/02/16 10:50:19 UTC

svn commit: r910448 - in /incubator/aries/trunk/blueprint/blueprint-core/src: main/java/org/apache/aries/blueprint/ main/java/org/apache/aries/blueprint/container/ main/java/org/apache/aries/blueprint/ext/ main/java/org/apache/aries/blueprint/mutable/ ...

Author: mahrwald
Date: Tue Feb 16 09:50:18 2010
New Revision: 910448

URL: http://svn.apache.org/viewvc?rev=910448&view=rev
Log:
ARIES-88 Initial drop for field injection

Added:
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/FITestBean.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/FITestSubBean.java
Modified:
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/ExtendedBeanMetadata.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/RecipeBuilder.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/ext/ExtNamespaceHandler.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/mutable/MutableBeanMetadata.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/reflect/BeanMetadataImpl.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/resources/org/apache/aries/blueprint/ext/blueprint-ext.xsd
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/AbstractBlueprintTest.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/WiringTest.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/resources/test-wiring.xml

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/ExtendedBeanMetadata.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/ExtendedBeanMetadata.java?rev=910448&r1=910447&r2=910448&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/ExtendedBeanMetadata.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/ExtendedBeanMetadata.java Tue Feb 16 09:50:18 2010
@@ -41,4 +41,10 @@
     
     Class<?> getRuntimeClass();
 
+    /**
+     * Whether the bean allows properties to be injected directly into its fields in the case
+     * where an appropriate setter method is not available.
+     * @return Whether field injection is allowed
+     */
+    boolean getFieldInjection();
 }

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java?rev=910448&r1=910447&r2=910448&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java Tue Feb 16 09:50:18 2010
@@ -41,6 +41,7 @@
 import org.apache.aries.blueprint.proxy.AsmInterceptorWrapper;
 import org.apache.aries.blueprint.proxy.CgLibInterceptorWrapper;
 import org.apache.aries.blueprint.utils.ReflectionUtils;
+import org.apache.aries.blueprint.utils.ReflectionUtils.PropertyDescriptor;
 import org.osgi.service.blueprint.container.ComponentDefinitionException;
 import org.osgi.service.blueprint.container.ReifiedType;
 import org.osgi.service.blueprint.reflect.BeanMetadata;
@@ -70,12 +71,14 @@
     private List<Object> arguments;
     private List<String> argTypes;
     private boolean reorderArguments;
+    private final boolean allowsFieldInjection;
 
 
-    public BeanRecipe(String name, ExtendedBlueprintContainer blueprintContainer, Object type) {
+    public BeanRecipe(String name, ExtendedBlueprintContainer blueprintContainer, Object type, boolean allowsFieldInjection) {
         super(name);
         this.blueprintContainer = blueprintContainer;
         this.type = type;
+        this.allowsFieldInjection = allowsFieldInjection;
     }
 
     public Object getProperty(String name) {
@@ -744,10 +747,10 @@
     private void setProperty(Object instance, Class clazz, String propertyName, Object propertyValue) {
         String[] names = propertyName.split("\\.");
         for (int i = 0; i < names.length - 1; i++) {
-            Method getter = getPropertyDescriptor(clazz, names[i]).getGetter();
-            if (getter != null) {
+            PropertyDescriptor pd = getPropertyDescriptor(clazz, names[i]);
+            if (pd.allowsGet()) {
                 try {
-                    instance = invoke(getter, instance, (Object[]) null);
+                    instance = pd.get(instance, blueprintContainer.getAccessControlContext());
                 } catch (Exception e) {
                     throw new ComponentDefinitionException("Error getting property: " + names[i] + " on bean " + getName() + " when setting property " + propertyName + " on class " + clazz.getName(), getRealCause(e));
                 }
@@ -759,10 +762,10 @@
                 throw new ComponentDefinitionException("No getter for " + names[i] + " property on bean " + getName() + " when setting property " + propertyName + " on class " + clazz.getName());
             }
         }
-        Method setter = getPropertyDescriptor(clazz, names[names.length - 1]).getSetter();
-        if (setter != null) {
+        final PropertyDescriptor pd = getPropertyDescriptor(clazz, names[names.length - 1]);
+        if (pd.allowsSet()) {
             // convert the value to type of setter/field
-            Type type = setter.getGenericParameterTypes()[0];
+            Type type = pd.getGenericType();
             // Instanciate value
             if (propertyValue instanceof Recipe) {
                 propertyValue = ((Recipe) propertyValue).create();
@@ -775,21 +778,20 @@
                 throw new ComponentDefinitionException("Unable to convert property value" +
                         " from " + valueType +
                         " to " + memberType +
-                        " for injection " + setter, e);
+                        " for injection " + pd, e);
             }
             try {
-                // set value
-                invoke(setter, instance, propertyValue);
+                pd.set(instance, propertyValue, blueprintContainer.getAccessControlContext());
             } catch (Exception e) {
-                throw new ComponentDefinitionException("Error setting property: " + setter, getRealCause(e));
+                throw new ComponentDefinitionException("Error setting property: " + pd, getRealCause(e));
             }
         } else {
             throw new ComponentDefinitionException("No setter for " + names[names.length - 1] + " property");
         }
     }
 
-    private ReflectionUtils.PropertyDescriptor getPropertyDescriptor(Class clazz, String name) {
-        for (ReflectionUtils.PropertyDescriptor pd : ReflectionUtils.getPropertyDescriptors(clazz)) {
+    private ReflectionUtils.PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String name) {
+        for (ReflectionUtils.PropertyDescriptor pd : ReflectionUtils.getPropertyDescriptors(clazz, allowsFieldInjection)) {
             if (pd.getName().equals(name)) {
                 return pd;
             }

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/RecipeBuilder.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/RecipeBuilder.java?rev=910448&r1=910447&r2=910448&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/RecipeBuilder.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/RecipeBuilder.java Tue Feb 16 09:50:18 2010
@@ -210,11 +210,19 @@
         return beanMetadata.getClassName();        
     }
     
+    private boolean allowsFieldInjection(BeanMetadata beanMetadata) {
+        if (beanMetadata instanceof ExtendedBeanMetadata) {
+            return ((ExtendedBeanMetadata) beanMetadata).getFieldInjection();
+        }
+        return false;
+    }
+    
     private BeanRecipe createBeanRecipe(BeanMetadata beanMetadata) {
         BeanRecipe recipe = new BeanRecipe(
                 getName(beanMetadata.getId()),
                 blueprintContainer,
-                getBeanClass(beanMetadata));
+                getBeanClass(beanMetadata),
+                allowsFieldInjection(beanMetadata));
         // Create refs for explicit dependencies
         recipe.setExplicitDependencies(getDependencies(beanMetadata));
         recipe.setPrototype(MetadataUtil.isPrototypeScope(beanMetadata));
@@ -251,7 +259,7 @@
     }
 
     private Recipe createRecipe(RegistrationListener listener) {
-        BeanRecipe recipe = new BeanRecipe(getName(null), blueprintContainer, ServiceListener.class);
+        BeanRecipe recipe = new BeanRecipe(getName(null), blueprintContainer, ServiceListener.class, false);
         recipe.setProperty("listener", getValue(listener.getListenerComponent(), null));
         if (listener.getRegistrationMethod() != null) {
             recipe.setProperty("registerMethod", listener.getRegistrationMethod());
@@ -264,7 +272,7 @@
     }
 
     private Recipe createRecipe(ReferenceListener listener) {
-        BeanRecipe recipe = new BeanRecipe(getName(null), blueprintContainer, AbstractServiceReferenceRecipe.Listener.class);
+        BeanRecipe recipe = new BeanRecipe(getName(null), blueprintContainer, AbstractServiceReferenceRecipe.Listener.class, false);
         recipe.setProperty("listener", getValue(listener.getListenerComponent(), null));
         recipe.setProperty("metadata", listener);
         recipe.setProperty("blueprintContainer", blueprintContainer);

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/ext/ExtNamespaceHandler.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/ext/ExtNamespaceHandler.java?rev=910448&r1=910447&r2=910448&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/ext/ExtNamespaceHandler.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/ext/ExtNamespaceHandler.java Tue Feb 16 09:50:18 2010
@@ -25,14 +25,6 @@
 import java.util.List;
 import java.util.Set;
 
-import org.w3c.dom.Attr;
-import org.w3c.dom.CharacterData;
-import org.w3c.dom.Comment;
-import org.w3c.dom.Element;
-import org.w3c.dom.EntityReference;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
 import org.apache.aries.blueprint.ExtendedReferenceListMetadata;
 import org.apache.aries.blueprint.ParserContext;
 import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
@@ -50,12 +42,19 @@
 import org.osgi.service.blueprint.reflect.ComponentMetadata;
 import org.osgi.service.blueprint.reflect.IdRefMetadata;
 import org.osgi.service.blueprint.reflect.Metadata;
-import org.osgi.service.blueprint.reflect.ReferenceListMetadata;
 import org.osgi.service.blueprint.reflect.RefMetadata;
+import org.osgi.service.blueprint.reflect.ReferenceListMetadata;
 import org.osgi.service.blueprint.reflect.ServiceReferenceMetadata;
 import org.osgi.service.blueprint.reflect.ValueMetadata;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.CharacterData;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Element;
+import org.w3c.dom.EntityReference;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 /**
  * A namespace handler for Aries blueprint extensions
@@ -91,6 +90,8 @@
 
     public static final String ROLE_ATTRIBUTE = "role";
     public static final String ROLE_PROCESSOR = "processor";
+    
+    public static final String FIELD_INJECTION_ATTRIBUTE = "field-injection";
 
     private static final Logger LOGGER = LoggerFactory.getLogger(ExtNamespaceHandler.class);
 
@@ -120,10 +121,26 @@
             return decorateProxyMethod(node, component, context);
         } else if (node instanceof Attr && nodeNameEquals(node, ROLE_ATTRIBUTE)) {
             return decorateRole(node, component, context);
+        } else if (node instanceof Attr && nodeNameEquals(node, FIELD_INJECTION_ATTRIBUTE)) {
+            return decorateFieldInjection(node, component, context);
         } else {
             throw new ComponentDefinitionException("Unsupported node: " + node.getNodeName());
         }
     }
+    
+    private ComponentMetadata decorateFieldInjection(Node node, ComponentMetadata component, ParserContext context) {
+        if (!(component instanceof BeanMetadata)) {
+            throw new ComponentDefinitionException("Attribute " + node.getNodeName() + " can only be used on a <bean> element");
+        }
+        
+        if (!(component instanceof MutableBeanMetadata)) {
+            throw new ComponentDefinitionException("Expected an instanceof MutableBeanMetadata");
+        }
+        
+        String value = ((Attr) node).getValue();
+        ((MutableBeanMetadata) component).setFieldInjection("true".equals(value) || "1".equals(value));
+        return component;
+    }
 
     private ComponentMetadata decorateRole(Node node, ComponentMetadata component, ParserContext context) {
         if (!(component instanceof BeanMetadata)) {

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/mutable/MutableBeanMetadata.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/mutable/MutableBeanMetadata.java?rev=910448&r1=910447&r2=910448&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/mutable/MutableBeanMetadata.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/mutable/MutableBeanMetadata.java Tue Feb 16 09:50:18 2010
@@ -58,5 +58,7 @@
     void setRuntimeClass(Class runtimeClass);
 
     void setProcessor(boolean processor);
+    
+    void setFieldInjection(boolean allowFieldInjection);
 
 }

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/reflect/BeanMetadataImpl.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/reflect/BeanMetadataImpl.java?rev=910448&r1=910447&r2=910448&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/reflect/BeanMetadataImpl.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/reflect/BeanMetadataImpl.java Tue Feb 16 09:50:18 2010
@@ -48,8 +48,10 @@
     private String scope;
     private Class runtimeClass;
     private boolean processor;
-
+    private boolean fieldInjection;
+    
     public BeanMetadataImpl() {
+        this.fieldInjection = false;
     }
 
     public BeanMetadataImpl(BeanMetadata source) {
@@ -70,6 +72,9 @@
         this.dependsOn = new ArrayList<String>(source.getDependsOn());
         if (source instanceof ExtendedBeanMetadata) {
             this.runtimeClass = ((ExtendedBeanMetadata) source).getRuntimeClass();
+            this.fieldInjection = ((ExtendedBeanMetadata) source).getFieldInjection();
+        } else {
+            this.fieldInjection = false;
         }
     }
     
@@ -199,6 +204,14 @@
         this.processor = processor;
     }
 
+    public boolean getFieldInjection() {
+        return fieldInjection;
+    }
+    
+    public void setFieldInjection(boolean fieldInjection) {
+        this.fieldInjection = fieldInjection;
+    }
+    
     @Override
     public String toString() {
         return "BeanMetadata[" +
@@ -214,6 +227,7 @@
                 ", factoryComponent=" + factoryComponent +
                 ", scope='" + scope + '\'' +
                 ", runtimeClass=" + runtimeClass +
+                ", fieldInjection=" + fieldInjection + 
                 ']';
     }
 }

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java?rev=910448&r1=910447&r2=910448&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java Tue Feb 16 09:50:18 2010
@@ -19,15 +19,20 @@
 package org.apache.aries.blueprint.utils;
 
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
 import java.security.AccessControlContext;
 import java.security.AccessController;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -41,7 +46,7 @@
 public class ReflectionUtils {
 
     // TODO: MLK: PropertyDescriptor holds a reference to Method which holds a reference to the Class itself
-    private static Map<Class, PropertyDescriptor[]> beanInfos = Collections.synchronizedMap(new WeakHashMap<Class, PropertyDescriptor[]>());
+    private static Map<Class<?>, PropertyDescriptor[][]> beanInfos = Collections.synchronizedMap(new WeakHashMap<Class<?>, PropertyDescriptor[][]>());
 
     public static boolean hasDefaultConstructor(Class type) {
         if (!Modifier.isPublic(type.getModifiers())) {
@@ -114,74 +119,97 @@
         return methods;
     }
 
-    public static PropertyDescriptor[] getPropertyDescriptors(Class clazz) {
-        PropertyDescriptor[] properties = beanInfos.get(clazz);
+    public static PropertyDescriptor[] getPropertyDescriptors(Class clazz, boolean allowFieldInjection) {
+        PropertyDescriptor[][] properties = beanInfos.get(clazz);
+        int index = allowFieldInjection ? 0 : 1;
+        
         if (properties == null) {
-            List<PropertyDescriptor> props = new ArrayList<PropertyDescriptor>();
+            properties = new PropertyDescriptor[2][];
+            beanInfos.put(clazz, properties);
+        }
+        
+        if (properties[index] == null) {
+            Map<String,PropertyDescriptor> props = new HashMap<String, PropertyDescriptor>();
             for (Method method : clazz.getMethods()) {
                 if (Modifier.isStatic(method.getModifiers()) || method.isBridge()) {
                     continue;
                 }
                 String name = method.getName();
-                Class argTypes[] = method.getParameterTypes();
-                Class resultType = method.getReturnType();
+                Class<?> argTypes[] = method.getParameterTypes();
+                Class<?> resultType = method.getReturnType();
+                
+                Class<?> argType = resultType;
+                Method getter = null;
+                Method setter = null;
+                
                 if (name.length() > 3 && name.startsWith("set") && resultType == Void.TYPE && argTypes.length == 1) {
-                    props.add(new PropertyDescriptor(decapitalize(name.substring(3)), argTypes[0], null, method));
-
+                    name = decapitalize(name.substring(3));
+                    setter = method;
+                    argType = argTypes[0];
                 } else if (name.length() > 3 && name.startsWith("get") && argTypes.length == 0) {
-                    props.add(new PropertyDescriptor(decapitalize(name.substring(3)), resultType, method, null));
+                    name = decapitalize(name.substring(3));
+                    getter = method;
                 } else if (name.length() > 2 && name.startsWith("is") && argTypes.length == 0 && resultType == boolean.class) {
-                    props.add(new PropertyDescriptor(decapitalize(name.substring(2)), resultType, method, null));
+                    name = decapitalize(name.substring(2));
+                    getter = method;
+                } else {
+                    continue;
                 }
-            }
-            PropertyDescriptor[] pds = props.toArray(new PropertyDescriptor[props.size()]);
-            for (int i = 0; i < pds.length - 1; i++) {
-                boolean remove = false;
-                for (int j = i + 1; j < pds.length; j++) {
-                    if (pds[i] != null && pds[j] != null) {
-                        if (pds[i].name.equals(pds[j].name)) {
-                            if (remove || !pds[i].type.equals(pds[j].type)) {
-                                remove = true;
-                                pds[j] = null;
-                                continue;
-                            } else {
-                                if (pds[j].getter != null) {
-                                    if (pds[i].getter == null) {
-                                        pds[i].getter = pds[j].getter;
-                                    } else if (pds[i].getter != pds[j].getter) {
-                                        remove = true;
-                                        pds[j] = null;
-                                        continue;
-                                    }
-                                }
-                                if (pds[j].setter != null) {
-                                    if (pds[i].setter == null) {
-                                        pds[i].setter = pds[j].setter;
-                                    } else if (pds[i].setter != pds[j].setter) {
-                                        remove = true;
-                                        pds[j] = null;
-                                        continue;
-                                    }
-                                }
-                            }
+                
+                if (props.containsKey(name)) {
+                    PropertyDescriptor pd = props.get(name);
+                    if (pd != INVALID_PROPERTY) {
+                        if (!argType.equals(pd.type)) {
+                            props.put(name, INVALID_PROPERTY);
+                        } else if (getter != null) {
+                            if (pd.getter == null || pd.getter.equals(getter))
+                                pd.getter = getter;
+                            else
+                                props.put(name, INVALID_PROPERTY);
+                        } else if (setter != null) {
+                            if (pd.setter == null || pd.setter.equals(setter)) 
+                                pd.setter = setter;
+                            else
+                                props.put(name, INVALID_PROPERTY);
                         }
                     }
-                }
-                if (remove) {
-                    pds[i] = null;
+                } else {
+                    props.put(name, new PropertyDescriptor(name, argType, getter, setter));
                 }
             }
-            props.clear();
-            for (int i = 0; i < pds.length - 1; i++) {
-                if (pds[i] != null) {
-                    pds[i].type = null;
-                    props.add(pds[i]);
+            
+            if (allowFieldInjection) {
+                for (Field field : clazz.getDeclaredFields()) {
+                    if (Modifier.isStatic(field.getModifiers())) {
+                        continue;
+                    }
+                    
+                    String name = decapitalize(field.getName());
+                    if (!props.containsKey(name)) {
+                        props.put(name, new PropertyDescriptor(name, field.getType(), field));
+                    } else {
+                        PropertyDescriptor pd = props.get(name);
+                        if (pd != INVALID_PROPERTY) {
+                            if (pd.type.equals(field.getType())) {
+                                pd.field = field;
+                            } 
+                            // no else, we don't require field implementations to have the same
+                            // type as the getter and setter
+                        }
+                    }
                 }
             }
-            properties = props.toArray(new PropertyDescriptor[props.size()]);
-            beanInfos.put(clazz, properties);
+            
+            Iterator<PropertyDescriptor> it = props.values().iterator();
+            while (it.hasNext()) {
+                if (it.next() == INVALID_PROPERTY)
+                    it.remove();
+            }
+            
+            Collection<PropertyDescriptor> tmp = props.values();
+            properties[index] = tmp.toArray(new PropertyDescriptor[tmp.size()]); 
         }
-        return properties;
+        return properties[index];
     }
 
     private static String decapitalize(String name) {
@@ -245,29 +273,112 @@
         }
     }
     
+    private static final PropertyDescriptor INVALID_PROPERTY = new PropertyDescriptor(null, null, null, null);
+
     public static class PropertyDescriptor {
         private String name;
-        private Class type;
+        private Class<?> type;
         private Method getter;
         private Method setter;
+        private Field field;
 
-        public PropertyDescriptor(String name, Class type, Method getter, Method setter) {
+        public PropertyDescriptor(String name, Class<?> type, Method getter, Method setter) {
             this.name = name;
             this.type = type;
             this.getter = getter;
             this.setter = setter;
         }
+        
+        public PropertyDescriptor(String name, Class<?> type, Field field) {
+            this.name = name;
+            this.type = type;
+            this.field = field;
+            this.getter = null;
+            this.setter = null;
+        }
 
         public String getName() {
             return name;
         }
-
-        public Method getGetter() {
-            return getter;
+        
+        public boolean allowsGet() {
+            return getter != null || field != null;
+        }
+        
+        public boolean allowsSet() {
+            return setter != null || field != null;
+        }
+        
+        public Object get(final Object instance, AccessControlContext acc) throws Exception {            
+            if (acc == null) {
+                return internalGet(instance);
+            } else {
+                try {
+                    return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+                        public Object run() throws Exception {
+                            return internalGet(instance);
+                        }            
+                    }, acc);
+                } catch (PrivilegedActionException e) {
+                    throw e.getException();
+                }
+            }
+        }
+            
+        private Object internalGet(Object instance) 
+                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+            if (getter != null) {
+                return getter.invoke(instance);
+            } else if (field != null) {
+                field.setAccessible(true);
+                return field.get(instance);
+            } else {
+                throw new UnsupportedOperationException();
+            }
         }
 
-        public Method getSetter() {
-            return setter;
+        public void set(final Object instance, final Object value, AccessControlContext acc) throws Exception {
+            if (acc == null) {
+                internalSet(instance, value);
+            } else {
+                try {
+                    AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+                        public Object run() throws Exception {
+                            internalSet(instance, value);
+                            return null;
+                        }            
+                    }, acc);
+                } catch (PrivilegedActionException e) {
+                    throw e.getException();
+                }
+            }            
+        }
+        
+        private void internalSet(Object instance, Object value) 
+                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+            if (setter != null) {
+                setter.invoke(instance, value);
+            } else if (field != null) {
+                field.setAccessible(true);
+                field.set(instance, value);
+            } else {
+                throw new UnsupportedOperationException();
+            }
+        }
+        
+        public Type getGenericType() {
+            if (setter != null)
+                return setter.getGenericParameterTypes()[0];
+            else if (getter != null)
+                return getter.getGenericReturnType();
+            else 
+                return field.getGenericType();
+                
+        }
+        
+        public String toString() {
+            return "PropertyDescriptor <name: "+name+", getter: "+getter+", setter: "+setter+
+                ", field: "+field+">";
         }
     }
 

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/resources/org/apache/aries/blueprint/ext/blueprint-ext.xsd
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/resources/org/apache/aries/blueprint/ext/blueprint-ext.xsd?rev=910448&r1=910447&r2=910448&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/resources/org/apache/aries/blueprint/ext/blueprint-ext.xsd (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/resources/org/apache/aries/blueprint/ext/blueprint-ext.xsd Tue Feb 16 09:50:18 2010
@@ -114,5 +114,7 @@
             </xsd:restriction>
         </xsd:simpleType>
     </xsd:attribute>
+    
+    <xsd:attribute name="field-injection" type="xsd:boolean" default="false" />
 
 </xsd:schema>

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/AbstractBlueprintTest.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/AbstractBlueprintTest.java?rev=910448&r1=910447&r2=910448&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/AbstractBlueprintTest.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/AbstractBlueprintTest.java Tue Feb 16 09:50:18 2010
@@ -29,18 +29,24 @@
 
 import org.apache.aries.blueprint.container.NamespaceHandlerRegistry;
 import org.apache.aries.blueprint.container.Parser;
+import org.apache.aries.blueprint.ext.ExtNamespaceHandler;
 import org.apache.aries.blueprint.namespace.ComponentDefinitionRegistryImpl;
 import org.xml.sax.SAXException;
 
 public abstract class AbstractBlueprintTest extends TestCase {
 
     protected ComponentDefinitionRegistryImpl parse(String name) throws Exception {
-        NamespaceHandlerRegistry.NamespaceHandlerSet handlers = new NamespaceHandlerRegistry.NamespaceHandlerSet() {
+      final URI extensionHandler = new URI("http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0");
+      NamespaceHandlerRegistry.NamespaceHandlerSet handlers = new NamespaceHandlerRegistry.NamespaceHandlerSet() {
             public Set<URI> getNamespaces() {
                 return null;
             }
             public NamespaceHandler getNamespaceHandler(URI namespace) {
-                return null;
+                if (namespace.equals(extensionHandler)) {
+                  return new ExtNamespaceHandler();
+                } else {
+                  return null;
+                }
             }
             public void removeListener(NamespaceHandlerRegistry.Listener listener) {
             }

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/WiringTest.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/WiringTest.java?rev=910448&r1=910447&r2=910448&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/WiringTest.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/WiringTest.java Tue Feb 16 09:50:18 2010
@@ -28,6 +28,8 @@
 import java.util.Map;
 import java.util.Set;
 
+import junit.framework.Assert;
+
 import org.apache.aries.blueprint.CallbackTracker.Callback;
 import org.apache.aries.blueprint.container.BlueprintRepository;
 import org.apache.aries.blueprint.di.CircularDependencyException;
@@ -35,6 +37,7 @@
 import org.apache.aries.blueprint.namespace.ComponentDefinitionRegistryImpl;
 import org.apache.aries.blueprint.pojos.BeanD;
 import org.apache.aries.blueprint.pojos.BeanF;
+import org.apache.aries.blueprint.pojos.FITestBean;
 import org.apache.aries.blueprint.pojos.Multiple;
 import org.apache.aries.blueprint.pojos.PojoA;
 import org.apache.aries.blueprint.pojos.PojoB;
@@ -135,6 +138,35 @@
         assertEquals(true, pojob.getDestroyCalled());
     }
     
+    public void testFieldInjection() throws Exception {
+      ComponentDefinitionRegistryImpl registry = parse("/test-wiring.xml");
+      Repository repository = new TestBlueprintContainer(registry).getRepository();
+      
+      Object fiTestBean = repository.create("FITestBean");
+      assertNotNull(fiTestBean);
+      assertTrue(fiTestBean instanceof FITestBean);
+      
+      FITestBean bean = (FITestBean) fiTestBean;
+      // single field injection
+      assertEquals("value", bean.getAttr());
+      // prefer setter injection to field injection
+      assertEquals("IS_LOWER", bean.getUpperCaseAttr());
+      // support cascaded injection 'bean.name' via fields
+      assertEquals("aName", bean.getBeanName());
+      
+      // fail if field-injection is not specified
+      try {
+          repository.create("FIFailureTestBean");
+          Assert.fail("Expected exception");
+      } catch (ComponentDefinitionException cde) {}
+      
+      // fail if field-injection is false
+      try {
+          repository.create("FIFailureTest2Bean");
+          Assert.fail("Expected exception");
+      } catch (ComponentDefinitionException cde) {}
+    }
+    
     public void testCompoundProperties() throws Exception {
         ComponentDefinitionRegistryImpl registry = parse("/test-wiring.xml");
         Repository repository = new TestBlueprintContainer(registry).getRepository();

Added: incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/FITestBean.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/FITestBean.java?rev=910448&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/FITestBean.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/FITestBean.java Tue Feb 16 09:50:18 2010
@@ -0,0 +1,31 @@
+/**
+ * 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.
+ */
+package org.apache.aries.blueprint.pojos;
+
+public class FITestBean {
+  private String attr;
+  private String upperCaseAttr;
+  private FITestSubBean bean = new FITestSubBean();
+  
+  public String getAttr() { return attr; }
+  
+  public String getUpperCaseAttr() { return upperCaseAttr; }
+  public void setUpperCaseAttr(String val) { upperCaseAttr = val.toUpperCase(); }
+  public String getBeanName() { return bean.getName(); }
+}

Added: incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/FITestSubBean.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/FITestSubBean.java?rev=910448&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/FITestSubBean.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/FITestSubBean.java Tue Feb 16 09:50:18 2010
@@ -0,0 +1,27 @@
+/**
+ * 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.
+ */
+package org.apache.aries.blueprint.pojos;
+
+public class FITestSubBean {
+    private String name;
+    
+    public String getName() {
+        return name;
+    }
+}

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/test/resources/test-wiring.xml
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/test/resources/test-wiring.xml?rev=910448&r1=910447&r2=910448&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/test/resources/test-wiring.xml (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/test/resources/test-wiring.xml Tue Feb 16 09:50:18 2010
@@ -16,6 +16,7 @@
     limitations under the License.
 -->
 <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+           xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0"
            default-availability="mandatory" >
 
     <type-converters>
@@ -116,5 +117,19 @@
           <idref component-id="pojoA"/>
         </property>
     </bean>
+    
+    <bean id="FITestBean" ext:field-injection="true" class="org.apache.aries.blueprint.pojos.FITestBean">
+        <property name="attr" value="value" />
+        <property name="upperCaseAttr" value="is_lower" />
+        <property name="bean.name" value="aName" />
+    </bean>
+
+    <bean id="FIFailureTestBean" class="org.apache.aries.blueprint.pojos.FITestBean">
+        <property name="attr" value="value" />
+    </bean>
+    
+    <bean id="FIFailureTest2Bean" class="org.apache.aries.blueprint.pojos.FITestBean" ext:field-injection="false">
+        <property name="attr" value="value" />
+    </bean>
 
 </blueprint>
\ No newline at end of file