FELIX-3729 use global ordering and event range to exclude too early/too late events for a given component
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1424313 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
index 797509f..3ad3f58 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
@@ -24,12 +24,12 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
-import java.util.TreeSet;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -80,6 +80,40 @@
private boolean registered;
+ private final Map<S, EdgeInfo> edgeInfoMap = new IdentityHashMap<S, EdgeInfo>( );
+
+ private static class EdgeInfo
+ {
+ private int open = -1;
+ private int close = -1;
+ private CountDownLatch latch;
+
+ public void setClose( int close )
+ {
+ this.close = close;
+ }
+
+ public CountDownLatch getLatch()
+ {
+ return latch;
+ }
+
+ public void setLatch( CountDownLatch latch )
+ {
+ this.latch = latch;
+ }
+
+ public void setOpen( int open )
+ {
+ this.open = open;
+ }
+
+ public boolean outOfRange( int trackingCount )
+ {
+ return (open != -1 && trackingCount < open)
+ || (close != -1 && trackingCount > close);
+ }
+ }
/**
* Constructor that receives several parameters.
@@ -663,10 +697,12 @@
if ( isOptional() || nextRefPair != null)
{
- m_componentManager.invokeUnbindMethod( DependencyManager.this, refPair, trackingCount );
- closeRefPair();
- this.refPair = nextRefPair;
+ RefPair<T> oldRefPair = this.refPair;
+ this.refPair = null;
this.trackingCount = trackingCount;
+ m_componentManager.invokeUnbindMethod( DependencyManager.this, oldRefPair, trackingCount );
+ ungetService( oldRefPair );
+ this.refPair = nextRefPair;
tracked( trackingCount );
}
else //required and no replacement service, deactivate
@@ -1193,26 +1229,8 @@
return true;
}
-
- boolean open( S componentInstance )
- {
- return bind( componentInstance );
- }
-
-
- /**
- * Revoke all bindings. This method cannot throw an exception since it must
- * try to complete all that it can
- * @param componentInstance
- */
- void close( S componentInstance )
- {
- unbind( componentInstance );
- }
-
boolean prebind()
{
-
return customizerRef.get().open();
}
@@ -1223,7 +1241,7 @@
* @return true if the dependency is satisfied and at least the minimum
* number of services could be bound. Otherwise false is returned.
*/
- private boolean bind( S componentInstance )
+ boolean open( S componentInstance )
{
// If no references were received, we have to check if the dependency
// is optional, if it is not then the dependency is invalid
@@ -1248,7 +1266,13 @@
// flag being set in the loop below
boolean success = m_dependencyMetadata.isOptional();
AtomicInteger trackingCount = new AtomicInteger( );
- Collection<RefPair<T>> refs = customizerRef.get().getRefs( trackingCount );
+ Collection<RefPair<T>> refs;
+ synchronized ( trackerRef.get().tracked() )
+ {
+ refs = customizerRef.get().getRefs( trackingCount );
+ EdgeInfo info = getEdgeInfo( componentInstance );
+ info.setOpen( trackingCount.get() );
+ }
m_componentManager.log( LogService.LOG_DEBUG,
"For dependency {0}, optional: {1}; to bind: {2}",
new Object[]{ m_dependencyMetadata.getName(), success, refs }, null );
@@ -1256,7 +1280,7 @@
{
if ( !refPair.isFailed() )
{
- if ( !invokeBindMethod( componentInstance, refPair ) )
+ if ( !invokeBindMethod( componentInstance, refPair, trackingCount.get() ) )
{
m_componentManager.log( LogService.LOG_DEBUG,
"For dependency {0}, failed to invoke bind method on object {1}",
@@ -1270,50 +1294,55 @@
}
- /**
- * Handles an update in the service reference properties of a bound service.
- * <p>
- * This just calls the {@link #invokeUpdatedMethod(S, RefPair<T>)}
- * method if the method has been configured in the component metadata. If
- * the method is not configured, this method does nothing.
- *
- * @param componentInstance
- * @param refPair The <code>ServiceReference</code> representing the updated
- */
- void update( S componentInstance, final RefPair<T> refPair )
+ private EdgeInfo getEdgeInfo( S componentInstance )
{
- if ( m_dependencyMetadata.getUpdated() != null )
+ EdgeInfo info = edgeInfoMap.get( componentInstance );
+ if ( info == null )
{
- invokeUpdatedMethod( componentInstance, refPair );
+ info = new EdgeInfo();
+ edgeInfoMap.put( componentInstance, info );
}
+ return info;
}
-
-
/**
* Revoke the given bindings. This method cannot throw an exception since
* it must try to complete all that it can
*/
- private void unbind( S componentInstance )
+ void close( S componentInstance )
{
- // only invoke the unbind method if there is an instance (might be null
- // in the delayed component situation) and the unbind method is declared.
- boolean doUnbind = componentInstance != null && m_dependencyMetadata.getUnbind() != null;
+ // only invoke the unbind method if there is an instance (might be null
+ // in the delayed component situation) and the unbind method is declared.
+ boolean doUnbind = componentInstance != null && m_dependencyMetadata.getUnbind() != null;
- AtomicInteger trackingCount = new AtomicInteger( );
- for ( RefPair<T> boundRef : customizerRef.get().getRefs( trackingCount ) )
- {
- if ( doUnbind )
- {
- invokeUnbindMethod( componentInstance, boundRef );
- }
+ AtomicInteger trackingCount = new AtomicInteger();
+ Collection<RefPair<T>> refPairs;
+ CountDownLatch latch = new CountDownLatch( 1 );
+ synchronized ( trackerRef.get().tracked() )
+ {
+ refPairs = customizerRef.get().getRefs( trackingCount );
+ EdgeInfo info = getEdgeInfo( componentInstance );
+ info.setClose( trackingCount.get() );
+ info.setLatch( latch );
+ }
- // unget the service, we call it here since there might be a
- // bind method (or the locateService method might have been
- // called) but there is no unbind method to actually unbind
- // the service (see FELIX-832)
-// ungetService( boundRef );
- }
+ m_componentManager.log( LogService.LOG_DEBUG,
+ "DependencyManager : close {0} for {1} at tracking count {2} refpairs: {3}",
+ new Object[] {this, componentInstance, trackingCount.get(), refPairs}, null );
m_componentManager.waitForTracked( trackingCount.get() );
+ for ( RefPair<T> boundRef : refPairs )
+ {
+ if ( doUnbind )
+ {
+ invokeUnbindMethod( componentInstance, boundRef, trackingCount.get() );
+ }
+
+ // unget the service, we call it here since there might be a
+ // bind method (or the locateService method might have been
+ // called) but there is no unbind method to actually unbind
+ // the service (see FELIX-832)
+// ungetService( boundRef );
+ }
+ latch.countDown();
}
@@ -1363,21 +1392,32 @@
* component this method has no effect and just returns <code>true</code>.
*
*
+ *
* @param componentInstance
* @param refPair the service reference, service object tuple.
*
+ * @param trackingCount
* @return true if the service should be considered bound. If no bind
* method is found or the method call fails, <code>true</code> is
* returned. <code>false</code> is only returned if the service must
* be handed over to the bind method but the service cannot be
* retrieved using the service reference.
*/
- boolean invokeBindMethod( S componentInstance, RefPair refPair )
+ boolean invokeBindMethod( S componentInstance, RefPair refPair, int trackingCount )
{
// The bind method is only invoked if the implementation object is not
// null. This is valid for both immediate and delayed components
if ( componentInstance != null )
{
+ synchronized ( trackerRef.get().tracked() )
+ {
+ EdgeInfo info = edgeInfoMap.get( componentInstance );
+ if (info != null && info.outOfRange( trackingCount ) )
+ {
+ //ignore events before open started or we will have duplicate binds.
+ return true;
+ }
+ }
MethodResult result = m_bindMethods.getBind().invoke( componentInstance, refPair, MethodResult.VOID );
if ( result == null )
{
@@ -1410,8 +1450,12 @@
* @param componentInstance
* @param refPair A service reference corresponding to the service whose service
*/
- private void invokeUpdatedMethod( S componentInstance, final RefPair<T> refPair )
+ void invokeUpdatedMethod( S componentInstance, final RefPair<T> refPair, int trackingCount )
{
+ if ( m_dependencyMetadata.getUpdated() == null )
+ {
+ return;
+ }
// The updated method is only invoked if the implementation object is not
// null. This is valid for both immediate and delayed components
if ( componentInstance != null )
@@ -1424,6 +1468,15 @@
"DependencyManager : invokeUpdatedMethod : Component set, but reference not present", null );
return;
}
+ synchronized ( trackerRef.get().tracked() )
+ {
+ EdgeInfo info = edgeInfoMap.get( componentInstance );
+ if (info != null && info.outOfRange( trackingCount ) )
+ {
+ //ignore events after close started or we will have duplicate unbinds.
+ return;
+ }
+ }
if ( !m_bindMethods.getUpdated().getServiceObject( refPair, m_componentManager.getActivator().getBundleContext() ))
{
m_componentManager.log( LogService.LOG_WARNING,
@@ -1458,13 +1511,37 @@
*
* @param componentInstance
* @param refPair A service reference corresponding to the service that will be
+ * @param trackingCount
*/
- void invokeUnbindMethod( S componentInstance, final RefPair<T> refPair )
+ void invokeUnbindMethod( S componentInstance, final RefPair<T> refPair, int trackingCount )
{
// The unbind method is only invoked if the implementation object is not
// null. This is valid for both immediate and delayed components
if ( componentInstance != null )
{
+ EdgeInfo info;
+ synchronized ( trackerRef.get().tracked() )
+ {
+ info = edgeInfoMap.get( componentInstance );
+ }
+ if (info != null && info.outOfRange( trackingCount ) )
+ {
+ //wait for unbinds to complete
+ if (info.getLatch() != null)
+ {
+ try
+ {
+ info.getLatch().await( );
+ }
+ catch ( InterruptedException e )
+ {
+ //ignore
+ }
+ }
+ //ignore events after close started or we will have duplicate unbinds.
+ return;
+ }
+
if (refPair == null)
{
//TODO should this be possible? If so, reduce or eliminate logging
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/ImmediateComponentManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/ImmediateComponentManager.java
index 9ed7487..fd8af5a 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/ImmediateComponentManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/ImmediateComponentManager.java
@@ -348,19 +348,19 @@
<T> void update( DependencyManager<S, T> dependencyManager, RefPair<T> refPair, int trackingCount )
{
final S impl = ( m_tmpImplementationObject != null ) ? m_tmpImplementationObject : m_implementationObject;
- dependencyManager.update( impl, refPair );
+ dependencyManager.invokeUpdatedMethod( impl, refPair, trackingCount );
}
<T> void invokeBindMethod( DependencyManager<S, T> dependencyManager, RefPair<T> refPair, int trackingCount )
{
final S impl = ( m_tmpImplementationObject != null ) ? m_tmpImplementationObject : m_implementationObject;
- dependencyManager.invokeBindMethod( impl, refPair);
+ dependencyManager.invokeBindMethod( impl, refPair, trackingCount );
}
<T> void invokeUnbindMethod( DependencyManager<S, T> dependencyManager, RefPair<T> oldRefPair, int trackingCount )
{
final S impl = ( m_tmpImplementationObject != null ) ? m_tmpImplementationObject : m_implementationObject;
- dependencyManager.invokeUnbindMethod( impl, oldRefPair);
+ dependencyManager.invokeUnbindMethod( impl, oldRefPair, trackingCount );
}
protected void setFactoryProperties( Dictionary<String, Object> dictionary )
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceFactoryComponentManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceFactoryComponentManager.java
index 52eb6f0..eee0f6b 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceFactoryComponentManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceFactoryComponentManager.java
@@ -211,7 +211,7 @@
{
for ( S implementationObject : serviceContexts.keySet() )
{
- dependencyManager.update( implementationObject, refPair );
+ dependencyManager.invokeUpdatedMethod( implementationObject, refPair, trackingCount );
}
}
@@ -219,11 +219,11 @@
{
for ( S implementationObject : serviceContexts.keySet() )
{
- dependencyManager.invokeBindMethod( implementationObject, refPair );
+ dependencyManager.invokeBindMethod( implementationObject, refPair, trackingCount );
}
for ( S implementationObject : tmpImplementationObjects.keySet() )
{
- dependencyManager.invokeBindMethod( implementationObject, refPair );
+ dependencyManager.invokeBindMethod( implementationObject, refPair, trackingCount );
}
}
@@ -231,11 +231,11 @@
{
for ( S implementationObject : serviceContexts.keySet() )
{
- dependencyManager.invokeUnbindMethod( implementationObject, oldRefPair );
+ dependencyManager.invokeUnbindMethod( implementationObject, oldRefPair, trackingCount );
}
for ( S implementationObject : tmpImplementationObjects.keySet() )
{
- dependencyManager.invokeUnbindMethod( implementationObject, oldRefPair );
+ dependencyManager.invokeUnbindMethod( implementationObject, oldRefPair, trackingCount );
}
}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTracker.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTracker.java
index 9e6489f..f31a938 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTracker.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTracker.java
@@ -125,7 +125,7 @@
*
* @return The current Tracked object.
*/
- private Tracked tracked() {
+ public Tracked tracked() {
return tracked;
}