[
https://issues.apache.org/jira/browse/FELIX-5274?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15519645#comment-15519645
]
Pierre De Rop commented on FELIX-5274:
--------------------------------------
Hi Bram,
I finally committed a patch in revision 1762151 for your issue (it fixes the
issue where you explicitly remove an instance-bound dependency from "destroy"
method).
added test case in FELIX5274_CleanupInstanceBoundDependenciesInDestroy.java.
Let me explain what was going on (I prefer to write here all the internals; for
the record):
So, not easy to explain, but the DM state machine is currently re-entrant, and
this may causes some side effects when you add or remove dependencies from
lifecycle callback methods.
In your case, you are removing an instance-bound dependency from the "destroy"
method. So, at the time you remove the instance-bound dependency from the
destroy() method, then the
DM state machine (which is currently transiting from
INSTANTIATED_AND_WAITING_FOR_REQUIRED -> WAITING_FOR_REQUIRED) then re-enter in
the "handleChange" method, which then transits from
WAITING_FOR_REQUIRED -> INACTIVE, and the problem is that at this point we have
not yet finished the transition from INSTANTIATED_AND_WAITING_FOR_REQUIRED ->
WAITING_FOR_REQUIRED.
Here is the scenario which helps to understand the issue:
1) you remove a component, using "dm.remove(component)" for example.
2) so, at this point, the "handleChange()" method (which is internal to
ComponentImpl.java in DM) then performs some state transitions in order to
destroy the component.
3) and at the time the state machine transits from
INSTANTIATED_AND_WAITING_FOR_REQUIRED -> WAITING_FOR_REQUIRED, then it does the
following:
{code}
if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED &&
newState == ComponentState.WAITING_FOR_REQUIRED) {
invoke(m_callbackDestroy); // 3.1
removeInstanceBoundDependencies();
invokeRemoveRequiredDependencies(); // 3.2
notifyListeners(newState);
if (! someDependenciesNeedInstance()) {
destroyComponent();
}
return true;
}
{code}
4) in 3.1, the "invoke(m_callbackDestroy)" is invoked, and your "destroy"
callback then removes an instance bound dependency that was initially added in
"init" callback.
And at this point the DM state machine sudenly transits from
WAITING_FOR_REQUIRED -> INACTIVE (notice it has not yet finished the initial
transition):
{code}
if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState ==
ComponentState.INACTIVE) {
stopDependencies();
destroyComponent(); // 4.1
notifyListeners(newState);
return true;
}
{code}
so, you see in 4.1 the component is destroyed, and when the initial transition
resumes, when we execute 3.2 the component
has already been destroyed in 4.1, and required dependencies can't be unbounded.
So, I think that the only way to properly resolve this issue without risks is
to not allow reentrency in the state machine, a transition should be fully
finished before evaluating what has been done in a lifecyle callback.
the patch which avoid the state machine to be reentrant is located at the
beginning of the handleChange method:
{code}
private void handleChange() {
if (isHandlingChange()) {
return;
}
...
}
{code}
With the patch, all tests are passing OK, except the
ComponentTest.testAddAvailableDependencyFromInitCallback().
This test was testing that a dependency added or removed from a lifecycle
callback was immediately evaluated, but now, since the patch does not allow to
re-enter into the state machine, I had to modify a bit the test, which is still
making sense.
> remove callback fails after manually removing dynamic dependencies
> ------------------------------------------------------------------
>
> Key: FELIX-5274
> URL: https://issues.apache.org/jira/browse/FELIX-5274
> Project: Felix
> Issue Type: Bug
> Components: Dependency Manager
> Affects Versions: org.apache.felix.dependencymanager-r8
> Reporter: Bram Pouwelse
> Assignee: Pierre De Rop
>
> When dynamic instance bound dependencies are removed in the component destroy
> dm fails to call the remove callback.
> I use an adapter service like
> {code}
> manager.add(createAdapterService(ApplicationService.class, null, "onAdd",
> "onChange", "onRemove")
> .setInterface(Servlet.class.getName(), null)
> .setImplementation(WinkServlet.class)
> .setCallbacks("dmInit", "dmStart", "dmStop", "dmDestroy")
> );
> {code}
> In the WinkServlet init callback an additional dependency is added and this
> is removed in the destroy callback. When the component is removed I get log
> messages like
> {code}
> ERROR org.amdatu.web.rest.wink - [main] "onRemove" callback not found on
> component instances []
> {code}
> [http://www.mail-archive.com/users%40felix.apache.org/msg17300.html]
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)