[maven-release-plugin]  copy for tag blueprint-0.2-incubating

git-svn-id: https://svn.apache.org/repos/asf/incubator/aries/tags/blueprint-0.2-incubating@990140 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ServiceRecipe.java b/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ServiceRecipe.java
index dddd5c7..ba1995f 100644
--- a/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ServiceRecipe.java
+++ b/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ServiceRecipe.java
@@ -25,6 +25,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.aries.blueprint.BlueprintConstants;
 import org.apache.aries.blueprint.ExtendedBlueprintContainer;
@@ -65,7 +66,7 @@
 
     private Map properties;
     private final AtomicBoolean registered = new AtomicBoolean();
-    private volatile ServiceRegistration registration;
+    private final AtomicReference<ServiceRegistration> registration = new AtomicReference<ServiceRegistration>();
     private Map registrationProperties;
     private List<ServiceListener> listeners;
     private volatile Object service;
@@ -162,7 +163,7 @@
             LOGGER.debug("Registering service {} with interfaces {} and properties {}",
                          new Object[] { name, classes, props });
 
-            registration = blueprintContainer.registerService(classArray, new TriggerServiceFactory(), props);            
+            registration.set(blueprintContainer.registerService(classArray, new TriggerServiceFactory(), props));            
         }
     }
 
@@ -171,7 +172,7 @@
             LOGGER.debug("Unregistering service {}", name);
             // This method needs to allow reentrance, so if we need to make sure the registration is
             // set to null before actually unregistering the service
-            ServiceRegistration reg = registration;
+            ServiceRegistration reg = registration.get();
             if (listeners != null) {
                 LOGGER.debug("Calling listeners for service unregistration");
                 for (ServiceListener listener : listeners) {
@@ -181,26 +182,26 @@
             if (reg != null) {
                 reg.unregister();
             }
-            // We need to do this hack in order to support reantrancy
-            if (registration == reg) {
-                registration = null;
-            }
+            
+            registration.compareAndSet(reg, null);
         }
     }
 
     protected ServiceReference getReference() {
-        if (registration == null) {
+    	ServiceRegistration reg = registration.get();
+        if (reg == null) {
             throw new IllegalStateException("Service is not registered");
         } else {
-            return registration.getReference();
+            return reg.getReference();
         }
     }
 
     protected void setProperties(Dictionary props) {
-        if (registration == null) {
+    	ServiceRegistration reg = registration.get();
+        if (reg == null) {
             throw new IllegalStateException("Service is not registered");
         } else {
-            registration.setProperties(props);
+            reg.setProperties(props);
             // TODO: set serviceProperties? convert somehow? should listeners be notified of this?
         }
     }
@@ -230,24 +231,26 @@
                 }
             }
         }
+        
         Object service = this.service;
         // We need the real service ...
         if (bundle != null) {
-            if (service instanceof ServiceFactory) {
-                service = ((ServiceFactory) service).getService(bundle, registration);
-            }
-            if (service == null) {
-                throw new IllegalStateException("service is null");
-            }
-            // Check if the service actually implement all the requested interfaces
-            validateClasses(service);
-        // We're not really interested in the service, but perform some sanity checks nonetheless
+        	if (service instanceof ServiceFactory) {
+        		service = ((ServiceFactory) service).getService(bundle, registration);
+        	}
+        	if (service == null) {
+        		throw new IllegalStateException("service is null");
+        	}
+        	// Check if the service actually implement all the requested interfaces
+        	validateClasses(service);
+        	// We're not really interested in the service, but perform some sanity checks nonetheless
         } else {
-             if (!(service instanceof ServiceFactory)) {
-                 // Check if the service actually implement all the requested interfaces
-                 validateClasses(service);
-             }
+        	if (!(service instanceof ServiceFactory)) {
+        		// Check if the service actually implement all the requested interfaces
+        		validateClasses(service);
+        	}
         }
+        
         return service;
     }
 
