[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">