FELIX-3377 Commit my latest patch (FELIX-3377-4-fmeschbe.patch) including input from David Jencks (thanks alot).
- Activate, Modified, Deactivate methods may return Map to set service properties
- Namespace "http://felix.apache.org/xmlns/scr/v1.2.0-felix" is required for component
declaration to support this feature
- ExtComponentContext provides setServiceProperties method to explicitly set the
service registration properties at any time
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1336331 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/component/ExtComponentContext.java b/scr/src/main/java/org/apache/felix/scr/component/ExtComponentContext.java
index 858a999..b31b482 100644
--- a/scr/src/main/java/org/apache/felix/scr/component/ExtComponentContext.java
+++ b/scr/src/main/java/org/apache/felix/scr/component/ExtComponentContext.java
@@ -33,47 +33,23 @@
{
/**
- * Updates the service registration properties of the component
- * registered as a service. The effects are:
- * <ol>
- * <li>The properties read from the component descriptor are updated
- * with the values from the given dictionary</li>
- * <li>Configuration Admin properties are applied</li>
- * <li>The ServiceRegistration service properties are updated with the
- * result.</li>
- * </ol>
+ * Sets the service registration properties of the component
+ * registered as a service. If the component is not registered as
+ * a service, this method has no effect.
* <p>
- * Calling this method is does not cause a component reconfiguration as
- * would be caused by a Configuration update. Particularly the
- * configured modified method (if any) is not called as a result of
- * calling this method.
- * <p>
- * Please note:
- * <ul>
- * <li>The provided properties may overwrite or add properties to
- * the properties read from the component descriptor. It is not
- * possible to remove such descriptor properties</li>
- * <li>The provided properties are only valid for the livecycle of the
- * component instance. After reactivation of a component (and thus
- * creation of a new component instance) the properties are removed.
- * </li>
- * <li>If the component can be dynamically updated with configuration
- * these properties will influence such configuration.</li>
- * <li>Configuration is not updated in the Configuration Admin Service
- * when calling service</li>
- * <li>Properties provided with this method may still be overwriiten
- * with configuration provided by the Configuration Admin Service.</lI>
- * </ul>
- * <p>
- * If the component to which this context belongs is not registered as
- * a service, this method
+ * The <code>component.id</code> and <code>component.name</code>
+ * property are set by the Service Component Runtime and cannot be
+ * removed or replaced.
*
* @param properties properties to update the default component
- * properties with.
+ * properties with. If this is <code>null</code> or empty the
+ * default set of properties as defined in Section 112.6,
+ * Component Properties, are used as the service registration
+ * properties.
*
* @throws IllegalStateException if this method is called for a
* Component Factory component
*/
- void updateProperties( Dictionary properties );
+ void setServiceProperties( Dictionary properties );
}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/ActivateMethod.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/ActivateMethod.java
index 6b9af96..6fc381b 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/ActivateMethod.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/ActivateMethod.java
@@ -21,7 +21,6 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-
import org.apache.felix.scr.impl.manager.AbstractComponentManager;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.log.LogService;
@@ -70,7 +69,7 @@
{
if ( methods[i].getName().equals( getMethodName() ) && isSuitable( methods[i] ) )
{
- if ( accept( methods[i], acceptPrivate, acceptPackage ) )
+ if ( accept( methods[i], acceptPrivate, acceptPackage, returnValue() ) )
{
// check modifiers etc.
return methods[i];
@@ -143,12 +142,20 @@
return "activate";
}
-
- public boolean invoke( Object componentInstance, Object rawParameter, final boolean methodCallFailureResult )
+ protected boolean returnValue()
{
- return methodExists() && super.invoke( componentInstance, rawParameter, methodCallFailureResult );
+ // allow returning Map if declared as DS 1.2-Felix or newer
+ return isDS12Felix();
}
+ public MethodResult invoke(Object componentInstance, Object rawParameter, final MethodResult methodCallFailureResult)
+ {
+ if (methodExists())
+ {
+ return super.invoke(componentInstance, rawParameter, methodCallFailureResult);
+ }
+ return null;
+ }
/**
* Returns a method taking a single parameter of one of the
@@ -250,7 +257,6 @@
private final ComponentContext m_componentContext;
private final int m_reason;
-
public ActivatorParameter( ComponentContext componentContext, int reason )
{
this.m_componentContext = componentContext;
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/BaseMethod.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/BaseMethod.java
index 99a3c40..3abe587 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/BaseMethod.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/BaseMethod.java
@@ -24,7 +24,6 @@
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Map;
-
import org.apache.felix.scr.impl.manager.AbstractComponentManager;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
@@ -56,7 +55,6 @@
private State m_state;
-
protected BaseMethod( final AbstractComponentManager componentManager, final String methodName,
final Class componentClass )
{
@@ -94,6 +92,12 @@
}
+ protected final boolean isDS12Felix()
+ {
+ return getComponentManager().getComponentMetadata().isDS12Felix();
+ }
+
+
protected final String getMethodName()
{
return m_methodName;
@@ -145,11 +149,6 @@
* the class hierarchy is traversed until a method is found or the root
* of the class hierarchy is reached without finding a method.
*
- * @param targetClass The class in which to look for the method
- * @param acceptPrivate <code>true</code> if private methods should be
- * considered.
- * @param acceptPackage <code>true</code> if package private methods should
- * be considered.
* @return The requested method or <code>null</code> if no acceptable method
* can be found in the target class or any super class.
* @throws InvocationTargetException If an unexpected Throwable is caught
@@ -185,7 +184,7 @@
{
// log and return null
getComponentManager().log( LogService.LOG_ERROR,
- "DependencyManager : Suitable but non-accessible method found in class {0}", new Object[]
+ "findMethod: Suitable but non-accessible method found in class {0}", new Object[]
{ targetClass.getName() }, null );
break;
}
@@ -216,15 +215,16 @@
final boolean acceptPackage ) throws SuitableMethodNotAccessibleException, InvocationTargetException;
- private boolean invokeMethod( final Object componentInstance, final Object rawParameter )
+ private MethodResult invokeMethod( final Object componentInstance, final Object rawParameter )
throws InvocationTargetException
{
try
{
if ( componentInstance != null )
{
- final Object[] params = getParameters( m_method, rawParameter );
- m_method.invoke( componentInstance, params );
+ final Object[] params = getParameters(m_method, rawParameter);
+ Object result = m_method.invoke(componentInstance, params);
+ return new MethodResult((m_method.getReturnType() != Void.TYPE), (Map) result);
}
else
{
@@ -236,7 +236,7 @@
catch ( IllegalStateException ise )
{
getComponentManager().log( LogService.LOG_DEBUG, ise.getMessage(), null );
- return false;
+ return null;
}
catch ( IllegalAccessException ex )
{
@@ -256,9 +256,18 @@
}
// assume success (also if the mehotd is not available or accessible)
- return true;
+ return MethodResult.VOID; // TODO: or null ??
}
+ protected void processResult( Object configResults, Object result, Method method )
+ {
+ //no op
+ }
+
+ protected boolean returnValue()
+ {
+ return false;
+ }
/**
* Returns the parameter array created from the <code>rawParameter</code>
@@ -311,7 +320,7 @@
Method method = clazz.getDeclaredMethod( name, parameterTypes );
// accept public and protected methods only and ensure accessibility
- if ( accept( method, acceptPrivate, acceptPackage ) )
+ if ( accept( method, acceptPrivate, acceptPackage, returnValue() ) )
{
return method;
}
@@ -384,15 +393,16 @@
* This method is package private for unit testing purposes. It is not
* meant to be called from client code.
*
+ *
* @param method The method to check
* @param acceptPrivate Whether a private method is acceptable
* @param acceptPackage Whether a package private method is acceptable
- * @return
+ * @param allowReturnValue whether the method can return a value (to update service registration properties)
+ * @return whether the method is acceptable
*/
- protected static boolean accept( Method method, boolean acceptPrivate, boolean acceptPackage )
+ static boolean accept( Method method, boolean acceptPrivate, boolean acceptPackage, boolean allowReturnValue )
{
- // method must be void
- if ( Void.TYPE != method.getReturnType() )
+ if (!(Void.TYPE == method.getReturnType() || (MAP_CLASS == method.getReturnType() && allowReturnValue)))
{
return false;
}
@@ -455,6 +465,8 @@
* Calls the declared method on the given component with the provided
* method call arguments.
*
+ *
+ *
* @param componentInstance The component instance on which to call the
* method
* @param rawParameter The parameter container providing the actual
@@ -468,8 +480,8 @@
* <code>methodCallFailureResult</code> is returned if the method was
* found and called, but the method threw an exception.
*/
- public boolean invoke( final Object componentInstance, final Object rawParameter,
- final boolean methodCallFailureResult )
+ public MethodResult invoke( final Object componentInstance, final Object rawParameter,
+ final MethodResult methodCallFailureResult )
{
try
{
@@ -493,7 +505,7 @@
private static interface State
{
- boolean invoke( final BaseMethod baseMethod, final Object componentInstance, final Object rawParameter )
+ MethodResult invoke( final BaseMethod baseMethod, final Object componentInstance, final Object rawParameter )
throws InvocationTargetException;
@@ -506,9 +518,9 @@
private static final State INSTANCE = new NotApplicable();
- public boolean invoke( final BaseMethod baseMethod, final Object componentInstance, final Object rawParameter )
+ public MethodResult invoke( final BaseMethod baseMethod, final Object componentInstance, final Object rawParameter )
{
- return true;
+ return MethodResult.VOID;
}
@@ -545,7 +557,7 @@
}
- public boolean invoke( final BaseMethod baseMethod, final Object componentInstance, final Object rawParameter )
+ public MethodResult invoke( final BaseMethod baseMethod, final Object componentInstance, final Object rawParameter )
throws InvocationTargetException
{
resolve( baseMethod );
@@ -565,14 +577,14 @@
private static final State INSTANCE = new NotFound();
- public boolean invoke( final BaseMethod baseMethod, final Object componentInstance, final Object rawParameter )
+ public MethodResult invoke( final BaseMethod baseMethod, final Object componentInstance, final Object rawParameter )
{
// 112.3.1 If the method is not found , SCR must log an error
// message with the log service, if present, and ignore the
// method
baseMethod.getComponentManager().log( LogService.LOG_ERROR, "{0} method [{1}] not found", new Object[]
{ baseMethod.getMethodNamePrefix(), baseMethod.getMethodName() }, null );
- return false;
+ return null;
}
@@ -587,7 +599,7 @@
private static final State INSTANCE = new Resolved();
- public boolean invoke( final BaseMethod baseMethod, final Object componentInstance, final Object rawParameter )
+ public MethodResult invoke( final BaseMethod baseMethod, final Object componentInstance, final Object rawParameter )
throws InvocationTargetException
{
baseMethod.getComponentManager().log( LogService.LOG_DEBUG, "invoking {0}: {1}", new Object[]
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/BindMethod.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/BindMethod.java
index a6d1c8f..a542ba3 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/BindMethod.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/BindMethod.java
@@ -195,7 +195,7 @@
if ( suitableMethodNotAccessible )
{
getComponentManager().log( LogService.LOG_ERROR,
- "DependencyManager : Suitable but non-accessible method found in class {0}", new Object[]
+ "doFindMethod: Suitable but non-accessible method found in class {0}", new Object[]
{ targetClass.getName() }, null );
throw new SuitableMethodNotAccessibleException();
}
@@ -428,7 +428,7 @@
// reference's interface attribute
if ( theParameter.isAssignableFrom( parameterClass ) )
{
- if ( accept( method, acceptPrivate, acceptPackage ) )
+ if ( accept( method, acceptPrivate, acceptPackage, false ) )
{
return method;
}
@@ -521,7 +521,7 @@
// parameters must be refclass,map
if ( parameters[0].isAssignableFrom( parameterClass ) && parameters[1] == MAP_CLASS )
{
- if ( accept( method, acceptPrivate, acceptPackage ) )
+ if ( accept( method, acceptPrivate, acceptPackage, false ) )
{
return method;
}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/DeactivateMethod.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/DeactivateMethod.java
index 4c464b9..211454e 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/DeactivateMethod.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/DeactivateMethod.java
@@ -46,4 +46,5 @@
{
return "deactivate";
}
+
}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/MethodResult.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/MethodResult.java
new file mode 100644
index 0000000..4262c4c
--- /dev/null
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/MethodResult.java
@@ -0,0 +1,63 @@
+/*
+ * 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.util.Map;
+
+/**
+ * The <code>MethodResult</code> conveys the return value of one of the
+ * activate, modify, and deactivate methods.
+ * <p>
+ * Note that the method returning <code>null</code> or being defined as
+ * <code>void</code> is not the same thing. If the method returns
+ * <code>null</code> an instance of this class is returned whose
+ * {@link #getResult()} method returns <code>null</code>. If the method is
+ * defined as <code>void</code> the special instance {@link #VOID} is returned.
+ */
+public class MethodResult
+{
+
+ /**
+ * Predefined instance indicating a successfull call to a void method.
+ */
+ public static final MethodResult VOID = new MethodResult(false, null);
+
+ /**
+ * The actual result from the method, which may be <code>null</code>.
+ */
+ private final Map result;
+
+ private final boolean hasResult;
+
+ MethodResult(final boolean hasResult, final Map result)
+ {
+ this.hasResult = hasResult;
+ this.result = result;
+ }
+
+ public boolean hasResult()
+ {
+ return hasResult;
+ }
+
+ public Map getResult()
+ {
+ return result;
+ }
+}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/AbstractComponentManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/AbstractComponentManager.java
index 9717de6..08f7f93 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/AbstractComponentManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/AbstractComponentManager.java
@@ -797,7 +797,7 @@
public abstract Dictionary getProperties();
- public abstract void resetComponentProperties( Dictionary properties );
+ public abstract void setServiceProperties(Dictionary serviceProperties, boolean updateServiceRegistration);
/**
* Returns the subset of component properties to be used as service
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentContextImpl.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentContextImpl.java
index eb81753..0b0aa76 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentContextImpl.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentContextImpl.java
@@ -27,7 +27,6 @@
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.ComponentInstance;
@@ -135,9 +134,9 @@
//---------- Speculative MutableProperties interface ------------------------------
- public void updateProperties(Dictionary properties)
+ public void setServiceProperties(Dictionary properties)
{
- getComponentManager().resetComponentProperties(properties);
+ getComponentManager().setServiceProperties(properties, true);
}
}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentFactoryImpl.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentFactoryImpl.java
index e02d3e8..7039d17 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentFactoryImpl.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/ComponentFactoryImpl.java
@@ -209,7 +209,8 @@
return props;
}
- public void resetComponentProperties(Dictionary properties) {
+ public void setServiceProperties(Dictionary serviceProperties, boolean updateServiceRegistration)
+ {
throw new IllegalStateException( "ComponentFactory service properties are immutable" );
}
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 f89c2fb..fe25047 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
@@ -31,6 +31,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.MethodResult;
import org.apache.felix.scr.impl.helper.UnbindMethod;
import org.apache.felix.scr.impl.helper.UpdatedMethod;
import org.apache.felix.scr.impl.metadata.ReferenceMetadata;
@@ -1059,7 +1060,7 @@
{
return getService( ref );
}
- }, true );
+ }, MethodResult.VOID ) != null;
}
// Concurrency Issue: The component instance still exists but
@@ -1112,6 +1113,7 @@
{
// The updated method is only invoked if the implementation object is not
// null. This is valid for both immediate and delayed components
+ //TODO should updated methods be able to change config properties?
if ( m_componentInstance != null )
{
m_updated.invoke( m_componentInstance, new BindMethod.Service()
@@ -1126,7 +1128,7 @@
{
return getService( ref );
}
- }, true );
+ }, MethodResult.VOID );
}
else
{
@@ -1167,7 +1169,7 @@
{
return getService( ref );
}
- }, true );
+ }, MethodResult.VOID );
}
else
{
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 54e61e7..437bdb6 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
@@ -23,11 +23,14 @@
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import org.apache.felix.scr.impl.BundleComponentActivator;
import org.apache.felix.scr.impl.config.ComponentHolder;
import org.apache.felix.scr.impl.helper.ActivateMethod;
+import org.apache.felix.scr.impl.helper.ActivateMethod.ActivatorParameter;
import org.apache.felix.scr.impl.helper.DeactivateMethod;
+import org.apache.felix.scr.impl.helper.MethodResult;
import org.apache.felix.scr.impl.helper.ModifiedMethod;
import org.apache.felix.scr.impl.metadata.ComponentMetadata;
import org.apache.felix.scr.impl.metadata.ReferenceMetadata;
@@ -71,7 +74,7 @@
// properties supplied ot ExtComponentContext.updateProperties
// null if properties are not to be overwritten
- private Dictionary m_propertiesOverwrite;
+ private Dictionary m_serviceProperties;
// the component properties from the Configuration Admin Service
// this is null, if none exist or none are provided
@@ -140,7 +143,7 @@
m_implementationObject = null;
m_componentContext = null;
m_properties = null;
- m_propertiesOverwrite = null;
+ m_serviceProperties = null;
}
@@ -228,8 +231,9 @@
}
// 4. Call the activate method, if present
- if ( !m_activateMethod.invoke( implementationObject,
- new ActivateMethod.ActivatorParameter( componentContext, 1 ), false ) )
+ final MethodResult result = m_activateMethod.invoke(implementationObject, new ActivatorParameter(
+ componentContext, 1), null);
+ if (result == null)
{
// 112.5.8 If the activate method throws an exception, SCR must log an error message
// containing the exception with the Log Service and activation fails
@@ -242,6 +246,10 @@
return null;
}
+ else if (result.hasResult())
+ {
+ setServiceProperties(result.getResult(), true);
+ }
return implementationObject;
}
@@ -262,8 +270,12 @@
// don't care for the result, the error (acccording to 112.5.12 If the deactivate
// method throws an exception, SCR must log an error message containing the
// exception with the Log Service and continue) has already been logged
- m_deactivateMethod.invoke( implementationObject, new ActivateMethod.ActivatorParameter( componentContext,
- reason ), true );
+ final MethodResult result = m_deactivateMethod.invoke( implementationObject, new ActivatorParameter( componentContext,
+ reason ), null );
+ if (result != null && result.hasResult())
+ {
+ setServiceProperties(result.getResult(), true);
+ }
// 2. Unbind any bound services
Iterator it = getReversedDependencyManagers();
@@ -348,16 +360,13 @@
}
}
- // 3. overwrite as per ExtComponentContext.updateProperties
- copyTo( props, m_propertiesOverwrite );
-
- // 4. overlay with Configuration Admin properties
+ // 3. overlay with Configuration Admin properties
copyTo( props, m_configurationProperties );
- // 5. copy any component factory properties, not supported yet
+ // 4. copy any component factory properties, not supported yet
copyTo( props, m_factoryProperties );
- // 6. 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( getId() ) );
@@ -368,17 +377,69 @@
}
- public void resetComponentProperties( Dictionary properties )
+ public void setServiceProperties(Map serviceProperties, boolean updateServiceRegistration)
{
- m_propertiesOverwrite = copyTo( null, properties );
- m_properties = null;
- Dictionary serviceProperties = getServiceProperties();
- if ( getServiceRegistration() != null )
+ Dictionary serviceProps = (serviceProperties == null) ? null : new Hashtable(serviceProperties);
+ setServiceProperties(serviceProps, updateServiceRegistration);
+ }
+
+ public void setServiceProperties(Dictionary serviceProperties, boolean updateServiceRegistration)
+ {
+ if ( serviceProperties == null || serviceProperties.isEmpty() )
{
- getServiceRegistration().setProperties( serviceProperties );
+ m_serviceProperties = null;
+ }
+ else
+ {
+ m_serviceProperties = copyTo(null, serviceProperties);
+ // set component.name and component.id
+ m_serviceProperties.put( ComponentConstants.COMPONENT_NAME, getComponentMetadata().getName() );
+ m_serviceProperties.put( ComponentConstants.COMPONENT_ID, new Long( getId() ) );
+ }
+
+ if (updateServiceRegistration)
+ {
+ updateServiceRegistration();
}
}
+ public Dictionary getServiceProperties() {
+ if ( m_serviceProperties != null )
+ {
+ return m_serviceProperties;
+ }
+ return super.getServiceProperties();
+ }
+
+ private void updateServiceRegistration()
+ {
+ ServiceRegistration sr = getServiceRegistration();
+ if (sr != null)
+ {
+ try
+ {
+ // Don't propagate if service properties did not change.
+ final Dictionary regProps = getServiceProperties();
+ if (!servicePropertiesMatches(sr, regProps))
+ {
+ sr.setProperties(regProps);
+ }
+ }
+ catch (IllegalStateException ise)
+ {
+ // service has been unregistered asynchronously, ignore
+ }
+ catch (IllegalArgumentException iae)
+ {
+ log(LogService.LOG_ERROR,
+ "Unexpected configuration property problem when updating service registration", iae);
+ }
+ catch (Throwable t)
+ {
+ log(LogService.LOG_ERROR, "Unexpected problem when updating service registration", t);
+ }
+ }
+ }
/**
* Called by the Configuration Admin Service to update the component with
@@ -487,8 +548,9 @@
// invariant: modify method existing and no static bound service changes
// 4. call method (nothing to do when failed, since it has already been logged)
- if ( !m_modifyMethod.invoke( getInstance(), new ActivateMethod.ActivatorParameter( m_componentContext, -1 ),
- true ) )
+ final MethodResult result = m_modifyMethod.invoke(getInstance(),
+ new ActivatorParameter(m_componentContext, -1), null);
+ if (result == null)
{
// log an error if the declared method cannot be found
log( LogService.LOG_ERROR, "Declared modify method ''{0}'' cannot be found, configuring by reactivation",
@@ -496,6 +558,10 @@
{ getComponentMetadata().getModified() }, null );
return false;
}
+ else if (result.hasResult())
+ {
+ setServiceProperties(result.getResult(), false);
+ }
// 5. update the target filter on the services now, this may still
// result in unsatisfied dependencies, in which case we abort
@@ -510,34 +576,7 @@
}
// 6. update service registration properties
- ServiceRegistration sr = getServiceRegistration();
- if ( sr != null )
- {
- try
- {
- // Don't propagate if service properties did not change.
- final Dictionary regProps = getServiceProperties();
- if ( !servicePropertiesMatches( sr, regProps ) )
- {
- sr.setProperties( regProps );
- }
- }
- catch ( IllegalStateException ise )
- {
- // service has been unregistered asynchronously, ignore
- }
- catch ( IllegalArgumentException iae )
- {
- log( LogService.LOG_ERROR,
- "Unexpected configuration property problem when updating service registration",
- iae );
- }
- catch ( Throwable t )
- {
- log( LogService.LOG_ERROR, "Unexpected problem when updating service registration",
- t );
- }
- }
+ updateServiceRegistration();
// 7. everything set and done, the component has been udpated
return true;
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java b/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
index a2ded08..3b524a7 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
@@ -364,7 +364,7 @@
/**
* Returns <code>true</code> if the metadata declaration has used the
- * Declarative Services version 1.1-felixnamespace or a later namespace.
+ * Declarative Services version 1.1-felix namespace or a later namespace.
*
* @see <a href="https://issues.apache.org/jira/browse/FELIX-1893">FELIX-1893</a>
*/
@@ -375,6 +375,28 @@
/**
+ * Returns <code>true</code> if the metadata declaration has used the
+ * Declarative Services version 1.2 namespace or a later namespace.
+ */
+ public boolean isDS12()
+ {
+ return getNamespaceCode() >= XmlHandler.DS_VERSION_1_2;
+ }
+
+
+ /**
+ * Returns <code>true</code> if the metadata declaration has used the
+ * Declarative Services version 1.2-felix namespace or a later namespace.
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/FELIX-3377">FELIX-3377</a>
+ */
+ public boolean isDS12Felix()
+ {
+ return getNamespaceCode() >= XmlHandler.DS_VERSION_1_2_FELIX;
+ }
+
+
+ /**
* Returns the name of the component
*
* @return A string containing the name of the component
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/metadata/XmlHandler.java b/scr/src/main/java/org/apache/felix/scr/impl/metadata/XmlHandler.java
index 3df798f..1e18a91 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/metadata/XmlHandler.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/metadata/XmlHandler.java
@@ -54,6 +54,12 @@
// Namespace URI of DS 1.1-felix (see FELIX-1893)
public static final String NAMESPACE_URI_1_1_FELIX = "http://felix.apache.org/xmlns/scr/v1.1.0-felix";
+ // Namespace URI of DS 1.2
+ public static final String NAMESPACE_URI_1_2 = "http://www.osgi.org/xmlns/scr/v1.2.0";
+
+ // Namespace URI of DS 1.2-felix (see FELIX-3377)
+ public static final String NAMESPACE_URI_1_2_FELIX = "http://felix.apache.org/xmlns/scr/v1.2.0-felix";
+
// namespace code for non-DS namespace
public static final int DS_VERSION_NONE = -1;
@@ -66,6 +72,12 @@
// namespace code for the DS 1.1-felix specification
public static final int DS_VERSION_1_1_FELIX = 2;
+ // namespace code for the DS 1.2 specification
+ public static final int DS_VERSION_1_2 = 3;
+
+ // namespace code for the DS 1.1-felix specification
+ public static final int DS_VERSION_1_2_FELIX = 4;
+
// mapping of namespace URI to namespace code
private static final Map NAMESPACE_CODE_MAP;
@@ -103,6 +115,8 @@
NAMESPACE_CODE_MAP.put( NAMESPACE_URI, new Integer( DS_VERSION_1_0 ) );
NAMESPACE_CODE_MAP.put( NAMESPACE_URI_1_1, new Integer( DS_VERSION_1_1 ) );
NAMESPACE_CODE_MAP.put( NAMESPACE_URI_1_1_FELIX, new Integer( DS_VERSION_1_1_FELIX ) );
+ NAMESPACE_CODE_MAP.put( NAMESPACE_URI_1_2, new Integer( DS_VERSION_1_2 ) );
+ NAMESPACE_CODE_MAP.put( NAMESPACE_URI_1_2_FELIX, new Integer( DS_VERSION_1_2_FELIX ) );
}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/helper/ActivateMethodTest.java b/scr/src/test/java/org/apache/felix/scr/impl/helper/ActivateMethodTest.java
index 0b74bba..1bcd45a 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/helper/ActivateMethodTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/helper/ActivateMethodTest.java
@@ -55,7 +55,7 @@
{
super.setUp();
- m_ctx = (ComponentContext) EasyMock.createNiceMock( ComponentContext.class );
+ m_ctx = (ComponentContext) EasyMock.createNiceMock(ComponentContext.class);
EasyMock.expect( m_ctx.getProperties() ).andReturn( new Hashtable() ).anyTimes();
EasyMock.replay( new Object[]
{ m_ctx } );
@@ -272,7 +272,7 @@
};
ImmediateComponentManager icm = new ImmediateComponentManager( null, null, metadata );
ActivateMethod am = new ActivateMethod( icm, methodName, methodName != null, obj.getClass() );
- am.invoke( obj, new ActivateMethod.ActivatorParameter( m_ctx, -1 ), false );
+ am.invoke( obj, new ActivateMethod.ActivatorParameter( m_ctx, -1 ), null );
Method m = get(am, "m_method");
assertNotNull( m );
assertEquals( methodName, m.getName() );
@@ -300,7 +300,7 @@
};
ImmediateComponentManager icm = new ImmediateComponentManager( null, null, metadata );
ActivateMethod am = new ActivateMethod( icm, methodName, methodName != null, obj.getClass() );
- am.invoke( obj, new ActivateMethod.ActivatorParameter( m_ctx, -1 ), false );
+ am.invoke( obj, new ActivateMethod.ActivatorParameter( m_ctx, -1 ), null );
assertNull( get( am, "m_method" ) );
assertNull( obj.getCalledMethod() );
}
@@ -310,7 +310,7 @@
throws NoSuchMethodException
{
Method method = ACCEPT_METHOD_CLASS.getDeclaredMethod( methodName, null );
- boolean accepted = BaseMethod.accept( method, acceptPrivate, acceptPackage );
+ boolean accepted = BaseMethod.accept( method, acceptPrivate, acceptPackage, false );
assertEquals( expected, accepted );
}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/helper/BindMethodTest.java b/scr/src/test/java/org/apache/felix/scr/impl/helper/BindMethodTest.java
index 0a9aead..4ad5c3f 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/helper/BindMethodTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/helper/BindMethodTest.java
@@ -441,7 +441,7 @@
ImmediateComponentManager icm = new ImmediateComponentManager( null, null, metadata );
BindMethod bm = new BindMethod( icm, methodName, component.getClass(), "reference",
FakeService.class.getName() );
- bm.invoke( component, m_service, true );
+ bm.invoke( component, m_service, null );
assertEquals( expectCallPerformed, component.callPerformed );
}
}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/metadata/XmlHandlerTest.java b/scr/src/test/java/org/apache/felix/scr/impl/metadata/XmlHandlerTest.java
index 9fa9864..bd32067 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/metadata/XmlHandlerTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/metadata/XmlHandlerTest.java
@@ -106,7 +106,7 @@
final List metadataList = readMetadataFromString( "<scr:component xmlns:scr=\"http://www.osgi.org/xmlns/scr/v1.1.0\" name=\"n\" ><implementation class=\"n\"/></scr:component>" );
assertEquals( "1 Descriptor expected", 1, metadataList.size() );
final ComponentMetadata metadata = ( ComponentMetadata ) metadataList.get( 0 );
- assertEquals( "Expect NS 1.0.0", XmlHandler.DS_VERSION_1_1, metadata.getNamespaceCode() );
+ assertEquals( "Expect NS 1.1.0", XmlHandler.DS_VERSION_1_1, metadata.getNamespaceCode() );
}
@@ -115,7 +115,25 @@
final List metadataList = readMetadataFromString( "<scr:component xmlns:scr=\"http://felix.apache.org/xmlns/scr/v1.1.0-felix\" name=\"n\" ><implementation class=\"n\"/></scr:component>" );
assertEquals( "1 Descriptor expected", 1, metadataList.size() );
final ComponentMetadata metadata = ( ComponentMetadata ) metadataList.get( 0 );
- assertEquals( "Expect NS 1.0.0", XmlHandler.DS_VERSION_1_1_FELIX, metadata.getNamespaceCode() );
+ assertEquals( "Expect NS 1.1.0-felix", XmlHandler.DS_VERSION_1_1_FELIX, metadata.getNamespaceCode() );
+ }
+
+
+ public void test_namespace_1_2_0() throws Exception
+ {
+ final List metadataList = readMetadataFromString( "<scr:component xmlns:scr=\"http://www.osgi.org/xmlns/scr/v1.2.0\" name=\"n\" ><implementation class=\"n\"/></scr:component>" );
+ assertEquals( "1 Descriptor expected", 1, metadataList.size() );
+ final ComponentMetadata metadata = ( ComponentMetadata ) metadataList.get( 0 );
+ assertEquals( "Expect NS 1.2.0", XmlHandler.DS_VERSION_1_2, metadata.getNamespaceCode() );
+ }
+
+
+ public void test_namespace_1_2_0_felix() throws Exception
+ {
+ final List metadataList = readMetadataFromString( "<scr:component xmlns:scr=\"http://felix.apache.org/xmlns/scr/v1.2.0-felix\" name=\"n\" ><implementation class=\"n\"/></scr:component>" );
+ assertEquals( "1 Descriptor expected", 1, metadataList.size() );
+ final ComponentMetadata metadata = ( ComponentMetadata ) metadataList.get( 0 );
+ assertEquals( "Expect NS 1.2.0-felix", XmlHandler.DS_VERSION_1_2_FELIX, metadata.getNamespaceCode() );
}
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/ComponentConfigurationTest.java b/scr/src/test/java/org/apache/felix/scr/integration/ComponentConfigurationTest.java
index 318ce49..b589301 100644
--- a/scr/src/test/java/org/apache/felix/scr/integration/ComponentConfigurationTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/integration/ComponentConfigurationTest.java
@@ -35,7 +35,7 @@
static
{
// uncomment to enable debugging of this test class
- // paxRunnerVmOption = DEBUG_VM_OPTION;
+ // paxRunnerVmOption = DEBUG_VM_OPTION;
}
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/MutablePropertiesTest.java b/scr/src/test/java/org/apache/felix/scr/integration/MutablePropertiesTest.java
index e406c01..bc4bb61 100644
--- a/scr/src/test/java/org/apache/felix/scr/integration/MutablePropertiesTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/integration/MutablePropertiesTest.java
@@ -30,6 +30,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
@@ -40,20 +41,22 @@
static
{
// uncomment to enable debugging of this test class
-// paxRunnerVmOption = DEBUG_VM_OPTION;
+ // paxRunnerVmOption = DEBUG_VM_OPTION;
descriptorFile = "/integration_test_mutable_properties.xml";
}
@Test
- public void test_mutable_properties()
+ public void test_mutable_properties() throws InvalidSyntaxException
{
final Component component = findComponentByName( "components.mutable.properties" );
TestCase.assertNotNull( component );
TestCase.assertEquals( Component.STATE_REGISTERED, component.getState() );
- ServiceReference serviceReference = bundleContext.getServiceReference( MutatingService.class.getName() );
+ ServiceReference[] serviceReferences = bundleContext.getServiceReferences( MutatingService.class.getName(), "(service.pid=components.mutable.properties)" );
+ TestCase.assertEquals( 1, serviceReferences.length );
+ ServiceReference serviceReference = serviceReferences[0];
checkProperties( serviceReference, 8, "otherValue", "p1", "p2" );
//update theValue
@@ -62,20 +65,52 @@
TestCase.assertEquals( Component.STATE_ACTIVE, component.getState() );
Dictionary d = new Hashtable(Collections.singletonMap( PROP_NAME, "anotherValue" ));
s.updateProperties(d);
- checkProperties(serviceReference, 8, "anotherValue", "p1", "p2");
+ checkProperties(serviceReference, 5, "anotherValue", "p1", "p2");
//configure with configAdmin
configure( "components.mutable.properties" );
delay();
- checkProperties(serviceReference, 8, PROP_NAME, "p1", "p2");
+ //no change
+ checkProperties(serviceReference, 5, "anotherValue", "p1", "p2");
- //check that a property from config admin can't be changed
+ //check that removing config switches back to defaults modified by config admin
+ s.updateProperties(null);
+ checkProperties( serviceReference, 8, "theValue", "p1", "p2" );
+
+ bundleContext.ungetService(serviceReference);
+ }
+
+ @Test
+ public void test_mutable_properties_returned() throws InvalidSyntaxException
+ {
+ final Component component = findComponentByName( "components.mutable.properties2" );
+ TestCase.assertNotNull( component );
+ TestCase.assertEquals( Component.STATE_REGISTERED, component.getState() );
+
+ ServiceReference[] serviceReferences = bundleContext.getServiceReferences( MutatingService.class.getName(), "(service.pid=components.mutable.properties2)" );
+ TestCase.assertEquals( 1, serviceReferences.length );
+ ServiceReference serviceReference = serviceReferences[0];
+ checkProperties( serviceReference, 8, "otherValue", "p1", "p2" );
+
+ //update theValue
+ MutatingService s = ( MutatingService ) bundleContext.getService( serviceReference );
+ Assert.assertNotNull(s);
+ checkProperties( serviceReference, 8, "anotherValue1", "p1", "p2" );
+ TestCase.assertEquals( Component.STATE_ACTIVE, component.getState() );
+ Dictionary d = new Hashtable(Collections.singletonMap( PROP_NAME, "anotherValue" ));
s.updateProperties(d);
- checkProperties(serviceReference, 8, PROP_NAME, "p1", "p2");
+ checkProperties(serviceReference, 5, "anotherValue", "p1", "p2");
- //check that another one can
- s.updateProperties(new Hashtable(Collections.singletonMap( "p1", "changed" )));
- checkProperties(serviceReference, 8, PROP_NAME, "changed", "p2");
+ //configure with configAdmin
+ configure( "components.mutable.properties2" );
+ delay();
+ delay();
+ //no change
+ checkProperties(serviceReference, 8, "anotherValue2", "p1", "p2");
+
+ //check that removing config switches back to defaults modified by config admin
+ s.updateProperties(null);
+ checkProperties( serviceReference, 8, "theValue", "p1", "p2" );
bundleContext.ungetService(serviceReference);
}
@@ -83,8 +118,10 @@
private void checkProperties(ServiceReference serviceReference, int count, String otherValue, String p1, String p2) {
Assert.assertEquals("wrong property count", count, serviceReference.getPropertyKeys().length);
Assert.assertEquals(otherValue, serviceReference.getProperty(PROP_NAME));
- Assert.assertEquals(p1, serviceReference.getProperty("p1"));
- Assert.assertEquals(p2, serviceReference.getProperty("p2"));
+ if ( count > 5 ) {
+ Assert.assertEquals(p1, serviceReference.getProperty("p1"));
+ Assert.assertEquals(p2, serviceReference.getProperty("p2"));
+ }
}
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/MutatingServiceImpl.java b/scr/src/test/java/org/apache/felix/scr/integration/components/MutatingServiceImpl.java
index 6ae2ff6..faf9554 100644
--- a/scr/src/test/java/org/apache/felix/scr/integration/components/MutatingServiceImpl.java
+++ b/scr/src/test/java/org/apache/felix/scr/integration/components/MutatingServiceImpl.java
@@ -20,6 +20,8 @@
import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
import org.apache.felix.scr.component.ExtComponentContext;
import org.osgi.service.component.ComponentContext;
@@ -39,8 +41,30 @@
}
+ private Map activateMutate( ComponentContext activateContext )
+ {
+ this.activateContext = activateContext;
+ Map result = new Hashtable( (Map )activateContext.getProperties() );
+ result.put( "theValue", "anotherValue1");
+ return result;
+ }
+
+ private Map modifiedMutate( ComponentContext activateContext )
+ {
+ Map result = new Hashtable( (Map )activateContext.getProperties() );
+ result.put( "theValue", "anotherValue2");
+ return result;
+ }
+
+ private Map deactivateMutate( ComponentContext activateContext )
+ {
+ Map result = new Hashtable( (Map )activateContext.getProperties() );
+ result.put( "theValue", "anotherValue3");
+ return result;
+ }
+
public void updateProperties(Dictionary changes) {
- ((ExtComponentContext)activateContext).updateProperties(changes);
+ ((ExtComponentContext)activateContext).setServiceProperties(changes);
}
}
diff --git a/scr/src/test/resources/integration_test_mutable_properties.xml b/scr/src/test/resources/integration_test_mutable_properties.xml
index 5dc0473..d9052cf 100644
--- a/scr/src/test/resources/integration_test_mutable_properties.xml
+++ b/scr/src/test/resources/integration_test_mutable_properties.xml
@@ -19,7 +19,7 @@
-->
<components>
<scr:component name="components.mutable.properties"
- xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
+ xmlns:scr="http://felix.apache.org/xmlns/scr/v1.2.0-felix"
enabled="true"
configuration-policy="optional"
activate="activate"
@@ -33,4 +33,20 @@
<property name="p1" value="p1" />
<property name="p2" value="p2" />
</scr:component>
+ <scr:component name="components.mutable.properties2"
+ xmlns:scr="http://felix.apache.org/xmlns/scr/v1.2.0-felix"
+ enabled="true"
+ configuration-policy="optional"
+ activate="activateMutate"
+ modified="modifiedMutate"
+ deactivate="deactivateMutate">
+ <implementation class="org.apache.felix.scr.integration.components.MutatingServiceImpl" />
+ <service>
+ <provide interface="org.apache.felix.scr.integration.components.MutatingService" />
+ </service>
+ <property name="service.pid" value="components.mutable.properties2" />
+ <property name="theValue" value="otherValue" />
+ <property name="p1" value="p1" />
+ <property name="p2" value="p2" />
+ </scr:component>
</components>