You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by dj...@apache.org on 2014/01/24 02:34:29 UTC
svn commit: r1560874 - in /felix/trunk/scr/src:
main/java/org/apache/felix/scr/impl/helper/
main/java/org/apache/felix/scr/impl/manager/
main/java/org/apache/felix/scr/impl/metadata/
test/java/org/apache/felix/scr/integration/ test/resources/
Author: djencks
Date: Fri Jan 24 01:34:28 2014
New Revision: 1560874
URL: http://svn.apache.org/r1560874
Log:
FELIX-4391 initial support for refname.cardinality.minimum property
Added:
felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/helper/Coercions.java (with props)
felix/trunk/scr/src/test/java/org/apache/felix/scr/integration/MinimumCardinalityTest.java (with props)
felix/trunk/scr/src/test/resources/integration_test_min_cardinality.xml (with props)
Modified:
felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTracker.java
felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTrackerCustomizer.java
felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java
Added: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/helper/Coercions.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/helper/Coercions.java?rev=1560874&view=auto
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/helper/Coercions.java (added)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/helper/Coercions.java Fri Jan 24 01:34:28 2014
@@ -0,0 +1,396 @@
+/*
+ * 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.scr.impl.helper;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.component.ComponentException;
+
+/**
+ * This implements the coercion table in RFC 190 5.6.3
+ *
+ */
+public class Coercions
+{
+// Numbers are AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short
+//annotation fields can be primitives, String, Class, enums, annotations, and arrays of the preceding types
+// input scalars
+// String | Integer | Long | Float
+// | Double | Byte | Short
+//| Character | Boolean
+ private static final byte byte0 = 0;
+ private static final double double0 = 0;
+ private static final float float0 = 0;
+ private static final int int0 = 0;
+ private static final long long0 = 0;
+ private static final short short0 = 0;
+
+ public static byte coerceToByte(Object o)
+ {
+ o = multipleToSingle( o, byte0 );
+ if (o instanceof Byte)
+ {
+ return (Byte)o;
+ }
+ if (o instanceof String)
+ {
+ try
+ {
+ return Byte.parseByte( (String)o );
+ }
+ catch ( NumberFormatException e )
+ {
+ throw new ComponentException(e);
+ }
+ }
+ if (o instanceof Boolean)
+ {
+ return (Boolean)o? 1: byte0;
+ }
+ if (o instanceof Character)
+ {
+ return ( byte ) ((Character)o).charValue();
+ }
+ if (o instanceof Number)
+ {
+ return ((Number)o).byteValue();
+ }
+ if (o == null)
+ {
+ return 0;
+ }
+ throw new ComponentException( "Unrecognized input type: " + o);
+ }
+
+ public static double coerceToDouble(Object o)
+ {
+ o = multipleToSingle( o, double0 );
+ if (o instanceof Double)
+ {
+ return (Double)o;
+ }
+ if (o instanceof String)
+ {
+ try
+ {
+ return Double.parseDouble((String)o );
+ }
+ catch ( NumberFormatException e )
+ {
+ throw new ComponentException(e);
+ }
+ }
+ if (o instanceof Boolean)
+ {
+ return (Boolean)o? 1: 0;
+ }
+ if (o instanceof Character)
+ {
+ return ( double ) ((Character)o).charValue();
+ }
+ if (o instanceof Number)
+ {
+ return ((Number)o).doubleValue();
+ }
+ if (o == null)
+ {
+ return 0;
+ }
+ throw new ComponentException( "Unrecognized input type: " + o);
+ }
+
+ public static float coerceToFloat(Object o)
+ {
+ o = multipleToSingle( o, float0 );
+ if (o instanceof Float)
+ {
+ return (Float)o;
+ }
+ if (o instanceof String)
+ {
+ try
+ {
+ return Float.parseFloat((String)o );
+ }
+ catch ( NumberFormatException e )
+ {
+ throw new ComponentException(e);
+ }
+ }
+ if (o instanceof Boolean)
+ {
+ return (Boolean)o? 1: 0;
+ }
+ if (o instanceof Character)
+ {
+ return ( float ) ((Character)o).charValue();
+ }
+ if (o instanceof Number)
+ {
+ return ((Number)o).floatValue();
+ }
+ if (o == null)
+ {
+ return 0;
+ }
+ throw new ComponentException( "Unrecognized input type: " + o);
+ }
+
+ public static int coerceToInteger(Object o)
+ {
+ o = multipleToSingle( o, int0 );
+ if (o instanceof Integer)
+ {
+ return (Integer)o;
+ }
+ if (o instanceof String)
+ {
+ try
+ {
+ return Integer.parseInt( (String)o );
+ }
+ catch ( NumberFormatException e )
+ {
+ throw new ComponentException(e);
+ }
+ }
+ if (o instanceof Boolean)
+ {
+ return (Boolean)o? 1: 0;
+ }
+ if (o instanceof Character)
+ {
+ return ( int ) ((Character)o).charValue();
+ }
+ if (o instanceof Number)
+ {
+ return ((Number)o).intValue();
+ }
+ if (o == null)
+ {
+ return 0;
+ }
+ throw new ComponentException( "Unrecognized input type: " + o);
+ }
+
+ public static long coerceToLong(Object o)
+ {
+ o = multipleToSingle( o, long0 );
+ if (o instanceof Long)
+ {
+ return (Long)o;
+ }
+ if (o instanceof String)
+ {
+ try
+ {
+ return Long.parseLong( (String)o );
+ }
+ catch ( NumberFormatException e )
+ {
+ throw new ComponentException(e);
+ }
+ }
+ if (o instanceof Boolean)
+ {
+ return (Boolean)o? 1: 0;
+ }
+ if (o instanceof Character)
+ {
+ return ( long ) ((Character)o).charValue();
+ }
+ if (o instanceof Number)
+ {
+ return ((Number)o).longValue();
+ }
+ if (o == null)
+ {
+ return 0;
+ }
+ throw new ComponentException( "Unrecognized input type: " + o);
+ }
+
+ public static short coerceToShort(Object o)
+ {
+ o = multipleToSingle( o, short0 );
+ if (o instanceof Short)
+ {
+ return (Short)o;
+ }
+ if (o instanceof String)
+ {
+ try
+ {
+ return Short.parseShort( (String)o );
+ }
+ catch ( NumberFormatException e )
+ {
+ throw new ComponentException(e);
+ }
+ }
+ if (o instanceof Boolean)
+ {
+ return (Boolean)o? 1: short0;
+ }
+ if (o instanceof Character)
+ {
+ return ( short ) ((Character)o).charValue();
+ }
+ if (o instanceof Number)
+ {
+ return ((Number)o).shortValue();
+ }
+ if (o == null)
+ {
+ return 0;
+ }
+ throw new ComponentException( "Unrecognized input type: " + o);
+ }
+
+ public static String coerceToString(Object o)
+ {
+ o = multipleToSingle( o, null );
+ if (o instanceof String)
+ {
+ return (String)o;
+ }
+ if (o == null)
+ {
+ return null;
+ }
+
+ return o.toString();
+ }
+
+ public static boolean coerceToBoolean(Object o)
+ {
+ o = multipleToSingle( o, false );
+ if (o instanceof Boolean)
+ {
+ return (Boolean)o;
+ }
+ if (o instanceof String)
+ {
+ try
+ {
+ return Boolean.parseBoolean( (String)o );
+ }
+ catch ( NumberFormatException e )
+ {
+ throw new ComponentException(e);
+ }
+ }
+ if (o instanceof Character)
+ {
+ return ((Character)o).charValue() != 0;
+ }
+ if (o instanceof Number)
+ {
+ return ((Number)o).intValue() != 0;
+ }
+ if (o == null)
+ {
+ return false;
+ }
+ throw new ComponentException( "Unrecognized input type: " + o);
+ }
+
+ public static Class<?> coerceToClass(Object o, Bundle b)
+ {
+ o = multipleToSingle( o, null );
+ if (o == null)
+ {
+ return null;
+ }
+ if (o instanceof String)
+ {
+ try
+ {
+ return b.loadClass( (String)o );
+ }
+ catch ( ClassNotFoundException e )
+ {
+ throw new ComponentException(e);
+ }
+ }
+ throw new ComponentException( "Unrecognized input type: " + o);
+ }
+
+ public static <T extends Enum<T>> T coerceToEnum(Object o, Class<T> clazz)
+ {
+ o = multipleToSingle( o, null );
+ if (o instanceof String)
+ {
+ try
+ {
+ return Enum.valueOf( clazz, (String)o );
+ }
+ catch ( IllegalArgumentException e )
+ {
+ throw new ComponentException(e);
+ }
+ }
+ if (o == null)
+ {
+ return null;
+ }
+ throw new ComponentException( "Unrecognized input type: " + o);
+ }
+
+ private static Object multipleToSingle(Object o, Object defaultValue)
+ {
+ if (o instanceof Collection)
+ {
+ return firstCollectionElement( o, defaultValue );
+ }
+ if (o != null && o.getClass().isArray()) {
+ return firstArrayElement( o, defaultValue);
+ }
+ return o;
+ }
+
+ private static Object firstCollectionElement( Object raw, Object defaultValue )
+ {
+ if (!(raw instanceof Collection))
+ {
+ throw new ComponentException("Not a collection: " + raw);
+ }
+ Collection c = ( Collection ) raw;
+ if (c.isEmpty())
+ {
+ return defaultValue;
+ }
+ return c.iterator().next();
+ }
+
+ private static Object firstArrayElement(Object o, Object defaultValue)
+ {
+ if (o == null || !o.getClass().isArray()) {
+ throw new ComponentException("Not an array: " + o);
+ }
+ if (Array.getLength( o ) == 0)
+ {
+ return defaultValue;
+ }
+ return Array.get( o, 0 );
+ }
+
+}
Propchange: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/helper/Coercions.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/helper/Coercions.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/helper/Coercions.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java?rev=1560874&r1=1560873&r2=1560874&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java Fri Jan 24 01:34:28 2014
@@ -41,6 +41,7 @@ import org.apache.felix.scr.Reference;
import org.apache.felix.scr.impl.BundleComponentActivator;
import org.apache.felix.scr.impl.helper.BindMethod;
import org.apache.felix.scr.impl.helper.BindMethods;
+import org.apache.felix.scr.impl.helper.Coercions;
import org.apache.felix.scr.impl.helper.MethodResult;
import org.apache.felix.scr.impl.metadata.ReferenceMetadata;
import org.osgi.framework.BundleContext;
@@ -50,6 +51,7 @@ import org.osgi.framework.InvalidSyntaxE
import org.osgi.framework.ServicePermission;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentConstants;
+import org.osgi.service.component.ComponentException;
import org.osgi.service.log.LogService;
@@ -86,7 +88,7 @@ public class DependencyManager<S, T> imp
// the target service filter
private volatile Filter m_targetFilter;
- //private volatile boolean m_registered;
+ private volatile int m_minCardinality;
/**
* Constructor that receives several parameters.
@@ -99,6 +101,8 @@ public class DependencyManager<S, T> imp
m_dependencyMetadata = dependency;
m_index = index;
m_customizer = newCustomizer();
+
+ m_minCardinality = defaultMinimumCardinality( dependency );
// dump the reference information if DEBUG is enabled
if ( m_componentManager.isLogEnabled( LogService.LOG_DEBUG ) )
@@ -112,6 +116,11 @@ public class DependencyManager<S, T> imp
dependency.getCardinality(), dependency.getBind(), dependency.getUnbind() }, null );
}
}
+
+ private static int defaultMinimumCardinality(ReferenceMetadata dependency)
+ {
+ return dependency.isOptional()? 0: 1;
+ }
int getIndex()
{
@@ -170,11 +179,7 @@ public class DependencyManager<S, T> imp
{
return false;
}
- if (isOptional())
- {
- return true;
- }
- return !tracker.isEmpty();
+ return cardinalitySatisfied( tracker.getServiceCount() );
}
protected ServiceTracker<T, RefPair<T>> getTracker()
@@ -258,9 +263,9 @@ public class DependencyManager<S, T> imp
return refPair;
}
- public void addedService( ServiceReference<T> serviceReference, RefPair<T> refPair, int trackingCount )
+ public void addedService( ServiceReference<T> serviceReference, RefPair<T> refPair, int trackingCount, int serviceCount )
{
- if ( !isOptional() )
+ if ( cardinalityJustSatisfied( serviceCount ) )
{
m_componentManager.activateInternal( trackingCount );
}
@@ -273,21 +278,17 @@ public class DependencyManager<S, T> imp
public void removedService( ServiceReference<T> serviceReference, RefPair<T> refPair, int trackingCount )
{
refPair.setDeleted( true );
- if ( !isOptional() )
+ if ( !cardinalitySatisfied( getTracker().getServiceCount() ) )
{
- if (getTracker().isEmpty())
- {
- m_componentManager.deactivateInternal( ComponentConstants.DEACTIVATION_REASON_REFERENCE, false, false );
- }
+ m_componentManager.deactivateInternal( ComponentConstants.DEACTIVATION_REASON_REFERENCE, false, false );
}
}
public boolean prebind()
{
- boolean success = m_dependencyMetadata.isOptional() || !getTracker().isEmpty();
AtomicInteger trackingCount = new AtomicInteger( );
- getTracker().getTracked( true, trackingCount );
- return success;
+ int serviceCount = getTracker().getTracked( true, trackingCount ).size();
+ return cardinalitySatisfied( serviceCount );
}
public void close()
@@ -316,7 +317,7 @@ public class DependencyManager<S, T> imp
return refPair;
}
- public void addedService( ServiceReference<T> serviceReference, RefPair<T> refPair, int trackingCount )
+ public void addedService( ServiceReference<T> serviceReference, RefPair<T> refPair, int trackingCount, int serviceCount )
{
m_componentManager.log( LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleDynamic added {2} (enter)", new Object[] {getName(), trackingCount, serviceReference}, null );
boolean tracked = false;
@@ -334,7 +335,7 @@ public class DependencyManager<S, T> imp
m_componentManager.registerMissingDependency( DependencyManager.this, serviceReference, trackingCount );
}
}
- else if ( isTrackerOpened() && !isOptional() )
+ else if ( isTrackerOpened() && cardinalityJustSatisfied( serviceCount ) )
{
m_componentManager.log( LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleDynamic, activating", new Object[] {getName(), trackingCount}, null );
tracked( trackingCount );
@@ -368,7 +369,7 @@ public class DependencyManager<S, T> imp
{
m_componentManager.log( LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleDynamic removed {2} (enter)", new Object[] {getName(), trackingCount, serviceReference}, null );
refPair.setDeleted( true );
- boolean unbind = isOptional() || !getTracker().isEmpty();
+ boolean unbind = cardinalitySatisfied( getTracker().getServiceCount() );
if ( unbind )
{
if ( isActive() )
@@ -392,21 +393,21 @@ public class DependencyManager<S, T> imp
public boolean prebind()
{
- boolean success = m_dependencyMetadata.isOptional();
+ int serviceCount = 0;
AtomicInteger trackingCount = new AtomicInteger( );
SortedMap<ServiceReference<T>, RefPair<T>> tracked = getTracker().getTracked( true, trackingCount );
for (RefPair<T> refPair: tracked.values())
{
if (getServiceObject( m_bindMethods.getBind(), refPair ))
{
- success = true;
+ serviceCount++;
}
else
{
m_componentManager.registerMissingDependency( DependencyManager.this, refPair.getRef(), trackingCount.get() );
}
}
- return success;
+ return cardinalitySatisfied( serviceCount );
}
public void close()
@@ -452,7 +453,7 @@ public class DependencyManager<S, T> imp
return refPair;
}
- public void addedService( ServiceReference<T> serviceReference, RefPair<T> refPair, int trackingCount )
+ public void addedService( ServiceReference<T> serviceReference, RefPair<T> refPair, int trackingCount, int serviceCount )
{
m_componentManager.log( LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleStaticGreedy added {2} (enter)", new Object[] {getName(), trackingCount, serviceReference}, null );
tracked( trackingCount );
@@ -465,7 +466,7 @@ public class DependencyManager<S, T> imp
m_componentManager.activateInternal( trackingCount );
}
- else if ( isTrackerOpened() && !isOptional() )
+ else if ( isTrackerOpened() && cardinalityJustSatisfied( serviceCount ) )
{
m_componentManager.activateInternal( trackingCount );
}
@@ -498,7 +499,7 @@ public class DependencyManager<S, T> imp
//try to reactivate after ref is no longer tracked.
m_componentManager.activateInternal( trackingCount );
}
- else if ( !isOptional() && getTracker().isEmpty() )
+ else if ( !cardinalitySatisfied( getTracker().getServiceCount() ) ) //may be called from an old tracker, so getTracker() may give a different answer
{
m_componentManager.log( LogService.LOG_DEBUG,
"Dependency Manager: Static dependency on {0}/{1} is broken", new Object[]
@@ -512,19 +513,23 @@ public class DependencyManager<S, T> imp
public boolean prebind()
{
- boolean success = m_dependencyMetadata.isOptional();
+ int serviceCount = 0;
AtomicInteger trackingCount = new AtomicInteger( );
- SortedMap<ServiceReference<T>, RefPair<T>> tracked = getTracker().getTracked( success || !getTracker().isEmpty(), trackingCount );
+ final ServiceTracker<T, RefPair<T>> tracker = getTracker();
+ SortedMap<ServiceReference<T>, RefPair<T>> tracked = tracker.getTracked( cardinalitySatisfied( tracker.getServiceCount() ), trackingCount );
for (RefPair<T> refPair: tracked.values())
{
- success |= getServiceObject( m_bindMethods.getBind(), refPair );
- if ( refPair.isFailed() )
+ if ( getServiceObject( m_bindMethods.getBind(), refPair ) )
+ {
+ serviceCount++;
+ }
+ else
{
m_componentManager.registerMissingDependency( DependencyManager.this, refPair.getRef(),
trackingCount.get() );
}
}
- return success;
+ return cardinalitySatisfied( serviceCount );
}
public void close()
@@ -558,11 +563,11 @@ public class DependencyManager<S, T> imp
return refPair;
}
- public void addedService( ServiceReference<T> serviceReference, RefPair<T> refPair, int trackingCount )
+ public void addedService( ServiceReference<T> serviceReference, RefPair<T> refPair, int trackingCount, int serviceCount )
{
m_componentManager.log( LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleStaticReluctant added {2} (enter)", new Object[] {getName(), trackingCount, serviceReference}, null );
tracked( trackingCount );
- if ( isTrackerOpened() && !isOptional() && !isActive())
+ if ( isTrackerOpened() && cardinalityJustSatisfied( serviceCount ) && !isActive())
{
m_componentManager.activateInternal( trackingCount );
}
@@ -602,7 +607,7 @@ public class DependencyManager<S, T> imp
}
}
- else if ( !isOptional() && getTracker().isEmpty() )
+ else if ( !cardinalitySatisfied( getTracker().getServiceCount() ) )
{
m_componentManager.log( LogService.LOG_DEBUG,
"Dependency Manager: Static dependency on {0}/{1} is broken", new Object[]
@@ -615,22 +620,28 @@ public class DependencyManager<S, T> imp
public boolean prebind()
{
- boolean success = m_dependencyMetadata.isOptional();
+ int serviceCount = 0;
Collection<RefPair<T>> refs = this.refs.get();
if (refs != null) {
//another thread is concurrently opening, and it got done already
for (RefPair<T> refPair: refs)
{
- success |= getServiceObject( m_bindMethods.getBind(), refPair );
+ if (getServiceObject( m_bindMethods.getBind(), refPair ))
+ {
+ serviceCount++;
+ }
}
- return success;
+ return cardinalitySatisfied( serviceCount );
}
refs = new ArrayList<RefPair<T>>();
AtomicInteger trackingCount = new AtomicInteger( );
SortedMap<ServiceReference<T>, RefPair<T>> tracked = getTracker().getTracked( true, trackingCount );
for (RefPair<T> refPair: tracked.values())
{
- success |= getServiceObject( m_bindMethods.getBind(), refPair );
+ if (getServiceObject( m_bindMethods.getBind(), refPair ))
+ {
+ serviceCount++;
+ }
refs.add(refPair) ;
}
if ( this.refs.compareAndSet( null, refs ) )
@@ -647,7 +658,7 @@ public class DependencyManager<S, T> imp
ungetService( ref );
}
}
- return success;
+ return cardinalitySatisfied( serviceCount );
}
public void close()
@@ -686,7 +697,7 @@ public class DependencyManager<S, T> imp
return refPair;
}
- public void addedService( ServiceReference<T> serviceReference, RefPair<T> refPair, int trackingCount )
+ public void addedService( ServiceReference<T> serviceReference, RefPair<T> refPair, int trackingCount, int serviceCount )
{
m_componentManager.log( LogService.LOG_DEBUG, "dm {0} tracking {1} SingleDynamic added {2} (enter)", new Object[] {getName(), trackingCount, serviceReference}, null );
boolean tracked = false;
@@ -721,7 +732,7 @@ public class DependencyManager<S, T> imp
this.refPair = refPair;
}
}
- else if ( isTrackerOpened() && !isOptional() )
+ else if ( isTrackerOpened() && cardinalityJustSatisfied( serviceCount ) )
{
tracked( trackingCount );
tracked = true;
@@ -772,7 +783,9 @@ public class DependencyManager<S, T> imp
trackingCount2 );
nextRefPair = tracked.values().iterator().next();
}
- if ( isOptional() || nextRefPair != null )
+
+ //n.b. we cannot use cardinalitySatisfied( serviceCount ) here as the call may come from an old tracker during target change.
+ if ( isEffectivelyOptional() || nextRefPair != null )
{
oldRefPair = this.refPair;
this.refPair = null;
@@ -782,7 +795,7 @@ public class DependencyManager<S, T> imp
deactivate = true; //required and no replacement service, deactivate
}
}
- else if ( !isOptional() && this.refPair == null && getTracker().isEmpty())
+ else if ( !cardinalitySatisfied( getTracker().getServiceCount() ) && this.refPair == null)
{
deactivate = true;
}
@@ -833,7 +846,7 @@ public class DependencyManager<S, T> imp
public boolean prebind()
{
RefPair<T> refPair = null;
- boolean success = m_dependencyMetadata.isOptional();
+ boolean success = cardinalitySatisfied( 0 );
AtomicInteger trackingCount = new AtomicInteger();
synchronized ( getTracker().tracked() )
{
@@ -904,7 +917,7 @@ public class DependencyManager<S, T> imp
return refPair;
}
- public void addedService( ServiceReference<T> serviceReference, RefPair<T> refPair, int trackingCount )
+ public void addedService( ServiceReference<T> serviceReference, RefPair<T> refPair, int trackingCount, int serviceCount )
{
m_componentManager.log( LogService.LOG_DEBUG, "dm {0} tracking {1} SingleStatic added {2} (enter)", new Object[] {getName(), trackingCount, serviceReference}, null );
this.trackingCount = trackingCount;
@@ -926,7 +939,7 @@ public class DependencyManager<S, T> imp
m_componentManager.log( LogService.LOG_DEBUG, "dm {0} tracking {1} SingleStatic active but new {2} is worse match than old {3}", new Object[] {getName(), trackingCount, refPair, this.refPair, }, null );
}
}
- else if (isTrackerOpened() && !isOptional() )
+ else if (isTrackerOpened() && cardinalityJustSatisfied( serviceCount ) )
{
m_componentManager.activateInternal( trackingCount );
}
@@ -964,7 +977,7 @@ public class DependencyManager<S, T> imp
final Object sync = getTracker().tracked();
synchronized (sync)
{
- reactivate = ( isActive() && refPair == this.refPair) || ( !isOptional() && getTracker().isEmpty());
+ reactivate = ( isActive() && refPair == this.refPair) || ( !cardinalitySatisfied( getTracker().getServiceCount() ));
if (!reactivate && refPair == this.refPair) {
this.refPair = null;
}
@@ -986,7 +999,7 @@ public class DependencyManager<S, T> imp
public boolean prebind()
{
- boolean success = m_dependencyMetadata.isOptional();
+ boolean success = cardinalitySatisfied( 0 );
if ( success || !getTracker().isEmpty() )
{
RefPair<T> refPair = null;
@@ -1089,7 +1102,7 @@ public class DependencyManager<S, T> imp
return null;
}
- public void addedService( ServiceReference<T> tServiceReference, RefPair<T> service, int trackingCount )
+ public void addedService( ServiceReference<T> tServiceReference, RefPair<T> service, int trackingCount, int serviceCount )
{
}
@@ -1114,7 +1127,21 @@ public class DependencyManager<S, T> imp
return m_dependencyMetadata.isOptional();
}
+ public boolean isEffectivelyOptional()
+ {
+ return m_minCardinality == 0;
+ }
+ private boolean cardinalitySatisfied(int serviceCount)
+ {
+ return m_minCardinality <= serviceCount;
+ }
+
+ private boolean cardinalityJustSatisfied(int serviceCount)
+ {
+ return m_minCardinality == serviceCount;
+ }
+
public boolean isMultiple()
{
return m_dependencyMetadata.isMultiple();
@@ -1763,8 +1790,10 @@ public class DependencyManager<S, T> imp
// 1. no target filter change
final String newTarget = ( String ) properties.get( m_dependencyMetadata.getTargetPropertyName() );
final String currentTarget = getTarget();
- if ( ( currentTarget == null && newTarget == null )
- || ( currentTarget != null && currentTarget.equals( newTarget ) ) )
+ int newMinimumCardinality = getMinimumCardinality( properties );
+ if ( m_minCardinality == newMinimumCardinality &&
+ ( ( currentTarget == null && newTarget == null )
+ || ( currentTarget != null && currentTarget.equals( newTarget ) ) ) )
{
// can update if target filter is not changed, since there is
// no change is service binding
@@ -1784,7 +1813,7 @@ public class DependencyManager<S, T> imp
// invariant: target filter change + dynamic policy
// 3. check optionality
- if ( m_dependencyMetadata.isOptional() )
+ if ( newMinimumCardinality == 0 )
{
// can update since even if no service matches the new filter, this
// makes no difference because the dependency is optional
@@ -1794,16 +1823,14 @@ public class DependencyManager<S, T> imp
// 4. check target services matching the new filter
ServiceReference<T>[] refs = getFrameworkServiceReferences( newTarget );
- if ( refs != null && refs.length > 0 )
+ if ( refs != null )
{
- // can update since there is at least on service matching the
- // new target filter and the services may be exchanged dynamically
- return true;
+ // Return whether there are enough target services
+ return newMinimumCardinality <= refs.length;
}
// invariant: target filter change + dynamic policy + no more matching service + required
- // 5. cannot dynamically update because the target filter results in
- // no more applicable services which is not acceptable
+ // 5. There are no services, and some are required.
return false;
}
@@ -1820,7 +1847,34 @@ public class DependencyManager<S, T> imp
*/
void setTargetFilter( Dictionary<String, Object> properties )
{
- setTargetFilter( ( String ) properties.get( m_dependencyMetadata.getTargetPropertyName() ) );
+ Integer minimumCardinality = getMinimumCardinality( properties );
+ setTargetFilter( ( String ) properties.get( m_dependencyMetadata.getTargetPropertyName() ),
+ minimumCardinality);
+ }
+
+ private int getMinimumCardinality(Dictionary<String, Object> properties)
+ {
+ Integer minimumCardinality = null;
+ try
+ {
+ minimumCardinality = Coercions.coerceToInteger( properties.get( m_dependencyMetadata.getMinCardinalityName()));
+ }
+ catch (ComponentException e)
+ {
+ m_componentManager.log( LogService.LOG_WARNING, "Invalid minimum cardinality property for dependency {0}: {1}", new Object[]
+ {getName(), e.getMessage()}, null );
+ }
+ if (minimumCardinality != null &&
+ (minimumCardinality < defaultMinimumCardinality( m_dependencyMetadata ) ||
+ (!m_dependencyMetadata.isMultiple() && minimumCardinality > 1 )))
+ {
+ minimumCardinality = null;
+ }
+ if ( minimumCardinality == null)
+ {
+ minimumCardinality = defaultMinimumCardinality( m_dependencyMetadata );
+ }
+ return minimumCardinality;
}
@@ -1834,7 +1888,7 @@ public class DependencyManager<S, T> imp
* @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)
+ private void setTargetFilter( String target, int minimumCardinality )
{
// if configuration does not set filter, use the value from metadata
if (target == null)
@@ -1848,6 +1902,7 @@ public class DependencyManager<S, T> imp
{getName(), m_tracker != null}, null );
if (m_tracker != null)
{
+ m_minCardinality = minimumCardinality;
return;
}
}
@@ -1907,7 +1962,9 @@ public class DependencyManager<S, T> imp
{getName(), initialActive, refMap}, null );
ServiceTracker<T, RefPair<T>> tracker = new ServiceTracker<T, RefPair<T>>( bundleContext, m_targetFilter, m_customizer, initialActive );
m_customizer.setTracker( tracker );
- // m_registered = true;
+ //set minimum cardinality
+ m_minCardinality = minimumCardinality;
+
tracker.open( m_componentManager.getTrackingCount() );
m_customizer.setTrackerOpened();
if ( oldTracker != null )
@@ -2017,4 +2074,5 @@ public class DependencyManager<S, T> imp
return false;
}
}
+
}
Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTracker.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTracker.java?rev=1560874&r1=1560873&r2=1560874&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTracker.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTracker.java Fri Jan 24 01:34:28 2014
@@ -865,15 +865,25 @@ public class ServiceTracker<S, T> {
* services.
* @since 1.5
*/
- public boolean isEmpty() {
- final Tracked t = tracked();
- if (t == null) { /* if ServiceTracker is not open */
- return true;
- }
- synchronized (t) {
- return t.isEmpty();
- }
- }
+ public boolean isEmpty() {
+ final Tracked t = tracked();
+ if (t == null) { /* if ServiceTracker is not open */
+ return true;
+ }
+ synchronized (t) {
+ return t.isEmpty();
+ }
+ }
+
+ public int getServiceCount() {
+ final Tracked t = tracked();
+ if (t == null) { /* if ServiceTracker is not open */
+ return 0;
+ }
+ synchronized (t) {
+ return t.size();
+ }
+ }
public boolean isActive() {
final Tracked t = tracked();
@@ -1171,6 +1181,7 @@ public class ServiceTracker<S, T> {
T object = null;
boolean becameUntracked = false;
int trackingCount = -1;
+ int serviceCount = -1;
/* Call customizer outside of synchronized region */
try {
object = customizerAdding(item, related);
@@ -1187,6 +1198,7 @@ public class ServiceTracker<S, T> {
*/
tracked.put( item, object );
trackingCount = modified(); /* increment modification count */
+ serviceCount = tracked.size();
notifyAll(); /* notify any waiters */
} else {
becameUntracked = true;
@@ -1207,7 +1219,7 @@ public class ServiceTracker<S, T> {
* let it propagate
*/
} else {
- customizerAdded( item, related, object, trackingCount );
+ customizerAdded( item, related, object, trackingCount, serviceCount );
}
}
@@ -1366,7 +1378,7 @@ public class ServiceTracker<S, T> {
*/
abstract T customizerAdding( final S item, final R related );
- abstract void customizerAdded( final S item, final R related, final T object, int trackingCount );
+ abstract void customizerAdded( final S item, final R related, final T object, int trackingCount, int serviceCount );
/**
* Call the specific customizer modified method. This method must not be
@@ -1476,8 +1488,8 @@ public class ServiceTracker<S, T> {
return customizer.addingService( item );
}
- final void customizerAdded( final ServiceReference<S> item, final ServiceEvent related, final T object, int trackingCount ) {
- customizer.addedService( item, object, trackingCount );
+ final void customizerAdded( final ServiceReference<S> item, final ServiceEvent related, final T object, int trackingCount, int serviceCount ) {
+ customizer.addedService( item, object, trackingCount, serviceCount );
}
/**
Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTrackerCustomizer.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTrackerCustomizer.java?rev=1560874&r1=1560873&r2=1560874&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTrackerCustomizer.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ServiceTrackerCustomizer.java Fri Jan 24 01:34:28 2014
@@ -69,7 +69,7 @@ public interface ServiceTrackerCustomize
*/
public T addingService( ServiceReference<S> reference );
- public void addedService( ServiceReference<S> reference, T service, int trackingCount );
+ public void addedService( ServiceReference<S> reference, T service, int trackingCount, int serviceCount );
/**
* A service tracked by the {@code ServiceTracker} has been modified.
@@ -92,8 +92,8 @@ public interface ServiceTrackerCustomize
* {@code ServiceTracker}.
*
* @param reference The reference to the service that has been removed.
- * @param service The service object for the specified referenced service.
- * @param trackingCount
+ * @param service The service object for the specified referenced service.
+ * @param trackingCount
*/
public void removedService( ServiceReference<S> reference, T service, int trackingCount );
Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java?rev=1560874&r1=1560873&r2=1560874&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java Fri Jan 24 01:34:28 2014
@@ -434,6 +434,11 @@ public class ReferenceMetadata
{
return getName() + ".target";
}
+
+ public String getMinCardinalityName()
+ {
+ return getName() + ".cardinality.minimum";
+ }
/**
Added: felix/trunk/scr/src/test/java/org/apache/felix/scr/integration/MinimumCardinalityTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/test/java/org/apache/felix/scr/integration/MinimumCardinalityTest.java?rev=1560874&view=auto
==============================================================================
--- felix/trunk/scr/src/test/java/org/apache/felix/scr/integration/MinimumCardinalityTest.java (added)
+++ felix/trunk/scr/src/test/java/org/apache/felix/scr/integration/MinimumCardinalityTest.java Fri Jan 24 01:34:28 2014
@@ -0,0 +1,142 @@
+/*
+ * 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.scr.integration;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.scr.integration.components.SimpleComponent;
+import org.apache.felix.scr.integration.components.SimpleServiceImpl;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.service.cm.Configuration;
+import org.osgi.util.tracker.ServiceTracker;
+
+@RunWith(JUnit4TestRunner.class)
+public class MinimumCardinalityTest extends ComponentTestBase
+{
+
+ private static final String pid = "MinimumCardinality";
+
+ static
+ {
+ descriptorFile = "/integration_test_min_cardinality.xml";
+ // uncomment to enable debugging of this test class
+// paxRunnerVmOption = DEBUG_VM_OPTION;
+ COMPONENT_PACKAGE = COMPONENT_PACKAGE;
+ }
+
+ @Test
+ public void testMinCardinality() throws Exception
+ {
+ ServiceTracker<SimpleComponent, SimpleComponent> tracker = new ServiceTracker<SimpleComponent, SimpleComponent>(bundleContext, SimpleComponent.class, null);
+ tracker.open();
+ //configuration-policy require
+ assertNull(tracker.getService());
+ onePresent( tracker, null );
+ onePresent( tracker, -1 );
+ onePresent( tracker, 2 );
+ onePresent( tracker, "-1" );
+ onePresent( tracker, 'c' );
+ onePresent( tracker, "2" );
+ onePresent( tracker, new int[] {4, 0} );
+ onePresent( tracker, null );
+
+ configureOne(1);
+ required(tracker, 1);
+ onePresent( tracker, null );
+
+ getConfigurationAdmin().getConfiguration( pid, null ).delete();
+ delay();
+ assertNull(tracker.getService());
+ manyPresent( tracker, null );
+ manyPresent( tracker, -1 );
+ manyPresent( tracker, "-1" );
+ manyPresent( tracker, new int[] {-4, 0} );
+ manyPresent( tracker, null );
+
+ configureMany(1);
+ required(tracker, 1);
+ configureMany(5);
+ required(tracker, 5);
+ manyPresent( tracker, null );
+ }
+
+ private void required(ServiceTracker<SimpleComponent, SimpleComponent> tracker, int count)
+ {
+ delay();
+ List<SimpleServiceImpl> services = new ArrayList<SimpleServiceImpl>();
+ for (int i = 0; i < count; i++)
+ {
+ assertNull("Expected no tracked with " + i + " services present, count " + count, tracker.getService());
+ services.add(SimpleServiceImpl.create( bundleContext, String.valueOf( i ) ));
+ }
+ assertNotNull(tracker.getService());
+ for (SimpleServiceImpl service: services)
+ {
+ service.drop();
+ assertNull(tracker.getService());
+ }
+ }
+
+ private void onePresent(ServiceTracker<SimpleComponent, SimpleComponent> tracker, Object value) throws IOException
+ {
+ configureOne(value);
+ delay();
+ assertNotNull(tracker.getService());
+ assertEquals(1, tracker.getServices().length);
+ }
+
+ private void configureOne(Object value ) throws IOException
+ {
+ configureTarget( "one.cardinality.minimum", value );
+ }
+
+ private void manyPresent(ServiceTracker<SimpleComponent, SimpleComponent> tracker, Object value) throws IOException
+ {
+ configureMany(value);
+ delay();
+ assertNotNull(tracker.getService());
+ assertEquals(1, tracker.getServices().length);
+ }
+
+ private void configureMany(Object value ) throws IOException
+ {
+ configureTarget( "many.cardinality.minimum", value );
+ }
+
+ private void configureTarget(final String targetKey, Object value) throws IOException
+ {
+ Hashtable<String, Object> props = new Hashtable<String, Object>();
+ if ( value != null )
+ {
+ props.put( targetKey, value );
+ }
+ Configuration config = getConfigurationAdmin().getConfiguration( pid, null );
+ config.update(props);
+ }
+
+}
Propchange: felix/trunk/scr/src/test/java/org/apache/felix/scr/integration/MinimumCardinalityTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: felix/trunk/scr/src/test/java/org/apache/felix/scr/integration/MinimumCardinalityTest.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: felix/trunk/scr/src/test/java/org/apache/felix/scr/integration/MinimumCardinalityTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: felix/trunk/scr/src/test/resources/integration_test_min_cardinality.xml
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/test/resources/integration_test_min_cardinality.xml?rev=1560874&view=auto
==============================================================================
--- felix/trunk/scr/src/test/resources/integration_test_min_cardinality.xml (added)
+++ felix/trunk/scr/src/test/resources/integration_test_min_cardinality.xml Fri Jan 24 01:34:28 2014
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+
+ <scr:component name="MinimumCardinality"
+ configuration-policy="require"
+ modified="configure">
+ <implementation class="org.apache.felix.scr.integration.components.SimpleComponent" />
+ <service>
+ <provide interface="org.apache.felix.scr.integration.components.SimpleComponent" />
+ </service>
+ <property name="service.pid" value="DynamicConfigurationComponentWithRequiredReference" />
+ <reference
+ name="one"
+ interface="org.apache.felix.scr.integration.components.SimpleService"
+ cardinality="0..1"
+ policy="dynamic"
+ bind="setSimpleService"
+ unbind="unsetSimpleService"
+ />
+ <reference
+ name="many"
+ interface="org.apache.felix.scr.integration.components.SimpleService"
+ cardinality="0..n"
+ policy="dynamic"
+ bind="bindSimpleService"
+ unbind="unbindSimpleService"
+ />
+ </scr:component>
+
+</components>
Propchange: felix/trunk/scr/src/test/resources/integration_test_min_cardinality.xml
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: felix/trunk/scr/src/test/resources/integration_test_min_cardinality.xml
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: felix/trunk/scr/src/test/resources/integration_test_min_cardinality.xml
------------------------------------------------------------------------------
svn:mime-type = text/xml