Community
Participate
Working Groups
Build Identifier: I20100628-2045 When annotating a handler method with @CanExecute, we expect to set up a run & track similar to @Inject so that the framework knows when the @Execute method can be called. As it is now, when annotating a parameter in the @CanExecute method with a custom annotation managed by an ExtendedObjectSupplier, the ExtendedObjectSupplier#get method gets called but the "track" flag is set to false which tells the supplier that only a get is expected, whereas a full run & track is expected. Here is the relevent list of calls: InjectorImpl.resolveArgs(Requestor, PrimaryObjectSupplier, boolean, boolean) line: 322 InjectorImpl.invokeUsingClass(Object, Class<?>, Class<Annotation>, Object, PrimaryObjectSupplier, boolean) line: 170 InjectorImpl.invoke(Object, Class<Annotation>, Object, PrimaryObjectSupplier) line: 159 ContextInjectionFactory.invoke(Object, Class<Annotation>, IEclipseContext, Object) line: 101 HandlerServiceImpl.canExecute(ParameterizedCommand) line: 97 Simply put: HandlerServiceImpl.canExecute calls ContextInjectionFactory.invoke(handler, CanExecute.class, context, Boolean.TRUE)); We end up later in InjectorImpl#invokeUsingClass which has this code: MethodRequestor requestor = new MethodRequestor(method, this, objectSupplier, userObject, false); Object[] actualArgs = resolveArgs(requestor, objectSupplier, false, false); The last booleans in the MethodRequestor constructor and in the resolveArgs call are the flag for "track" that are then passed to Primary/Extended ObjectSuppliers. One way or another, it should be set to true when processing @CanExecute I guess the simplest way is to change the API to make track a parameter of invokeUsingClass and invoke in InjectorImpl, and make the HandlerServiceImpl.canExecute method call ContextInjectionFactory.invoke with that flag set to true. Reproducible: Always
Right now, the menu items and tool items generate RAT when they are created: final IEclipseContext lclContext = getContext(item); lclContext.runAndTrack(new RunAndTrack() { @Override public boolean changed(IEclipseContext context) { if (newItem.isDisposed()) { return false; } EHandlerService service = lclContext.get(EHandlerService.class); ParameterizedCommand cmd = item.getWbCommand(); if (cmd == null) { cmd = generateParameterizedCommand(item, lclContext); } if (cmd == null) { return false; } item.setEnabled(service.canExecute(cmd)); return true; } }); The hope was that the lookup of the handler and the calling of @CanExecute would cause this to be re-evaluated if: 1) the handler changes and 2) any of the parameters (like "selection" or "activePart") changes
(In reply to comment #0) > Build Identifier: I20100628-2045 > When annotating a handler method with @CanExecute, we expect to set up a run & > track similar to @Inject so that the framework knows when the @Execute method > can be called. Nope, sorry, that's not how it is supposed to work. The #invoke() is a one-time operation, similar to calling a method with a variable list of arguments. The changes in the arguments, unlike #inject() are not tracked.
(In reply to comment #2) > Nope, sorry, that's not how it is supposed to work. > Except this is how the system is supposed to work. An invoke in a RAT should record accesses ... that is required to support 3.x. This is what I mentioned the other day, changes in "selection" or "activePart" or "activeContext" that are accessed as a consequence of the @CanExecute need to be recorded so the RAT can be re-done. We need a solution to this problem, whether it's to allow an invoke to record changes or some other one. This is the bug to discuss. I cannot use an @Inject mechanism because the entry point is not the invoke(*) (I don't need it to be continually called on changes by the framework) but the user of invoke, since they're the ones that link the change that causes a re-evaluate of @CanExecute with menuItem.setEnabled(*) PW
Since only context changes are triggering a re-evaluation, I added another method in my handler that I inject. I did not think of using @Inject in a handler immediately so I put the workaround here for others who might experience the problem. (for the @Dynamic annotation, see bug 317706). ======================== 8< ======================== /** The Constant SERVICE_OK. */ public static final String SERVICE_OK = "logservice.ok"; //$NON-NLS-1$ @Inject void refreshExternal(@Dynamic LogService logger, IEclipseContext context) { context.set(SERVICE_OK, logger != null ? Boolean.TRUE : Boolean.FALSE); } @CanExecute boolean canExecute(@Optional @Named(SERVICE_OK) Boolean serviceOK) { return Boolean.TRUE.equals(serviceOK); } ======================== 8< ======================== Pretty clean for a workaround, and it allows to define a "default" state (ie, how you consider the "null" value for your flag). As far as I'm concerned I'm satisfied with this. I understand why @CanExecute is a "one-shot" call only, it's just that I did not realize @Inject could be used in handlers as well (there's no reason why not, but I have not seen samples do this yet, so I just did not consider it).
OK, so maybe we are talking about different things here. The framework should not ever call something accessed through invoke(*). Calling out at unspecified times will cause severe problems for the e4 code. I'll move my discussion (which is how to correctly keep menu item and tool item enabled state up to date, where I control the RAT mechanism) back to bug 315781 PW