FELIX-387 Fix support for reference service target properties
(+ fix in ImmediateComponentManager.updated to ensure new configuration is actually used on reactivate)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@580971 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/AbstractComponentManager.java b/scr/src/main/java/org/apache/felix/scr/AbstractComponentManager.java
index 3bdd255..7fb7a5c 100644
--- a/scr/src/main/java/org/apache/felix/scr/AbstractComponentManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/AbstractComponentManager.java
@@ -324,10 +324,16 @@
// Before creating the implementation object, we are going to
// test if all the mandatory dependencies are satisfied
+ Dictionary properties = getProperties();
Iterator it = m_dependencyManagers.iterator();
while ( it.hasNext() )
{
DependencyManager dm = ( DependencyManager ) it.next();
+
+ // ensure the target filter is correctly set
+ dm.setTargetFilter( properties );
+
+ // check whether the service is satisfied
if ( !dm.isSatisfied() )
{
// at least one dependency is not satisfied
diff --git a/scr/src/main/java/org/apache/felix/scr/DependencyManager.java b/scr/src/main/java/org/apache/felix/scr/DependencyManager.java
index 8683676..acacfdf 100644
--- a/scr/src/main/java/org/apache/felix/scr/DependencyManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/DependencyManager.java
@@ -24,12 +24,14 @@
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
@@ -48,10 +50,10 @@
private static final int STATE_MASK = AbstractComponentManager.STATE_UNSATISFIED
| AbstractComponentManager.STATE_ACTIVATING | AbstractComponentManager.STATE_ACTIVE
| AbstractComponentManager.STATE_REGISTERED | AbstractComponentManager.STATE_FACTORY;
-
+
// the ServiceReference class instance
private static final Class SERVICE_REFERENCE_CLASS = ServiceReference.class;
-
+
// pseudo service to mark a bound service without actual service instance
private static final Object BOUND_SERVICE_SENTINEL = new Object();
@@ -63,22 +65,29 @@
// The map of bound services indexed by their ServiceReference
private Map m_bound;
-
+
// the number of matching services registered in the system
private int m_size;
// the bind method
private Method m_bind;
-
+
// whether the bind method takes a service reference
private boolean m_bindUsesReference;
-
+
// the unbind method
private Method m_unbind;
-
+
// whether the unbind method takes a service reference
private boolean m_unbindUsesReference;
+ // the target service filter string
+ private String m_target;
+
+ // the target service filter
+ private Filter m_targetFilter;
+
+
/**
* Constructor that receives several parameters.
*
@@ -91,17 +100,16 @@
m_dependencyMetadata = dependency;
m_bound = Collections.synchronizedMap( new HashMap() );
+ // setup the target filter from component descriptor
+ setTargetFilter( m_dependencyMetadata.getTarget() );
+
// register the service listener
String filterString = "(" + Constants.OBJECTCLASS + "=" + dependency.getInterface() + ")";
- if ( dependency.getTarget() != null )
- {
- filterString = "(&" + filterString + dependency.getTarget() + ")";
- }
componentManager.getActivator().getBundleContext().addServiceListener( this, filterString );
// get the current number of registered services available
ServiceReference refs[] = getServiceReferences();
- m_size = (refs == null) ? 0 : refs.length;
+ m_size = ( refs == null ) ? 0 : refs.length;
}
@@ -116,15 +124,15 @@
switch ( event.getType() )
{
case ServiceEvent.REGISTERED:
- m_size++;
serviceAdded( event.getServiceReference() );
break;
+
case ServiceEvent.MODIFIED:
serviceRemoved( event.getServiceReference() );
serviceAdded( event.getServiceReference() );
break;
+
case ServiceEvent.UNREGISTERING:
- m_size--;
serviceRemoved( event.getServiceReference() );
break;
}
@@ -138,12 +146,24 @@
* <p>
* Depending on the component state and dependency configuration, the
* component may be activated, re-activated or the service just be provided.
- *
+ *
* @param reference The reference to the service newly registered or
* modified.
*/
private void serviceAdded( ServiceReference reference )
{
+ // ignore the service, if it does not match the target filter
+ if ( targetFilterMatch( reference ) )
+ {
+ m_componentManager.getActivator().log( LogService.LOG_DEBUG,
+ "Dependency Manager: Ignoring added Service for " + m_dependencyMetadata.getName() + " : does not match target filter " + getTarget(),
+ m_componentManager.getComponentMetadata(), null );
+ return;
+ }
+
+ // increment the number of services
+ m_size++;
+
// if the component is currently unsatisfied, it may become satisfied
// by adding this service, try to activate
if ( m_componentManager.getState() == AbstractComponentManager.STATE_UNSATISFIED )
@@ -189,7 +209,7 @@
* Depending on the component state and dependency configuration, the
* component may be deactivated, re-activated, the service just be unbound
* with or without a replacement service.
- *
+ *
* @param reference The reference to the service unregistering or being
* modified.
*/
@@ -201,10 +221,13 @@
return;
}
+ // decrement the number of services
+ m_size--;
+
if ( handleServiceEvent() )
{
// if the dependency is not satisfied anymore, we have to
- // deactivate the component
+ // deactivate the component
if ( !isSatisfied() )
{
m_componentManager.getActivator()
@@ -259,7 +282,7 @@
+ m_dependencyMetadata.getName() + "/" + m_dependencyMetadata.getInterface()
+ " not satisfied", m_componentManager.getComponentMetadata(), null );
m_componentManager.deactivate();
-
+
// abort here we do not need to do more
return;
}
@@ -284,6 +307,7 @@
// && state != AbstractComponentManager.INSTANCE_CREATED;
}
+
//---------- Service tracking support -------------------------------------
/**
@@ -308,7 +332,7 @@
ungetService( boundRefs[i] );
}
}
-
+
// drop the method references (to help GC)
m_bind = null;
m_unbind = null;
@@ -322,7 +346,7 @@
* no correlation to the number of services bound to this dependency
* manager. It is actually the maximum number of services which may be
* bound to this dependency manager.
- *
+ *
* @see #isValid()
*/
int size()
@@ -334,7 +358,7 @@
/**
* Returns the first service reference returned by the
* {@link #getServiceReferences()} method or <code>null</code> if no
- * matching service can be found.
+ * matching service can be found.
*/
ServiceReference getServiceReference()
{
@@ -359,12 +383,12 @@
try
{
return m_componentManager.getActivator().getBundleContext().getServiceReferences(
- m_dependencyMetadata.getInterface(), m_dependencyMetadata.getTarget() );
+ m_dependencyMetadata.getInterface(), getTarget() );
}
catch ( InvalidSyntaxException ise )
{
m_componentManager.getActivator().log( LogService.LOG_ERROR,
- "Unexpected problem with filter '" + m_dependencyMetadata.getTarget() + "'",
+ "Unexpected problem with filter '" + getTarget() + "'",
m_componentManager.getComponentMetadata(), ise );
return null;
}
@@ -412,7 +436,7 @@
return ( services.size() > 0 ) ? services.toArray() : null;
}
-
+
//---------- bound services maintenance -----------------------------------
/**
@@ -444,7 +468,7 @@
* We have to keep track of all services for which we called the bind
* method to be able to call the unbind method in case the service is
* unregistered.
- *
+ *
* @param serviceReference The reference to the service being marked as
* bound.
*/
@@ -458,9 +482,9 @@
* Returns the bound service represented by the given service reference
* or <code>null</code> if this is instance is not currently bound to that
* service.
- *
+ *
* @param serviceReference The reference to the bound service
- *
+ *
* @return the service for the reference or the {@link #BOUND_SERVICE_SENTINEL}
* if the service is bound or <code>null</code> if the service is not
* bound.
@@ -476,9 +500,9 @@
* is already bound the given service, that bound service instance is
* returned. Otherwise the service retrieved from the service registry
* and kept as a bound service for future use.
- *
+ *
* @param serviceReference The reference to the service to be returned
- *
+ *
* @return The requested service or <code>null</code> if no service is
* registered for the service reference (any more).
*/
@@ -572,13 +596,13 @@
{
return true;
}
-
+
// assume success to begin with: if the dependency is optional,
// we don't care, whether we can bind a service. Otherwise, we
// require at least one service to be bound, thus we require
// flag being set in the loop below
boolean success = m_dependencyMetadata.isOptional();
-
+
// number of services to bind
for ( int index = 0; index < refs.length; index++ )
{
@@ -754,7 +778,8 @@
// Get the bind method
m_componentManager.getActivator().log( LogService.LOG_DEBUG,
"getting bind: " + m_dependencyMetadata.getBind(), m_componentManager.getComponentMetadata(), null );
- if (m_bind == null) {
+ if ( m_bind == null )
+ {
m_bind = getBindingMethod( m_dependencyMetadata.getBind(), implementationObject.getClass(),
m_dependencyMetadata.getInterface() );
@@ -767,7 +792,7 @@
m_componentManager.getComponentMetadata(), null );
return true;
}
-
+
// cache whether the bind method takes a reference
m_bindUsesReference = SERVICE_REFERENCE_CLASS.equals( m_bind.getParameterTypes()[0] );
}
@@ -777,7 +802,7 @@
if ( m_bindUsesReference )
{
parameter = ref;
-
+
// mark this service as bound using the special sentinel
bindService( ref );
}
@@ -941,4 +966,117 @@
}
}
+
+ //------------- Service target filter support -----------------------------
+
+ /**
+ * Sets the target filter from target filter property contained in the
+ * properties. The filter is taken from a property whose name is derived
+ * from the dependency name and the suffix <code>.target</code> as defined
+ * for target properties on page 302 of the Declarative Services
+ * Specification, section 112.6.
+ *
+ * @param properties The properties containing the optional target service
+ * filter property
+ */
+ void setTargetFilter( Dictionary properties )
+ {
+ setTargetFilter( ( String ) properties.get( m_dependencyMetadata.getTargetPropertyName() ) );
+ }
+
+
+ /**
+ * Sets the target filter of this dependency to the new filter value. If the
+ * new target filter is the same as the old target filter, this method has
+ * not effect. Otherwise any services currently bound but not matching the
+ * new filter are unbound. Likewise any registered services not currently
+ * bound but matching the new filter are bound.
+ *
+ * @param target The new target filter to be set. This may be
+ * <code>null</code> if no target filtering is to be used.
+ */
+ private void setTargetFilter( String target )
+ {
+ // do nothing if target filter does not change
+ if ( ( m_target == null && target == null ) || ( m_target != null && m_target.equals( target ) ) )
+ {
+ return;
+ }
+
+ m_target = target;
+ if ( target != null )
+ {
+ try
+ {
+ m_targetFilter = m_componentManager.getActivator().getBundleContext().createFilter( target );
+ }
+ catch ( InvalidSyntaxException ise )
+ {
+ // log
+ m_targetFilter = null;
+ }
+ }
+ else
+ {
+ m_targetFilter = null;
+ }
+
+ // check for services to be removed
+ if ( m_targetFilter != null )
+ {
+ ServiceReference[] refs = getBoundServiceReferences();
+ if ( refs != null )
+ {
+ for ( int i = 0; i < refs.length; i++ )
+ {
+ if ( !m_targetFilter.match( refs[i] ) )
+ {
+ // might want to do this asynchronously ??
+ serviceRemoved( refs[i] );
+ }
+ }
+ }
+ }
+
+ // check for new services to be added
+ ServiceReference[] refs = getServiceReferences();
+ if ( refs != null )
+ {
+ for ( int i = 0; i < refs.length; i++ )
+ {
+ if ( getBoundService( refs[i] ) == null )
+ {
+ // might want to do this asynchronously ??
+ serviceAdded( refs[i] );
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Returns the target filter of this dependency as a string or
+ * <code>null</code> if this dependency has no target filter set.
+ *
+ * @return The target filter of this dependency or <code>null</code> if
+ * none is set.
+ */
+ private String getTarget()
+ {
+ return m_target;
+ }
+
+
+ /**
+ * Checks whether the service references matches the target filter of this
+ * dependency.
+ *
+ * @param ref The service reference to check
+ * @return <code>true</code> if this dependency has no target filter or if
+ * the target filter matches the service reference.
+ */
+ private boolean targetFilterMatch( ServiceReference ref )
+ {
+ return m_targetFilter == null || m_targetFilter.match( ref );
+ }
}
diff --git a/scr/src/main/java/org/apache/felix/scr/ImmediateComponentManager.java b/scr/src/main/java/org/apache/felix/scr/ImmediateComponentManager.java
index eb98100..14d5693 100644
--- a/scr/src/main/java/org/apache/felix/scr/ImmediateComponentManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/ImmediateComponentManager.java
@@ -24,6 +24,7 @@
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Iterator;
+import java.util.List;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
@@ -342,13 +343,25 @@
// 1. the properties from the component descriptor
Dictionary props = copyTo( null, getComponentMetadata().getProperties() );
- // 2. overlay with Configuration Admin properties
+ // 2. add target properties of references
+ // 112.6 Component Properties, target properties (p. 302)
+ List depMetaData = getComponentMetadata().getDependencies();
+ for ( Iterator di = depMetaData.iterator(); di.hasNext(); )
+ {
+ ReferenceMetadata rm = ( ReferenceMetadata ) di.next();
+ if ( rm.getTarget() != null )
+ {
+ props.put( rm.getTargetPropertyName(), rm.getTarget() );
+ }
+ }
+
+ // 3. overlay with Configuration Admin properties
copyTo( props, m_configurationProperties );
- // 3. copy any component factory properties, not supported yet
+ // 4. copy any component factory properties, not supported yet
copyTo( props, m_factoryProperties );
- // 4. set component.name and component.id
+ // 5. set component.name and component.id
props.put( ComponentConstants.COMPONENT_NAME, getComponentMetadata().getName() );
props.put( ComponentConstants.COMPONENT_ID, new Long( m_componentId ) );
@@ -374,6 +387,9 @@
// store the properties
m_configurationProperties = configuration;
+ // clear the current properties to force using the configuration data
+ m_properties = null;
+
// reactivate the component to ensure it is provided with the
// configuration data
if ( ( getState() & ( STATE_ACTIVE | STATE_FACTORY | STATE_REGISTERED ) ) != 0 )
diff --git a/scr/src/main/java/org/apache/felix/scr/ReferenceMetadata.java b/scr/src/main/java/org/apache/felix/scr/ReferenceMetadata.java
index 31b4a86..66296c2 100644
--- a/scr/src/main/java/org/apache/felix/scr/ReferenceMetadata.java
+++ b/scr/src/main/java/org/apache/felix/scr/ReferenceMetadata.java
@@ -161,7 +161,7 @@
return;
}
- m_target = target;
+ m_target = ( target == null || target.length() == 0 ) ? null : target;
}
@@ -314,6 +314,19 @@
/**
+ * Returns the name of the component property referring to the {@link #getTarget() target}
+ * property of this reference.
+ *
+ * @return the name of the target property which is the name of this referene
+ * suffixed with the string ".target".
+ */
+ public String getTargetPropertyName()
+ {
+ return getName() + ".target";
+ }
+
+
+ /**
* Method used to verify if the semantics of this metadata are correct
*
*/