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.
testEarProjectIsGCed: junit.framework.AssertionFailedError: project cannot be garbage collected: private static final org.netbeans.modules.j2ee.dd.api.application.DDProvider org.netbeans.modules.j2ee.dd.api.application.DDProvider.ddProvider-> org.netbeans.modules.j2ee.dd.api.application.DDProvider@1a7d0c4-ddMap-> java.util.HashMap@5f177b-table-> [Ljava.util.HashMap$Entry;@4145f5-[6]-> java.util.HashMap$Entry@1ac05bc-value-> org.netbeans.modules.j2ee.dd.impl.application.ApplicationProxy@e5e84-listeners-> java.util.ArrayList@caba7f-elementData-> [Ljava.lang.Object;@1ce2867-[1]-> org.netbeans.modules.j2ee.sun.share.configbean.DescriptorListener@1c005eb-config-> org.netbeans.modules.j2ee.sun.share.configbean.SunONEDeploymentConfiguration@e5d2a3-module-> org.netbeans.modules.j2ee.deployment.devmodules.api.J2eeApplication@d210b2-impl-> org.netbeans.modules.j2ee.earproject.ProjectEar@1d8915a-project-> org.netbeans.modules.j2ee.earproject.EarProject@14afa30 at junit.framework.Assert.fail(Assert.java:47)
The fix is to use a weak listener for org.netbeans.modules.j2ee.sun.share.configbean.DescriptorListener when attaching it to the dd root (line 104 in DescriptorListener).
this looks like something related to the config area
this is probably a p2, too
The fix is not as trivial as using a weak listener. The code originally used a weak listener and then I spent a couple of days trying to figure out why the listeners would randomly stop working mid stream -- because the proxy for standard DD would get garbage collected since there were no strong references. There is code already to disconnect the listener. I will verify it is working and find out if that at fault. Also, I cannot tell from description -- Was this path from the root set the ONLY piece of code holding the project?
ConfigSupportImpl.dispose() in j2eeserver is only called when the target server is changed by the user. It is NOT called when the project is closed. The listener in question cannot be a weak listener. There are some common use cases ("open existing J2EE 1.4 project", I think) where the listener is has the only outstanding reference to the DDProxy to which the listener was added. If a weak listener is used, the DDProxy is garbage collected when this state occurs and no subsequent change events will be received by the server plugin. Can j2eeserver module add a ProjectOpenedHook that calls J2eeModuleProvider.ConfigSupport.dispose() in the projectClose() method (or some other mechanism that would be invoked on project close)? As far as I can tell, server plugins are not able add a ProjectOpenedHook at all.
Peter, I inspected various solutions today (seems to me that OpenedProjectHooked would not be the right one). However I can't see any scenario where the DDProxy could be accidentally collected (at least for the place we are discussing: DescriptorListener). When the listener is added to the DDProxy it is stored in private variable too (DescriptorListener itself is afaik always referenced from SunONEDeploymentConfiguration). In fact the DDProxy objects are strongly referenced from DDProviders (I consider it memory leak). Only the DDProvider for webapp uses weakreferences. Am I missing something? I really can't see any use case which could cause the garbage collection (unwanted) of the DDProxy. If such scenario exists it would be nice to have a unit test for it before we start fixing this issue. I have tested that both fixes (weaklistener in DescriptorListener and weak values in DDProvider caches) pass the test. Unfortunately there is no explanation in code of DDProvider why the weak references were commented out (causing the memory leak). Thanks for you opinions P.
See issue 110080 and issue 110081, specifically from my last comment (Jul 17 23:17:40): "The problem is simple: call DDProvider.getDDRoot(), add listeners, and (key point) don't hold a hard reference to proxy WebApp. The proxy WebApp is later garbage collected. Then after that, when the web.xml editor is opened, web.xml's dataobject asks for the proxy WebApp and a new one is created. Thus expected events are never received because the listeners are on the wrong (discarded) proxy."
I have some more ideas on my side that would at least eliminate the project leak, even if the strong referenced listener code stays. I will try them out and if they work, check them in.
I think I understand it better now, but you actually hold a strong reference in DescriptorListener. I think usage of the WeakListener makes no difference: public void addListener(final RootInterface rootDD) { if(rootDD instanceof Webservices) { if(wsRootDD != null) { wsRootDD.removePropertyChangeListener(this); } wsRootDD = rootDD; } else { if(stdRootDD != null) { stdRootDD.removePropertyChangeListener(this); } stdRootDD = rootDD; } rootDD.addPropertyChangeListener(this); //could be used rootDD.addPropertyChangeListener(WeakListeners.propertyChange(this, rootDD)); }
Ah, the wonders of sleeping on a problem. (FWIW, I didn't like the projectClose() idea much either). The old code that originally used a weak listener didn't hold a strong reference to the corresponding dd proxy (hence 110080) The current does, so weaklistener will work fine and is the correct fix for this problem. And you're right, the DDProviders that have strong references to the DDProxies (ejb ddapi, possibly ear and appclient as well) are a memory leak (separate issue).
Fixed. http://serverplugins.netbeans.org/source/browse/serverplugins/sun/sunddui/src/org/netbeans/modules/j2ee/sun/share/configbean/DescriptorListener.java?r1=1.2&r2=1.3