FELIX-950 Replace bound service for non-multiple references if a new
service is registered with a higher service.ranking. Also always ensure
binding to the highest ranking service of non-multiple references.
Finally cleaned up ServiceReference and Service acquisition methods.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@745981 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/ComponentContextImpl.java b/scr/src/main/java/org/apache/felix/scr/impl/ComponentContextImpl.java
index 4c11191..b7ba318 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/ComponentContextImpl.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/ComponentContextImpl.java
@@ -60,49 +60,7 @@
public Object locateService( String name )
{
DependencyManager dm = m_componentManager.getDependencyManager( name );
- if ( dm == null )
- {
- return null;
- }
-
- ServiceReference selectedRef;
- if ( dm.size() == 1 )
- {
- // short cut for single bound service
- selectedRef = dm.getServiceReference();
- }
- else
- {
- // is it correct to assume an ordered bound services set ?
- int maxRanking = Integer.MIN_VALUE;
- long minId = Long.MAX_VALUE;
- selectedRef = null;
-
- ServiceReference[] refs = dm.getFrameworkServiceReferences();
- for ( int i = 0; refs != null && i < refs.length; i++ )
- {
- ServiceReference ref = refs[i];
- Integer rank = ( Integer ) ref.getProperty( Constants.SERVICE_RANKING );
- int ranking = ( rank == null ) ? Integer.MIN_VALUE : rank.intValue();
- long id = ( ( Long ) ref.getProperty( Constants.SERVICE_ID ) ).longValue();
- if ( maxRanking < ranking || ( maxRanking == ranking && id < minId ) )
- {
- maxRanking = ranking;
- minId = id;
- selectedRef = ref;
- }
- }
- }
-
- // this is not realistic, as at least one service is available
- // whose service id is smaller than Long.MAX_VALUE, still be sure
- if ( selectedRef == null )
- {
- return null;
- }
-
- // return the service for the selected reference
- return dm.getService( selectedRef );
+ return ( dm != null ) ? dm.getService() : null;
}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/DependencyManager.java b/scr/src/main/java/org/apache/felix/scr/impl/DependencyManager.java
index 99cc5d0..c8b2402 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/DependencyManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/DependencyManager.java
@@ -184,15 +184,34 @@
// otherwise bind if we have a bind method and the service needs
// be bound
- else if ( m_dependencyMetadata.getBind() != null && ( m_dependencyMetadata.isMultiple() || !isBound() ) )
+ else if ( m_dependencyMetadata.getBind() != null )
{
- // bind the service, getting it if required
- invokeBindMethod( m_componentManager.getInstance(), reference );
+ // multiple bindings or not bound at all yet
+ if ( m_dependencyMetadata.isMultiple() || !isBound() )
+ {
+ // bind the service, getting it if required
+ invokeBindMethod( m_componentManager.getInstance(), reference );
+ }
+ else
+ {
+ // single service binding only which already exists
+ // we have to check whether the bound service is to be
+ // replaced
+ ServiceReference[] boundRefs = getBoundServiceReferences();
+ if ( isHigher( reference, boundRefs[0] ) )
+ {
+ // bind the service, getting it if required
+ invokeBindMethod( m_componentManager.getInstance(), reference );
+
+ // unbind the old service reference
+ unbind( m_componentManager.getInstance(), boundRefs );
+ }
+ }
}
}
}
-
-
+
+
/**
* Called by the {@link #serviceChanged(ServiceEvent)} method if an existing
* service is unregistered from the system or if a registered service has
@@ -404,18 +423,6 @@
/**
- * Returns the first service reference returned by the
- * {@link #getFrameworkServiceReferences()} method or <code>null</code> if no
- * matching service can be found.
- */
- ServiceReference getServiceReference()
- {
- ServiceReference[] sr = getFrameworkServiceReferences();
- return ( sr != null && sr.length > 0 ) ? sr[0] : null;
- }
-
-
- /**
* Returns an array of <code>ServiceReference</code> instances for services
* implementing the interface and complying to the (optional) target filter
* declared for this dependency. If no matching service can be found
@@ -443,24 +450,74 @@
/**
+ * Returns a <code>ServiceReference</code> instances for a service
+ * implementing the interface and complying to the (optional) target filter
+ * declared for this dependency. If no matching service can be found
+ * <code>null</code> is returned. If the configured target filter is
+ * syntactically incorrect an error message is logged with the LogService
+ * and <code>null</code> is returned. If multiple matching services are
+ * registered the service with the highest service.ranking value is
+ * returned. If multiple matching services have the same service.ranking
+ * value, the service with the lowest service.id is returned.
+ * <p>
+ * This method always directly accesses the framework's service registry
+ * and ignores the services bound by this dependency manager.
+ */
+ ServiceReference getFrameworkServiceReference()
+ {
+ // get the framework registered services and short cut
+ ServiceReference[] refs = getFrameworkServiceReferences();
+ if ( refs == null )
+ {
+ return null;
+ }
+ else if ( refs.length == 1 )
+ {
+ return refs[0];
+ }
+
+ // is it correct to assume an ordered bound services set ?
+ int maxRanking = Integer.MIN_VALUE;
+ long minId = Long.MAX_VALUE;
+ ServiceReference selectedRef = null;
+
+ // find the service with the highest ranking
+ for ( int i = 0; refs != null && i < refs.length; i++ )
+ {
+ ServiceReference ref = refs[i];
+ int ranking = getServiceRanking( ref );
+ long id = getServiceId( ref );
+ if ( maxRanking < ranking || ( maxRanking == ranking && id < minId ) )
+ {
+ maxRanking = ranking;
+ minId = id;
+ selectedRef = ref;
+ }
+ }
+
+ return selectedRef;
+ }
+
+
+ /**
* Returns the service instance for the service reference returned by the
- * {@link #getServiceReference()} method. If this returns a
+ * {@link #getFrameworkServiceReference()} method. If this returns a
* non-<code>null</code> service instance the service is then considered
* bound to this instance.
*/
Object getService()
{
- ServiceReference sr = getServiceReference();
+ ServiceReference sr = getFrameworkServiceReference();
return ( sr != null ) ? getService( sr ) : null;
}
/**
* Returns an array of service instances for the service references returned
- * by the {@link #getFrameworkServiceReferences()} method. If no services match the
- * criteria configured for this dependency <code>null</code> is returned.
- * All services returned by this method will be considered bound after this
- * method returns.
+ * by the {@link #getFrameworkServiceReferences()} method. If no services
+ * match the criteria configured for this dependency <code>null</code> is
+ * returned. All services returned by this method will be considered bound
+ * after this method returns.
*/
Object[] getServices()
{
@@ -484,6 +541,96 @@
}
+ /**
+ * Returns <code>true</code> if the <code>newReference</code> has a higher
+ * ranking than the <code>oldReference</code>, otherwise <code>false</code>
+ * is returned.
+ * <p>
+ * The higher ranking of a service reference is defined in the OSGi
+ * Compendium Services Specification as the service with the highest service
+ * ranking as specified by the service.ranking property. If both services
+ * have the same service ranking, then the service with the lowest service
+ * ID as specified by the service.id property is chosen.
+ *
+ * @param newReference The ServiceReference representing the newly added
+ * Service
+ * @param oldReference The ServiceReference representing the service which
+ * is already bound to the component
+ *
+ * @return <code>true</code> if <code>newReference</code> has higher ranking
+ */
+ private boolean isHigher( ServiceReference newReference, ServiceReference oldReference )
+ {
+ // get and compare the service.ranking properties
+ int nrRank = getServiceRanking( newReference );
+ int orRank = getServiceRanking( oldReference );
+ if ( nrRank > orRank )
+ {
+ return true;
+ }
+ else if ( nrRank < orRank )
+ {
+ return false;
+ }
+
+ // no ranks are equal, compare the service ids. These ids are never equal,
+ // so unless a problem exists, this should be decisive
+ try
+ {
+ return getServiceId( newReference ) < getServiceId( oldReference );
+ }
+ catch ( Exception e )
+ {
+ // ignore, we don't expect an exception, since the servid.id
+ // property is set by the framework as a Long value; so we neither
+ // expect this property to be null nor to be anything else than Long
+ }
+
+ // fall back to newReference not being higher
+ return false;
+ }
+
+
+ /**
+ * Returns the value of the <code>service.ranking</code> service property
+ * if the property exists and is of type <code>java.lang.Integer</code>. If
+ * the property does not exist or is of another type, zero is returned as
+ * the default value for service ranking.
+ *
+ * @param serviceReference The Service reference whose ranking is to be
+ * returned.
+ */
+ private int getServiceRanking( ServiceReference serviceReference )
+ {
+ Object nrRankObj = serviceReference.getProperty( Constants.SERVICE_RANKING );
+ if ( nrRankObj instanceof Integer )
+ {
+ return ( ( Integer ) nrRankObj ).intValue();
+ }
+ return 0;
+ }
+
+
+ /**
+ * Returns the value of the <code>service.id</code> service property.
+ *
+ * @param serviceReference The Service reference whose service id is to be
+ * returned.
+ *
+ * @throws ClassCastException if the <code>service.id</code> property exists
+ * but is not a <code>java.lang.Long</code> value. This is not
+ * expected since the framework should guarantee this property and
+ * its type.
+ * @throws NullPointerException if the <code>service.id</code> property
+ * does not exist. This is not expected since the framework should
+ * guarantee this property and its type.
+ */
+ private long getServiceId( ServiceReference serviceReference )
+ {
+ return ( ( Long ) serviceReference.getProperty( Constants.SERVICE_ID ) ).longValue();
+ }
+
+
//---------- bound services maintenance -----------------------------------
/**
@@ -635,35 +782,38 @@
return true;
}
- // Get service references
- ServiceReference refs[] = getFrameworkServiceReferences();
-
- // refs can be null if the dependency is optional
- if ( refs == null )
- {
- 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++ )
+ // Get service reference(s)
+ if ( m_dependencyMetadata.isMultiple() )
{
- // success is if we have the minimal required number of services bound
- if ( invokeBindMethod( instance, refs[index] ) )
+ // bind all registered services
+ ServiceReference[] refs = getFrameworkServiceReferences();
+ if ( refs != null )
+ {
+ for ( int index = 0; index < refs.length; index++ )
+ {
+ // success is if we have the minimal required number of services bound
+ if ( invokeBindMethod( instance, refs[index] ) )
+ {
+ // of course, we have success if the service is bound
+ success = true;
+ }
+ }
+ }
+ }
+ else
+ {
+ // bind best matching service
+ ServiceReference ref = getFrameworkServiceReference();
+ if ( ref != null && invokeBindMethod( instance, ref ) )
{
// of course, we have success if the service is bound
success = true;
-
- // if the reference is not multiple, we are already done
- if ( !m_dependencyMetadata.isMultiple() )
- {
- break;
- }
}
}
@@ -680,11 +830,20 @@
*/
void unbind( Object instance )
{
+ unbind( instance, getBoundServiceReferences() );
+ }
+
+
+ /**
+ * Revoke the given bindings. This method cannot throw an exception since
+ * it must try to complete all that it can
+ */
+ private void unbind( Object instance, ServiceReference[] boundRefs )
+ {
// 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 = instance != null && m_dependencyMetadata.getUnbind() != null;
- ServiceReference[] boundRefs = getBoundServiceReferences();
if ( boundRefs != null )
{
for ( int i = 0; i < boundRefs.length; i++ )