@@ -297,18 +300,16 @@
         }
     }
 
-    public synchronized Object getService(Bundle bundle, ServiceRegistration registration) {
+    public Object getService(Bundle bundle, ServiceRegistration registration) {
         /** getService() can get called before registerService() returns with the registration object.
          *  So we need to set the registration object in case registration listeners call 
          *  getServiceReference(). 
          */
-        if (this.registration == null) {
-            this.registration = registration;
-        }
+    	this.registration.compareAndSet(null, registration);
         return internalGetService(bundle, registration);
     }
 
-    public synchronized void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+    public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
         if (this.service instanceof ServiceFactory) {
             ((ServiceFactory) this.service).ungetService(bundle, registration, service);
         }
diff --git a/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java b/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java
index 050d444..96a0d73 100644
--- a/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java
+++ b/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java
@@ -431,16 +431,28 @@
             return result;
         }
         
-        private Method findMethodByClass(Class<?> arg) throws ComponentDefinitionException {
+        private Method findMethodByClass(Class<?> arg)
+                throws ComponentDefinitionException {
             Method result = null;
+
+            if (!hasSameTypeSetter()) {
+                throw new ComponentDefinitionException(
+                        "At least one Setter method has to match the type of the Getter method for property "
+                                + getName());
+            }
+
+            if (setters.size() == 1) {
+                return setters.iterator().next();
+            }
             
             for (Method m : setters) {
                 Class<?> paramType = m.getParameterTypes()[0];
-                
-                if ((arg == null && Object.class.isAssignableFrom(paramType)) 
+
+                if ((arg == null && Object.class.isAssignableFrom(paramType))
                         || (arg != null && paramType.isAssignableFrom(arg))) {
-                    
-                    // pick the method that has the more specific parameter if any
+
+                    // pick the method that has the more specific parameter if
+                    // any
                     if (result != null) {
                         Class<?> oldParamType = result.getParameterTypes()[0];
                         if (paramType.isAssignableFrom(oldParamType)) {
@@ -449,18 +461,35 @@
                             result = m;
                         } else {
                             throw new ComponentDefinitionException(
-                                    "Ambiguous setter method for property "+getName()+
-                                    ". More than one method matches the  parameter type "+arg);
+                                    "Ambiguous setter method for property "
+                                            + getName()
+                                            + ". More than one method matches the  parameter type "
+                                            + arg);
                         }
                     } else {
                         result = m;
                     }
                 }
-            }            
-            
+            }
+
             return result;
         }
         
