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.

Bug 156822 - Projects remain in case when a module is disabled
Summary: Projects remain in case when a module is disabled
Status: RESOLVED WONTFIX
Alias: None
Product: projects
Classification: Unclassified
Component: Generic Infrastructure (show other bugs)
Version: 6.x
Hardware: All All
: P4 blocker with 1 vote (vote)
Assignee: Jesse Glick
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-01-14 15:38 UTC by epele83
Modified: 2009-08-14 16:45 UTC (History)
4 users (show)

See Also:
Issue Type: DEFECT
Exception Reporter:


Attachments
Testcase suite with reproducible exception (7.60 KB, application/x-gzip)
2009-05-14 10:21 UTC, matteodg
Details

Note You need to log in before you can comment on or make changes to this bug.
Description epele83 2009-01-14 15:38:29 UTC
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
Comment 1 Jesse Glick 2009-01-14 17:03:32 UTC
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.
Comment 2 matteodg 2009-01-14 18:26:45 UTC
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.
Comment 3 Jaroslav Tulach 2009-03-09 16:42:22 UTC
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.
Comment 4 matteodg 2009-05-14 10:21:24 UTC
Created attachment 82106 [details]
Testcase suite with reproducible exception
Comment 5 matteodg 2009-05-14 10:57:23 UTC
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...
Comment 6 matteodg 2009-05-14 10:59:44 UTC
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.
Comment 7 Jaroslav Tulach 2009-05-18 16:33:04 UTC
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.
Comment 8 Milos Kleint 2009-05-19 14:45:55 UTC
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..
Comment 9 Jesse Glick 2009-05-19 17:19:07 UTC
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.
Comment 10 matteodg 2009-05-19 22:19:05 UTC
@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.
Comment 11 Jesse Glick 2009-05-19 22:27:04 UTC
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.
Comment 12 Milos Kleint 2009-08-14 16:09:53 UTC
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
Comment 13 Jesse Glick 2009-08-14 16:45:34 UTC
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.