Bug 226809 - [ActivityMgmt] Forbid to add activities to the enabled list which are originally disabled
Summary: [ActivityMgmt] Forbid to add activities to the enabled list which are origina...
Status: VERIFIED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.4   Edit
Hardware: PC Windows Vista
: P3 minor (vote)
Target Milestone: ---   Edit
Assignee: Kim Horne CLA
QA Contact:
URL:
Whiteboard:
Keywords: contributed
Depends on:
Blocks:
 
Reported: 2008-04-12 12:13 EDT by Jan-Hendrik Diederich CLA
Modified: 2008-04-29 21:09 EDT (History)
3 users (show)

See Also:


Attachments
Removes activities from the enabled list which are controlled by disabled expressions (6.38 KB, patch)
2008-04-12 12:13 EDT, Jan-Hendrik Diederich CLA
no flags Details | Diff
Extended Patch against wrong enablement (8.07 KB, patch)
2008-04-14 19:24 EDT, Jan-Hendrik Diederich CLA
no flags Details | Diff
Activity States, very preliminary. For a discussion base. (21.95 KB, application/pdf)
2008-04-15 13:55 EDT, Jan-Hendrik Diederich CLA
no flags Details
New patch way proposal nr. 0 (10.71 KB, patch)
2008-04-16 21:25 EDT, Jan-Hendrik Diederich CLA
no flags Details | Diff
New patch way proposal nr. 1 (10.36 KB, patch)
2008-04-16 21:31 EDT, Jan-Hendrik Diederich CLA
no flags Details | Diff
Suggested replacement patch (13.99 KB, patch)
2008-04-17 12:52 EDT, Kim Horne CLA
eclipse: review?
Details | Diff
Little fix to the fix. (1.27 KB, patch)
2008-04-21 14:18 EDT, Jan-Hendrik Diederich CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Jan-Hendrik Diederich CLA 2008-04-12 12:13:01 EDT
Created attachment 95811 [details]
Removes activities from the enabled list which are controlled by disabled expressions

Build ID: I20080330-1350

Steps To Reproduce:
1. Add expression controlled disabled activities with the ActivityManager.setEnabledActivities() method to the enabled activities list.
2. Enable their expressions and fire their evaluation source.
3. Notice that they aren't updated properly since the system don't recognizes the difference since it has them already in the enabled list.