+        // ensure there is a setter that matches the type of the getter
+        private boolean hasSameTypeSetter() {
+            if (getter == null) {
+                return true;
+            }
+            Iterator<Method> it = setters.iterator();
+            while (it.hasNext()) {
+                Method m = it.next();
+                if (m.getParameterTypes()[0].equals(getter.getReturnType())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         private Method findMethodWithConversion(Object value) throws ComponentDefinitionException {
             ExecutionContext ctx = ExecutionContext.Holder.getContext();
             List<Method> matchingMethods = new ArrayList<Method>();
diff --git a/blueprint-itests/src/test/java/org/apache/aries/blueprint/itests/BlueprintContainerTest.java b/blueprint-itests/src/test/java/org/apache/aries/blueprint/itests/BlueprintContainerTest.java
index 740e9aa..d08bcfa 100644
--- a/blueprint-itests/src/test/java/org/apache/aries/blueprint/itests/BlueprintContainerTest.java
+++ b/blueprint-itests/src/test/java/org/apache/aries/blueprint/itests/BlueprintContainerTest.java
@@ -31,6 +31,7 @@
 import java.text.SimpleDateFormat;
 import java.util.Currency;
 import java.util.Hashtable;
+import java.util.HashSet;
 
 import org.apache.aries.blueprint.sample.Bar;
 import org.apache.aries.blueprint.sample.Foo;
@@ -63,6 +64,20 @@
         // do the test
         testBlueprintContainer(bundle);
     }
+    
+    @Test
+    public void testDeadlock() throws Exception {
+      bundleContext.registerService("java.util.Set",new HashSet<Object>(), null);
+      
+      Bundle bundle = getInstalledBundle("org.apache.aries.blueprint.sample");
+      assertNotNull(bundle);
+
+      bundle.start();
+      
+      getBlueprintContainerForBundle(bundleContext, "org.apache.aries.blueprint.sample",5000);
+      
+      // no actual assertions, we just don't want to deadlock
+    }
 
     @org.ops4j.pax.exam.junit.Configuration
     public static Option[] configuration() {
diff --git a/blueprint-sample/src/main/java/org/apache/aries/blueprint/sample/DodgyListener.java b/blueprint-sample/src/main/java/org/apache/aries/blueprint/sample/DodgyListener.java
new file mode 100644
index 0000000..03aa59b
--- /dev/null
+++ b/blueprint-sample/src/main/java/org/apache/aries/blueprint/sample/DodgyListener.java
@@ -0,0 +1,54 @@
+/**
+ *  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.sample;
+
+import java.util.Set;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+public class DodgyListener {
+  private BundleContext ctx;
+  
+  public void setBundleContext(BundleContext ctx) {
+    this.ctx = ctx;
+  }
+  
+  public void bind(Set a, Map props) {
+    System.out.println("Attempting to provoke deadlock");
+    
+    Thread t = new Thread() {
+      public void run() {
+    	// we pretend to be another bundle (otherwise we'll deadlock in Equinox itself :(
+    	BundleContext otherCtx = ctx.getBundle(0).getBundleContext();  
+    	  
+        ServiceReference ref = otherCtx.getServiceReference("java.util.List");
+        otherCtx.getService(ref);
+      }
+    };
+    t.start();
+    
+    // let the other thread go first
+    try {
+      Thread.sleep(100);
+    } catch (Exception e) {}
+    
+    ServiceReference ref = ctx.getServiceReference("java.util.List");
+    ctx.getService(ref);
+  }
+}
\ No newline at end of file
diff --git a/blueprint-sample/src/main/resources/OSGI-INF/blueprint/config.xml b/blueprint-sample/src/main/resources/OSGI-INF/blueprint/config.xml
index 2e71f68..6552812 100644
--- a/blueprint-sample/src/main/resources/OSGI-INF/blueprint/config.xml
+++ b/blueprint-sample/src/main/resources/OSGI-INF/blueprint/config.xml
@@ -118,8 +118,38 @@
 		<reference-listener bind-method="bind"
 			unbind-method="unbind" ref="listBindingListener" />
 	</reference-list>
-
+	
 	<bean id="listBindingListener" class="org.apache.aries.blueprint.sample.BindingListener" />
+	
+	
+	
+	<!-- Deadlock setup 
+	  We have a lazily activated service (i.e. register the service but don't create it yet)
+	  Then we have a reference-listener (or some other piece of client code that is called while instantiating blueprint components)
+	  
+	  This then tries to obtain the initial service twice:
+	  - first from a new thread
+	  - second from the current thread (that is creating blueprint components)
+	  
+	  This is of course contrived. However, any other piece of code may be substituted for the first thread and with unlucky
+	  timing we get into the same deadlock situation.
+	-->
+	
+	<service interface="java.util.List" activation="lazy">
+	  <bean class="java.util.ArrayList" />
+	</service>
+	
+	<bean id="dodgyListener" class="org.apache.aries.blueprint.sample.DodgyListener">
+	  <property name="bundleContext" ref="blueprintBundleContext" />
+	</bean>
+	
+	<reference-list interface="java.util.Set" availability="optional">
+	  <reference-listener bind-method="bind" ref="dodgyListener" />
+	</reference-list>
+
+
+
+
 
 	<bean id="circularReference" class="org.apache.aries.blueprint.sample.BindingListener"
 		init-method="init">