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.