This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.
Hi guys, I have a ContextAwareAction on the Toolbar and in the class constructor I attach a Listener to the global context. public final class MyAction extends AbstractAction implements ContextAwareAction, LookupListener { public MyAction() { this(Utilities.actionsGlobalContext()); } private MyAction(Lookup context) { // attach a listener to the lookup result = context.lookup(TEMPLATE); result.addLookupListener(this); // start the LookupListener resultChanged(null); } public Action createContextAwareInstance(Lookup actionContext) { return new MyAction(actionContext); } public void actionPerformed(ActionEvent e) { // do something } public void resultChanged(LookupEvent ev) { //do something } } When we disable the module programmatically, the Listener is still alive, and when I turn on the module (programmatically or manually) and the global context changes I have a NoClassDefFoundError. Is there a way to remove the Listener to Utilities.actionsGlobalContext() when the module goes down? Thanks in advance Paolo
Generally you would use WeakListeners for this purpose. You might also implement Presenter.Toolbar and use Component.add/removeNotify to decide when to begin or end listening. However I would think that the Lookup.Result would be collected when your MyAction instance was. Perhaps the L.R is being cached? Yarda should be able to say. For purposes of this example it is pointless to implement ContextAwareAction, as this interface is generally only meaningful for actions added to context menus. Using Utilities.actionsGlobalContext() is appropriate for a global toolbar button. By the way you can use lookupResult(Class) rather than making an explicit Lookup.Template.
I think at the end the question is: is disabling a module (without restarting NB) safe? It seems that disabling a module leaves some instance already in the heap, but orphan of its Class definition... Re-enabling a module will create another class definition with a new module's classloader, giving some NoClassDefFoundError.
Is this really a bug report? I am sorry, but I have no idea what to do. Use WeakListeners as Jesse suggested and your problem may be gone. If you write a unit test and simulate some problem, feel free to reopen.
Created attachment 82106 [details] Testcase suite with reproducible exception
We just created a testcase suite with a minimal code to reproduce the exception. Basically our use case is to disable/enable modules depending on a user license. At startup we disable all modules and, after the user enters the license configuration, we enable only allowed modules. The point is that one of the enabled modules contains a project implementation (in our testcase, TestProject). If before the disabling there was a project opened in the Project Tab, we want to have it opened again after the re-enabling. This leads to a problem with ClassLoader of that module. At the end we want to reopen only TestProject instances formerly present in the ProjectTab. In the attached testcase we reopen an hardcoded project (only for simplifying and for testing purpose). And to show the problem we test that the project instance should be a TestProject instance. Then you will see that at first boot the project has been open successful, but the second time (after disable/enable) it won't. We got an assertion error on testing that the hardcoded project is a TestProject class. Instead in a debugging session, in the watch top component we have that "p instanceof TestProject" is true!!! But at runtime we got the assertion error.... In the suite there are two modules: - ProjectAPIImpl module: it contains a simple project implementation (TestProject class) and a module Installer that opens a project (hardcoded only for simplify the testcase) when the module is enabled. - Test project module: contains a top component with Disable and Enable buttons for modules disabling/enabling Step to reproduce: 1) run the suite 2) you'll find a "xyz" project open (a TestProject instance) 3) click on Disable button to disable the ProjectAPIImpl module 4) the "xyz" project is no longer there 5) click on Enable button to enable again the ProjectAPIImpl module 6) AssertionError when trying to check that xyz is a TestProject instance...
FYI: The testcase is based on NB 6.5.1, but the same error is in NB6.1 too. Originally the issue was with toolbar actions, but the problem here set out is basically another effect of the problem with disable/enable modules without restarting the application.
The ProjectManager.findProject caches projects even if its module has been disabled. When it is enabled again, the cache still returns old value and class cast exception is thrown.
jtulach, jglick: it seems a bit dishonest not to accept the fact that the module system is not ready (and never was) to have disabling and enabling modules work properly without a restart. That's why upgrading modules always restarts the IDE, that's why ergonomics/FeatureOnDemand just enables modules. It might work for very simple modules, but anything that attaches a listener to other module's codes or similar will make the module not work, weak listeners are just means to push the dirt under the carpet so that noone notices. Until there's a clear code separation between modules (can there be?) that cleans up (completely) on module disable, I don't see any point in fixing single occurencies of the problem. Even if I do fix this one, the user (or someone else) will immediately be hit by the next one..
To matteodg: I would not recommend dynamically changing module enablement just to accept licenses. The NB Platform already has a mechanism (used by the IDE) to request acceptance of a license before even starting the module system; if you need to load modules before showing the license dialog, I would rather recommend simply preventing any useful app functionality from proceeding until the dialog is accepted, but leave modules untouched.
@jglick: Thanks for your comment. We are enabling modules depending on user authorizations: for example (at the first start or when he needs to update those information) the user has to enter the server from which our application can gather authorization information. After that we disable the modules the user is not authorized to use. Is NetBeans IDE doing such a thing? I do not think: likely with "accept license" you meant only reply yes/no to a license dialog, isn't it? For the moment we cannot trigger a restart, since to do that we would have to change a big portion of code... and we are close to a software release... Therefore we will live with it... But in future we want to finalize this code, finding a good way to implement this feature: maybe injecting at runtime some content to the SystemFileSystem. Can you suggest a more clean implementation? Thanks in advance.
Runtime control of SFS content is possible, though it may be overkill. I might recommend that important entry points in the code which require certain authorizations check for them at that time and abort with an error dialog if it is missing. Controlling security through module enablement is probably not a good idea, and anyway cannot be a primary security technique (under the assumption that all client software is untrusted); can at best be used to hide irrelevant items from the GUI.
even though the example code deals with Projects, it's not primarily a project system issue but module system one. (as the initial bug description proves. Reassigning to the module system
The issue is indeed in the project system; ProjectManager is not checking for module reload. In principle it could find the ModuleInfo associated with the Project's impl class, check for its enablement to go to false, and call notifyDeleted on the ProjectStateImpl. In practice we do not intend to spend the effort required to make this scenario work; many aspects of the IDE will not work well after modules have been dynamically disabled, and we have found it to be impractical to fix them all. Several alternatives which do not involve changes in module status have been suggested to the reporter already, and it is these which we would recommend instead.