More information:
With this patch I just remove every activity which is disabled   and controlled by an expression from the setEnabledActivities list. Due to a logic short-cut in the code this may be a risk, because it says if every activity is in the enabled list it doesn't question the TriggerPointAdvisor which tests against the expressions.
Comment 1 Jan-Hendrik Diederich CLA 2008-04-12 14:17:04 EDT
(In reply to comment #0)
> With this patch I just remove every activity which is disabled   and controlled
> by an expression from the setEnabledActivities list. Due to a logic short-cut
> in the code this may be a risk, ...
This was bad described: the patch offers no risk, but if it's not patched the described risk comes up.
Comment 2 Jan-Hendrik Diederich CLA 2008-04-14 19:24:46 EDT
Created attachment 96016 [details]
Extended Patch against wrong enablement

This patch includes the last patch (see comment #0), and fixes a second loophole. An activity which was already removed from the enabledList, and then disabled by an expression, wouldn't update it's state without this patch. It would stay filtered but would not be restricted. This patch should be applied ASAP.

A corresponding unit test is also included.
Comment 3 Kim Horne CLA 2008-04-15 10:28:05 EDT
I really dont like seeing the expression being tested in the manager - that logic all belongs in the advisor.  At least, it has until now...

Why is it not sufficient to just strip any activities bound to expressions from the list passed into setEnabledIds?
Comment 4 Jan-Hendrik Diederich CLA 2008-04-15 13:13:36 EDT
(In reply to comment #3)
> I really dont like seeing the expression being tested in the manager - that
> logic all belongs in the advisor.  At least, it has until now...
Maybe. But forever the only "activity.setEnabled(..)" command has been in the MutableActivityManager! It's in the updateActivity(..) method which is private and called by the public method "setEnabledActivities(..)". And the calling of this method in the activity enabled listener (enabledWhenListener) depends on the enabled state of the activity. In the old version it tested against the state of the enabledList which may be incorrect in that way that it doesn't mirrors the actual state of expression enabled activities.

> Why is it not sufficient to just strip any activities bound to expressions
> from the list passed into setEnabledIds?

Well, you can of course go this way and remove all of the expression controlled activities. But remember that you then have to write a second way where the listener of expression controlled activities don't uses setEnabledActivities(..) and handles the transformation of the enabledList itself (in the "enabledWhenListener") and the updates of other listeners too. So that expression disabled activities are also removed from the enabledList, not only the non-expression-controlled. And they both should be synchronized, don't they? I think no one wishes to make two parallel lists.
Additionally I want you point to the logic short-cut point of the code (which is actually really reasonable); it's in MutableActivityManager in 
"updateIdentifier(Identifier identifier, Set changedActivityIds)", in the following lines:

if (enabledActivityIds.size() == definedActivityIds.size()) {
   enabled = true;
   enabledChanged = identifier.setEnabled(enabled); ... }
else
{ ... call advisor ... }

(That's not by me, this has been already there for a very long time.)
Comment 5 Jan-Hendrik Diederich CLA 2008-04-15 13:55:20 EDT
Created attachment 96129 [details]
Activity States, very preliminary. For a discussion base.
Comment 6 Jan-Hendrik Diederich CLA 2008-04-16 21:25:53 EDT
Created attachment 96367 [details]
New patch way proposal nr. 0

This is a patch proposal which divides the control of expression enabled activities and "normal" activities. Expression controlled activities can't be filtered anymore (see Bug 224673).
This solution takes a more general approach to divide the two types of activities. It relies on the new MutableActivityManager#setExpressionEnabledActivityIds() method.
Comment 7 Jan-Hendrik Diederich CLA 2008-04-16 21:31:08 EDT
Created attachment 96370 [details]
New patch way proposal nr. 1

This is a patch proposal which divides the control of expression enabled activities and "normal" activities. Expression controlled activities can't be filtered anymore (see Bug 224673).
This solution takes a more specific approach to divide the two types of activities. It relies on the two new MutableActivityManager#addExpressionEnabledActivity and #removExpressionEnabledActivity methods.

Both patches are valid working solutions, which use the same unit tests and stuff. The outside API behavior is the same. Paul, Kim: just tell me what your favorite way is - general, specific, 3rd way - and I will work it out (actually these both maybe just need a little more java-doc (although they are all private methods)).

Just chose:
[ ]: Way 0
[ ]: Way 1
[ ]: Total other way
Comment 8 Kim Horne CLA 2008-04-17 09:05:46 EDT
On first blush I like option 1.  I have some minor issues with the names of some methods (remove*) but not enough to complain.  I want to look at it a bit further but I think it's a keeper.
Comment 9 Kim Horne CLA 2008-04-17 12:24:04 EDT
(In reply to comment #8)
> On first blush I like option 1.  I have some minor issues with the names of
> some methods (remove*) but not enough to complain.  I want to look at it a bit
> further but I think it's a keeper.
> 

After discussing with Paul there are still some issues.  Basically the problem is this:  the code in the patch takes into account a user trying to set enabled an activity bound to an expression but doesn't guard against a user trying to remove enablement from an activity bound to an expression.  The following test code should show the problem:

TestSourceProvider testSourceProvider = new TestSourceProvider();
    		IEvaluationService evalService = (IEvaluationService) PlatformUI
    				.getWorkbench().getService(IEvaluationService.class);
    		evalService.addSourceProvider(testSourceProvider);
    		testSourceProvider.fireSourceChanged();
    		
    		
    		IWorkbenchActivitySupport support = PlatformUI.getWorkbench()
    			.getActivitySupport();		
    		support.setEnabledActivityIds(Collections.emptySet());
    		Set set = new HashSet(support.getActivityManager().getEnabledActivityIds());
    		Set previousSet = new HashSet(support.getActivityManager().getEnabledActivityIds());
    		set.add(EXPRESSION_ACTIVITY_ID_2);
    		support.setEnabledActivityIds(set);
    		assertEquals(previousSet, support.getActivityManager().getEnabledActivityIds());
    		
    		testSourceProvider.setVariable();
    		testSourceProvider.fireSourceChanged();
    		
    		set = new HashSet(support.getActivityManager().getEnabledActivityIds());
    		assertFalse(set.equals(previousSet));
    		
    		set.remove(EXPRESSION_ACTIVITY_ID_2);
    		support.setEnabledActivityIds(set);
    		
    		assertFalse(support.getActivityManager().getEnabledActivityIds().equals(previousSet));

    		evalService.removeSourceProvider(testSourceProvider);
Comment 10 Kim Horne CLA 2008-04-17 12:52:34 EDT
Created attachment 96468 [details]
Suggested replacement patch

Takes the Option 1 patch and changes it.  Instead of processing the deltas as a whole we process additions and removals separately.  From each we strip any expression activities.  We then use these deltas to augment the previously enabled list, instead of blowing it away.  This patch contains the above test case I posted.
Comment 11 Kim Horne CLA 2008-04-17 12:53:58 EDT
(In reply to comment #10)
> Created an attachment (id=96468) [details]
> Suggested replacement patch
> 
> Takes the Option 1 patch and changes it.  Instead of processing the deltas as a
> whole we process additions and removals separately.  From each we strip any
> expression activities.  We then use these deltas to augment the previously
> enabled list, instead of blowing it away.  This patch contains the above test
> case I posted.
> 

I haven't run the full set of suites over this patch - only UtilsTest
Comment 12 Kim Horne CLA 2008-04-17 13:39:27 EDT
(In reply to comment #11)
> 
> I haven't run the full set of suites over this patch - only UtilsTest
> 

All tests are passing for me.
Comment 13 Jan-Hendrik Diederich CLA 2008-04-17 21:59:41 EDT
(In reply to comment #9)
> After discussing with Paul there are still some issues.  Basically the problem
> is this:  the code in the patch takes into account a user trying to set enabled
> an activity bound to an expression but doesn't guard against a user trying to
> remove enablement from an activity bound to an expression.
How? Only the deltaActivityIds are taken into account for changing anything. And every id gets removed from the set if it has an expression. So how can you touch it? It must be total immutable!


> The following test code should show the problem:
> ... 
>                 set = new
>                       HashSet(support.getActivityManager()
>                       .getEnabledActivityIds());
>                 assertFalse(set.equals(previousSet));

This shouldn't change anything, right? The activity has an expression on it, so it should be totally ignored!
>                 set.remove(EXPRESSION_ACTIVITY_ID_2);
>                 support.setEnabledActivityIds(set);
Then, why should this be false? It must be true: they both are still equal.
>                 assertFalse(support.getActivityManager()
>                      .getEnabledActivityIds().equals(previousSet));
> 
>                 evalService.removeSourceProvider(testSourceProvider);
Instead:
                  assertTrue(support.getActivityManager()
                      .getEnabledActivityIds().equals(previousSet));

I can't see any failure in my code.
The call in setEnabledActivityIds():
> removeExpressionControlledActivities(this.enabledActivityIds,
> 					deltaActivityIds);
> if (deltaActivityIds.size() > 0) {		
>    activityEventsByActivityId = updateActivities(deltaActivityIds);
> } else { return; }

And in removeExpressionControlledActivities():
> for (Iterator iterator = deltaActivityIds.iterator(); iterator
>				.hasNext();) {
>			String id = (String) iterator.next();
>			IActivity activity = (IActivity) activitiesById.get(id);
>			Expression expression = activity.getExpression();
>			if (expression != null) {
Look: both are removed! enabledActivityIds and deltaActivityIds,
so enabledActivityIds are corrected _and_ deltaActivityIds.
>				enabledActivityIds.remove(id);
>				iterator.remove(); ...
>			}
>		}
So I don't see a chance of abuse.
Comment 14 Paul Webster CLA 2008-04-18 07:54:10 EDT
(In reply to comment #13)
> How? Only the deltaActivityIds are taken into account for changing anything.
> And every id gets removed from the set if it has an expression. So how can you
> touch it? It must be total immutable!

This is the point ... the set returned from getEnabledActivityIds() must be the union of any user-set activities plus any expression-enabled-true activities.

That means when the user calls setEnabledActivityIds(set) we must remove any expression-enabled-false activities and add back any missing expression-enabled-true activities.

> >                 set.remove(EXPRESSION_ACTIVITY_ID_2);
> >                 support.setEnabledActivityIds(set);
> Then, why should this be false? It must be true: they both are still equal.
> >                 assertFalse(support.getActivityManager()
> >                      .getEnabledActivityIds().equals(previousSet));

This is the point ... EXPRESSION_ACTIVITY_ID_2 is enabled (via its expression).  It doesn't matter that the user tries to remove it from the list of enabled IDs, it is still enabled and must still be in the getEnabledActivityIds() set

PW
Comment 15 Kim Horne CLA 2008-04-18 08:43:44 EDT
(In reply to comment #14)
> (In reply to comment #13)
> > How? Only the deltaActivityIds are taken into account for changing anything.
> > And every id gets removed from the set if it has an expression. So how can you
> > touch it? It must be total immutable!
> 
> This is the point ... the set returned from getEnabledActivityIds() must be the
> union of any user-set activities plus any expression-enabled-true activities.
> 
> That means when the user calls setEnabledActivityIds(set) we must remove any
> expression-enabled-false activities and add back any missing
> expression-enabled-true activities.
> 
> > >                 set.remove(EXPRESSION_ACTIVITY_ID_2);
> > >                 support.setEnabledActivityIds(set);
> > Then, why should this be false? It must be true: they both are still equal.
> > >                 assertFalse(support.getActivityManager()
> > >                      .getEnabledActivityIds().equals(previousSet));
> 
> This is the point ... EXPRESSION_ACTIVITY_ID_2 is enabled (via its expression).
>  It doesn't matter that the user tries to remove it from the list of enabled
> IDs, it is still enabled and must still be in the getEnabledActivityIds() set
> 
> PW
> 

^--- what he said.

Comment 16 Kim Horne CLA 2008-04-21 09:34:00 EDT
Based on an IRC conversation I think we're now all in agreement.  If you dont have any objections Paul I'll put this in for tomorrows build.
Comment 17 Kim Horne CLA 2008-04-21 14:03:38 EDT
Silence is golden.  Fix in HEAD.
Comment 18 Jan-Hendrik Diederich CLA 2008-04-21 14:18:46 EDT
Created attachment 96906 [details]
Little fix to the fix.

Sorry. I have to add a fix I already added localy. Sorry. Two lines of code. It just didn't cause any bug, because it fired a notifier with false data, which no one was listening to.
Comment 19 Jan-Hendrik Diederich CLA 2008-04-21 14:20:07 EDT
See comment #18 for the reopening cause.
Comment 20 Jan-Hendrik Diederich CLA 2008-04-21 15:08:52 EDT
Reducing bug to minor, because no really harmful errors are expected, which can be caused by the bug left.
Comment 21 Kim Horne CLA 2008-04-21 15:09:36 EDT
Patch applied.
Comment 22 Kim Horne CLA 2008-04-29 15:54:37 EDT
Verified by code inspection in I20080429-0100.  Jan, if you have a moment, I'd appreciate it if you could verify as well.
Comment 23 Jan-Hendrik Diederich CLA 2008-04-29 21:09:56 EDT
Verified in I20080429-0100 with extended junit UI test.