[
https://issues.apache.org/jira/browse/FELIX-1841?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12773264#action_12773264
]
Pierre De Rop commented on FELIX-1841:
--------------------------------------
Felix,
I'm not sure, but it seems that the issue comes from the
DependencyManager.ungetService(), which is wrongly invoked
from the DependencyManager.serviceRemoved() method:
Let me try to explain:
- The service "Service" is injected to "Client" service, which do have a (1..1)
dependency over "Service".
- Then the "Service" service properties are updated.
- So, in DependencyManager.java, the serviceChanged is called with a MODIFIED
event:
case ServiceEvent.MODIFIED:
m_componentManager.log( LogService.LOG_DEBUG, "Dependency
Manager: Updating {0}", new Object[]
{ serviceString }, null );
// remove the service first
// only continue with further event handling if the service
// removal did not cause the component to be deactivated
if ( serviceRemoved( ref ) )
{
// recalculate the number of services matching the filter
// because we don't know whether this service previously
matched
// or not
ServiceReference refs[] = getFrameworkServiceReferences();
m_size = ( refs == null ) ? 0 : refs.length;
// now try to bind the service - if it matches the target
filter
// without recalculating the size (already done).
if ( targetFilterMatch( ref ) )
{
serviceAdded( ref );
}
}
- Here, the serviceRemoved method is then invoked, which ends up in doing a
bound service replacement (line 358):
else
{
// try to bind a replacement service first if this is a unary
// cardinality reference and a replacement is available.
if ( !m_dependencyMetadata.isMultiple() )
{
// if the dependency is mandatory and no replacement is
// available, bind returns false and we deactivate
if ( !bind() )
{
m_componentManager
.log(
LogService.LOG_DEBUG,
"Dependency Manager: Deactivating component due
to mandatory dependency on {0}/{1} not satisfied",
new Object[]
{ m_dependencyMetadata.getName(),
m_dependencyMetadata.getInterface() }, null );
m_componentManager.deactivateInternal(
ComponentConstants.DEACTIVATION_REASON_REFERENCE );
// required service could not be replaced, component
// is deactivated and we are done
return false;
}
}
// call the unbind method if one is defined
if ( m_dependencyMetadata.getUnbind() != null )
{
invokeUnbindMethod( reference );
}
// make sure the service is returned
ungetService( reference );
}
- the "if (!bind())" line will re-bind "Client" to the same "Service" service,
as "Service" properties are just modified (and "Service" is still present)
- The invokeUnbindMethod will then unbind the same service
- And the ungetService method will remove the bound service from the m_bound
map -> but I think that this is the issue, because the map should contain the
same "Service" service, which has just been re-bound, in the bind() method.
- So, let's follow up with the issue: the "serviceAdded" method is then called
from the serviceChanged() method.
- The serviceAdded method will then attempt to bind the same service, line 268
(but we already did it from the serviceRemoved method), and here, the isBound()
returns false because the m_bound map has been wrongly emptied previously.
// multiple bindings or not bound at all yet
if ( m_dependencyMetadata.isMultiple() || !isBound() )
{
// bind the service, getting it if required
invokeBindMethod( reference );
}
}
So, I replaced the else bloc (line 358) with the following patch (which is
attached to this issue): and the problem seems to disappear:
// dynamic dependency, multiple or single but this service is the
bound one
else
{
boolean doUnget = true;
// try to bind a replacement service first if this is a unary
// cardinality reference and a replacement is available.
if ( !m_dependencyMetadata.isMultiple() )
{
// if the dependency is mandatory and no replacement is
// available, bind returns false and we deactivate
if ( !bind() )
{
m_componentManager
.log(
LogService.LOG_DEBUG,
"Dependency Manager: Deactivating component due
to mandatory dependency on {0}/{1} not satisfied",
new Object[]
{ m_dependencyMetadata.getName(),
m_dependencyMetadata.getInterface() }, null );
m_componentManager.deactivateInternal(
ComponentConstants.DEACTIVATION_REASON_REFERENCE );
// required service could not be replaced, component
// is deactivated and we are done
return false;
}
// Check if we have been rebound to the same (modified)
service.
if (m_bound.size() == 1 && getBoundService( reference ) !=
null) {
// the same (modified) service has been re-bound: don't
unget.
doUnget = false;
}
}
// call the unbind method if one is defined
if ( m_dependencyMetadata.getUnbind() != null )
{
invokeUnbindMethod( reference );
}
// make sure the service is returned.
if (doUnget) {
ungetService( reference );
}
}
Does this make sense to you ? Am I correct or did I miss something else ?
Thanks;
/pierre
> SCR invokes bind method twice when dependency service properties are modified
> -----------------------------------------------------------------------------
>
> Key: FELIX-1841
> URL: https://issues.apache.org/jira/browse/FELIX-1841
> Project: Felix
> Issue Type: Bug
> Components: Declarative Services (SCR)
> Reporter: Pierre De Rop
> Priority: Minor
>
> It seems that when a service S is depending on another service D, and when D
> service properties are modified (using ServiceRegistration.setProperties
> method), then D is
> re-bound twice into the using service S.
> For example, I have a service "Client" has a "1..1" dependency over "Service":
> <?xml version="1.0" encoding="UTF-8"?>
> <component name="Client">
> <implementation class="client.Client"/>
> <reference name="SERVICE"
> interface="service.Service"
> policy="dynamic"
> cardinality="1..1"
> target="(foo=bar)"
> bind="bind"/>
> </component>
> public class Client {
> protected void bind(Service s) {
> System.out.println("Client:: bound Service : " + s);
> Thread.dumpStack();
> }
> }
> I have another bundle which provide the "Service" dependency, and sometimes,
> the "Service" properties are modified like this:
> ServiceRegistration reg ...
> reg.setProperties(...)
>
> So, when the setProperties takes place, "Client" is re-bound twice with the
> service "Service".
> Indeed, in DependencyManager, when a ServiceEvent.MODIFIED event. is caught,
> the following code is invoked (line 170):
> case ServiceEvent.MODIFIED:
> m_componentManager.log( LogService.LOG_DEBUG, "Dependency
> Manager: Updating {0}", new Object[]
> { serviceString }, null );
> // remove the service first
> // only continue with further event handling if the service
> // removal did not cause the component to be deactivated
> if ( serviceRemoved( ref ) )
> {
> // recalculate the number of services matching the filter
> // because we don't know whether this service previously
> matched
> // or not
> ServiceReference refs[] = getFrameworkServiceReferences();
> m_size = ( refs == null ) ? 0 : refs.length;
> // now try to bind the service - if it matches the target
> filter
> // without recalculating the size (already done).
> if ( targetFilterMatch( ref ) )
> {
> serviceAdded( ref );
> }
> }
> break;
> So, the service is first re-bound to the Client, when the serviceRemoved()
> method is invoked (it's a bound service replacement, I guess).
> But the problem here is that the modified service is also re-bound, when
> serviceAdded is invoked (line 189).
> Don't you think that this is a bug and the service should be re-bound only
> once, not twice ?
> Here is the first stacktrace of the first bind:
> java.lang.Exception: Stack trace
> at java.lang.Thread.dumpStack(Thread.java:1158)
> at client.Client.bind(Client.java:13)
> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
> at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
> at java.lang.reflect.Method.invoke(Method.java:592)
> at
> org.apache.felix.scr.impl.helper.BaseMethod.invokeMethod(BaseMethod.java:213)
> at
> org.apache.felix.scr.impl.helper.BaseMethod.access$500(BaseMethod.java:38)
> at
> org.apache.felix.scr.impl.helper.BaseMethod$Resolved.invoke(BaseMethod.java:542)
> at
> org.apache.felix.scr.impl.helper.BaseMethod.invoke(BaseMethod.java:434)
> at
> org.apache.felix.scr.impl.manager.DependencyManager.invokeBindMethod(DependencyManager.java:948)
> at
> org.apache.felix.scr.impl.manager.DependencyManager.bind(DependencyManager.java:884)
> at
> org.apache.felix.scr.impl.manager.DependencyManager.serviceRemoved(DependencyManager.java:367)
> at
> org.apache.felix.scr.impl.manager.DependencyManager.serviceChanged(DependencyManager.java:177)
> at
> org.apache.felix.framework.util.EventDispatcher.invokeServiceListenerCallback(EventDispatcher.java:878)
> at
> org.apache.felix.framework.util.EventDispatcher.fireEventImmediately(EventDispatcher.java:732)
> at
> org.apache.felix.framework.util.EventDispatcher.fireServiceEvent(EventDispatcher.java:662)
> at org.apache.felix.framework.Felix.fireServiceEvent(Felix.java:3587)
> at org.apache.felix.framework.Felix.access$000(Felix.java:40)
> at org.apache.felix.framework.Felix$1.serviceChanged(Felix.java:625)
> at
> org.apache.felix.framework.ServiceRegistry.servicePropertiesModified(ServiceRegistry.java:505)
> at
> org.apache.felix.framework.ServiceRegistrationImpl.setProperties(ServiceRegistrationImpl.java:116)
> at service.impl.ServiceFactory.run(ServiceFactory.java:48)
> at java.lang.Thread.run(Thread.java:595)
> and here is the second stacktrace, when Service is re-bound:
> Client: bound Service : service.impl.servicei...@142a80d
> java.lang.Exception: Stack trace
> at java.lang.Thread.dumpStack(Thread.java:1158)
> at client.Client.bind(Client.java:13)
> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
> at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
> at java.lang.reflect.Method.invoke(Method.java:592)
> at
> org.apache.felix.scr.impl.helper.BaseMethod.invokeMethod(BaseMethod.java:213)
> at
> org.apache.felix.scr.impl.helper.BaseMethod.access$500(BaseMethod.java:38)
> at
> org.apache.felix.scr.impl.helper.BaseMethod$Resolved.invoke(BaseMethod.java:542)
> at
> org.apache.felix.scr.impl.helper.BaseMethod.invoke(BaseMethod.java:434)
> at
> org.apache.felix.scr.impl.manager.DependencyManager.invokeBindMethod(DependencyManager.java:948)
> at
> org.apache.felix.scr.impl.manager.DependencyManager.serviceAdded(DependencyManager.java:271)
> at
> org.apache.felix.scr.impl.manager.DependencyManager.serviceChanged(DependencyManager.java:189)
> at
> org.apache.felix.framework.util.EventDispatcher.invokeServiceListenerCallback(EventDispatcher.java:878)
> at
> org.apache.felix.framework.util.EventDispatcher.fireEventImmediately(EventDispatcher.java:732)
> at
> org.apache.felix.framework.util.EventDispatcher.fireServiceEvent(EventDispatcher.java:662)
> at org.apache.felix.framework.Felix.fireServiceEvent(Felix.java:3587)
> at org.apache.felix.framework.Felix.access$000(Felix.java:40)
> at org.apache.felix.framework.Felix$1.serviceChanged(Felix.java:625)
> at
> org.apache.felix.framework.ServiceRegistry.servicePropertiesModified(ServiceRegistry.java:505)
> at
> org.apache.felix.framework.ServiceRegistrationImpl.setProperties(ServiceRegistrationImpl.java:116)
> at service.impl.ServiceFactory.run(ServiceFactory.java:48)
> at java.lang.Thread.run(Thread.java:595)
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.