You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pd...@apache.org on 2015/11/04 21:55:46 UTC
svn commit: r1712646 - in /felix/sandbox/pderop/dependencymanager.ds:
org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/
org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/
Author: pderop
Date: Wed Nov 4 20:55:46 2015
New Revision: 1712646
URL: http://svn.apache.org/viewvc?rev=1712646&view=rev
Log:
Keep the code up-to-date with the felix-trunk version.
Added:
felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX5045_OptionalDependencyCBCalledBeforeStartTest.java
felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX5054_CleanupInstanceBoundDependenciesInDestroy.java
Modified:
felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java
felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java
Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX5045_OptionalDependencyCBCalledBeforeStartTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX5045_OptionalDependencyCBCalledBeforeStartTest.java?rev=1712646&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX5045_OptionalDependencyCBCalledBeforeStartTest.java (added)
+++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX5045_OptionalDependencyCBCalledBeforeStartTest.java Wed Nov 4 20:55:46 2015
@@ -0,0 +1,117 @@
+/*
+ * 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.felix.dm.itest.api;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * This test validates a corner case:
+ *
+ * We have the following component players: A, BFactory, B.
+ *
+ * component A defines from A.init() a required dependency on BFactory, and an optional dependency on B.
+ * component A has a "start" lifecycle callback.
+ *
+ * when A.bind(BFactory factory) is called, the factory.create() method is then invoked, which triggers a registration of the B Service.
+ * At this point B is available, then A.start() should be called before A.bind(B).
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX5045_OptionalDependencyCBCalledBeforeStartTest extends TestBase {
+ public void test_A_DependsOnBFactoryFromInit() throws Throwable {
+ final DependencyManager m = getDM();
+ Ensure e = new Ensure();
+
+ Component bFactory = m.createComponent().setImplementation(new BFactory()).setInterface(BFactory.class.getName(), null);
+ Component a = m.createComponent().setImplementation(new A(e));
+
+ // Enable first bFactory.
+ m.add(bFactory);
+
+ // Then enable A.
+ m.add(a);
+
+ // A should get BFactory, then it should instantiate B, then A.start() should be called, then A.bind(B) should be called.
+ e.waitForStep(4, 5000);
+
+ // Now, remove BFactory. A.unbind(B b) should be called, then
+ m.remove(bFactory);
+ e.waitForStep(6, 5000);
+ e.ensure();
+ }
+
+ public static class A {
+ final Ensure m_e;
+
+ public A(Ensure e) {
+ m_e = e;
+ }
+
+ void init(Component component) {
+ m_e.step(1);
+ DependencyManager dm = component.getDependencyManager();
+ Dependency depBFactory = dm.createServiceDependency().setService(BFactory.class).setRequired(true).setCallbacks("bind", "unbind");
+ Dependency depB = dm.createServiceDependency().setService(B.class).setRequired(false).setCallbacks("bind", "unbind");
+ component.add(depBFactory, depB);
+ }
+
+ void bind(BFactory bFactory) {
+ m_e.step(2);
+ bFactory.createB();
+ }
+
+ void start() {
+ m_e.step(3);
+ }
+
+ void bind(B b) {
+ m_e.step(4);
+ }
+
+ void unbind(B b) {
+ m_e.step(5);
+ }
+
+ void unbind(BFactory bFactory) {
+ m_e.step(6);
+ }
+ }
+
+ public static class BFactory {
+ volatile BundleContext m_bctx;
+ ServiceRegistration m_registraiton;
+
+ void createB() {
+ m_registraiton = m_bctx.registerService(B.class.getName(), new B(), null);
+ }
+
+ void deleteB() {
+ m_registraiton.unregister();
+ }
+ }
+
+ public static class B {
+ }
+}
Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX5054_CleanupInstanceBoundDependenciesInDestroy.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX5054_CleanupInstanceBoundDependenciesInDestroy.java?rev=1712646&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX5054_CleanupInstanceBoundDependenciesInDestroy.java (added)
+++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/FELIX5054_CleanupInstanceBoundDependenciesInDestroy.java Wed Nov 4 20:55:46 2015
@@ -0,0 +1,87 @@
+package org.apache.felix.dm.itest.api;
+
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FELIX5054_CleanupInstanceBoundDependenciesInDestroy extends TestBase {
+ Ensure m_ensure;
+
+ public void testInstanceBoundDependencyNotReAddedTwice() {
+ DependencyManager m = getDM();
+ m_ensure = new Ensure();
+
+ Component a = m.createComponent()
+ .setImplementation(new A())
+ .add(m.createServiceDependency().setService(B.class).setRequired(true).setCallbacks("bindB", "unbindB"));
+
+ Component b = m.createComponent()
+ .setImplementation(new B())
+ .setInterface(B.class.getName(), null);
+
+ Component c = m.createComponent()
+ .setImplementation(new C())
+ .setInterface(C.class.getName(), null);
+
+ m.add(b);
+ m.add(a);
+ m.add(c);
+
+ m.remove(b);
+ m_ensure.waitForStep(5, 3000);
+ m.add(b);
+ m_ensure.waitForStep(8, 3000);
+ }
+
+ public class A {
+ private Ensure.Steps m_stepsBindB = new Ensure.Steps(1, 6);
+ private Ensure.Steps m_stepsUnbindB = new Ensure.Steps(5);
+ private Ensure.Steps m_stepsBindC = new Ensure.Steps(3, 8);
+ private Ensure.Steps m_stepsUnbindC = new Ensure.Steps(4);
+ private Ensure.Steps m_stepsInit = new Ensure.Steps(2, 7);
+ private Dependency m_depC;
+
+ void bindB(B b) {
+ m_ensure.steps(m_stepsBindB);
+ }
+
+ void unbindB(B b) {
+ m_ensure.steps(m_stepsUnbindB);
+ }
+
+ void init(Component component) {
+ DependencyManager dm = component.getDependencyManager();
+ m_depC = dm.createServiceDependency().setService(C.class).setRequired(true).setCallbacks("bindC", "unbindC");
+ m_ensure.steps(m_stepsInit);
+ component.add(m_depC);
+ }
+
+ void bindC(C c) {
+ m_ensure.steps(m_stepsBindC);
+ }
+
+ void unbindC(C c) {
+ m_ensure.steps(m_stepsUnbindC);
+ }
+ }
+
+ public static class B {
+
+ }
+
+ public static class C {
+
+ }
+}
Modified: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java?rev=1712646&r1=1712645&r2=1712646&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java (original)
+++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.itest/src/org/apache/felix/dm/itest/api/ServiceRaceTest.java Wed Nov 4 20:55:46 2015
@@ -207,10 +207,12 @@ public class ServiceRaceTest extends Tes
});
// Remove configuration (asynchronously)
+ final Ensure stepConfDeleted = new Ensure(false);
schedule(new Runnable() {
public void run() {
try {
conf.delete();
+ stepConfDeleted.step(1);
}
catch (IOException e) {
warn("error while unconfiguring", e);
@@ -222,6 +224,8 @@ public class ServiceRaceTest extends Tes
expectedStep += 2; // stop/destroy
expectedStep += DEPENDENCIES; // removed all dependencies
step.waitForStep(expectedStep, STEP_WAIT);
+ // Make sure configuration is removed
+ stepConfDeleted.waitForStep(1, STEP_WAIT);
step.ensure();
Assert.assertEquals(0, clientImpl.getDependencies());
Modified: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java?rev=1712646&r1=1712645&r2=1712646&view=diff
==============================================================================
--- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java (original)
+++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ComponentImpl.java Wed Nov 4 20:55:46 2015
@@ -269,6 +269,12 @@ public class ComponentImpl implements Co
* @see org.apache.felix.dm.itest.api.FELIX4913_OptionalCallbackInvokedTwiceTest which reproduces the use case.
*/
private final Map<Event, Event> m_invokeCallbackCache = new IdentityHashMap<>();
+
+ /**
+ * Flag used to check if the start callback has been invoked.
+ * We use this flag to ensure that we only inject optional dependencies after the start callback has been called.
+ */
+ private boolean m_startCalled;
/**
* Default component declaration implementation.
@@ -974,7 +980,7 @@ public class ComponentImpl implements Co
if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.TRACKING_OPTIONAL) {
invokeAutoConfigInstanceBoundDependencies();
invokeAddRequiredInstanceBoundDependencies();
- invoke(m_callbackStart);
+ invokeStart();
invokeAddOptionalDependencies();
registerService();
notifyListeners(newState);
@@ -983,13 +989,14 @@ public class ComponentImpl implements Co
if (oldState == ComponentState.TRACKING_OPTIONAL && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
unregisterService();
invokeRemoveOptionalDependencies();
- invoke(m_callbackStop);
+ invokeStop();
invokeRemoveInstanceBoundDependencies();
notifyListeners(newState);
return true;
}
if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.WAITING_FOR_REQUIRED) {
invoke(m_callbackDestroy);
+ removeInstanceBoundDependencies();
invokeRemoveRequiredDependencies();
notifyListeners(newState);
if (! someDependenciesNeedInstance()) {
@@ -1006,7 +1013,17 @@ public class ComponentImpl implements Co
return false;
}
- /**
+ private void invokeStart() {
+ invoke(m_callbackStart);
+ m_startCalled = true;
+ }
+
+ private void invokeStop() {
+ invoke(m_callbackStop);
+ m_startCalled = false;
+ }
+
+ /**
* Sets the m_handlingChange flag that indicates if the state machine is currently running the handleChange method.
*/
private void handlingChange(boolean transiting) {
@@ -1408,14 +1425,32 @@ public class ComponentImpl implements Co
/**
* This method ensures that a dependency callback is invoked only one time;
+ * It also ensures that if the dependency callback is optional, then we only
+ * invoke the bind method if the component start callback has already been called.
*/
private void invokeCallbackSafe(DependencyContext dc, EventType type, Event event) {
- if (m_invokeCallbackCache.put(event, event) == null) {
- dc.invokeCallback(type, event);
- }
+ if (! dc.isRequired() && ! m_startCalled) {
+ return;
+ }
+ if (m_invokeCallbackCache.put(event, event) == null) {
+ dc.invokeCallback(type, event);
+ }
}
/**
+ * Removes and closes all instance bound dependencies.
+ * This method is called when a component is destroyed.
+ */
+ private void removeInstanceBoundDependencies() {
+ for (DependencyContext dep : m_dependencies) {
+ if (dep.isInstanceBound()) {
+ m_dependencies.remove(dep);
+ dep.stop();
+ }
+ }
+ }
+
+ /**
* Clears the cache of invoked components callbacks.
* We only clear the cache when the state machine is not running.
* The cache is used to avoid calling the same bind callback twice.