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 38420 - Lookup.getDefault() releases instances when modules are installed/uninstalled
Summary: Lookup.getDefault() releases instances when modules are installed/uninstalled
Status: VERIFIED FIXED
Alias: None
Product: platform
Classification: Unclassified
Component: Module System (show other bugs)
Version: 3.x
Hardware: All All
: P2 blocker (vote)
Assignee: Jaroslav Tulach
URL:
Keywords:
Depends on:
Blocks: 38249
  Show dependency tree
 
Reported: 2004-01-06 10:25 UTC by Jaroslav Tulach
Modified: 2008-12-23 08:37 UTC (History)
5 users (show)

See Also:
Issue Type: DEFECT
Exception Reporter:


Attachments
And old instance has not been released. (19.61 KB, text/plain)
2004-01-13 12:28 UTC, Jan Jancura
Details
Proposed fix (20.35 KB, patch)
2004-01-22 13:28 UTC, Jaroslav Tulach
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Jaroslav Tulach 2004-01-06 10:25:31 UTC
According to bug report in issue 38249 and to my
all my knowledge of the code the module system
create new metaInfServices lookup and replaces
already existing one with completelly new
instance. Result of this action is that all
instances created by the previous metaInfServices
lookup are lost and new ones are being created
from scratch. This seems like a serious bug.
Comment 1 Jaroslav Tulach 2004-01-06 10:29:39 UTC
Jesse, please suggest how to write a test for core/module system. I
can do it, but suggest where to place it. 

The fix could imho be based on addition of new method 
Lookup Lookups.metaInfLookup (ClassLoader l, Lookup previousOne)
that would create new metaInfLookup and inherited all existing
instances from the previous one. I can implement this (easily) in openide.

The core/modules would then use this method to preserve previously
created instances. 
Comment 2 Jan Jancura 2004-01-06 13:57:57 UTC
*** Issue 38249 has been marked as a duplicate of this issue. ***
Comment 3 Jesse Glick 2004-01-06 17:17:04 UTC
I don't have time to work on this but I can offer some hints. Do not
consider this a P2 bug (or even necessarily a bug at all, except
perhaps a minor performance issue); cf. issue #38249 for explanation.

The proposed added API method would probably be enough. But be careful
to ensure that only instances whose impl classes are loadable from the
new class loader are retained - you do not want to keep around stale
instances from the old module in case a module is disabled and then
quickly reenabled.

Tests for the module system normally go into the existing
ModuleManagerTest. Though this would not test NbTopManager.Lkp.
(Methods in question: sCLC and mCLU.) Could make a new test class in
o.n.c.modules.
Comment 4 Jesse Glick 2004-01-06 18:16:48 UTC
Got a better idea. Using one MISL based on MM.SCL is probably not too
good anyway - consider

1. A module can register a service that it cannot itself load (but
that another module can). Weird.

2. If you have two modules with the same package names etc. (e.g. two
incompat versions of an autoload lib), they must both choose different
class names for their MISL instances or they will clash.

Probably better would be to stop using the current Ls.mISL and add a
new factory method to Lookups:

public interface MetaInfServiceFinder {
    Enumeration/*<URL>*/ findServices(String className);
}
public Lookup metaInfServicesLookup(ClassLoader l,
MetaInfServiceFinder f);

Then NbTM.Lkp should be modified to create one MISL *per module*. It
could go through the list of enabled modules during startup, scanning
each module JAR (Module.jarFile) for META-INF/services/* entries
(skipping any modules with no such entries) and calling the new
factory; the finder would look directly in the JAR file and return
jar: URLs. When a module is disabled, remove its lookup (if any); when
enabled, scan it.

It does not suffice to do this trick using the current factory method
because if module A declares service S1 and module B depends on A and
declares service S2, B's class loader will find both S1 and S2,
creating duplicates. You need to scan b.jar directly.

Such a change might have beneficial performance impact too, as it
would avoid calling MM.SCL.findAllResources(String). The finder impl
could prescan the JAR for all known entries so that
findServices(String) would be fast.
Comment 5 Jaroslav Tulach 2004-01-08 14:20:44 UTC
What is surprising that the similar test is in

org.netbeans.core.lookup.MetaInfLookupTest

and it is passing fine.
Comment 6 Jan Jancura 2004-01-08 15:40:53 UTC
Still reproducible.
Do you really test Lookup.getDefault().lookup(Debugger.class);
based on mf-layers.xml?
    <folder name="Services">        
        <folder name="Hidden">
            <file name="debugger.instance">
                <attr name="instanceClass"
stringvalue="org.netbeans.modules.debugger.multisession.EnterpriseDebugger"/>
            </file>
        </folder>

See my mflayer here:
trunk\debuggercore\src\org\netbeans\modules\debugger\resources\mf-layer.xml

Comment 7 Jesse Glick 2004-01-08 15:58:59 UTC
Aha, you are registering it in a layer, not in META-INF/services! That
is quite different. So perhaps META-INF/services already works fine
(which would be one quick fix for #38249 to use - anyway using
META-INF/services is preferred for singletons like this).

So the problem is presumably in InstanceDataObject, or perhaps in
FolderLookup.
Comment 8 Jaroslav Tulach 2004-01-08 16:01:13 UTC
Of course I am not testing debugger - that is part of issue 38249, I
am looking for a simple test how to reproduce the problem
automatically. If you know how to write automated test for Debugger,
please contribute. Otherwise I am going to try layer based
registration similar to what you pointed out in
org.netbeans.core.lookup.MetaInfLookupTest, but not exact.
Comment 9 Jaroslav Tulach 2004-01-12 15:40:43 UTC
Test written that tries to reproduce the problem using .instance in
layer file, but the behaviour could not be simulated. Closing as invalid.

http://www.netbeans.org/source/browse/core/test/unit/src/org/netbeans/core/lookup/InstanceDataObjectModule38420Test.java
Comment 10 Jan Jancura 2004-01-12 16:09:31 UTC
Yarda,
its not invalid, its still reproducible.
Its very easy to reproduce it in Netbeans + debugger.
Attach to NB from debugger and evaluate org.openide..Lookup.getDefault
().lookup (org.netbeans...Debugger.class);

And regarding your testcase. Is not a difference that you keep
instance of Lookup.Result?

Or.. can you create test exactly on Debugger.class instance?
Comment 11 Jaroslav Tulach 2004-01-12 16:37:02 UTC
Nice comment, but more appropriate for 38249. What exactly makes you
believe that the problem is in core? You should try really harder to
blame us for your problems. 

I've connected to ide, printed the expression and got syntax error.
But never mind, that should still prove nothing. Even if I got one
instance,  then disable module and got another one that still does not
prove anything. That is the regular behaviour. 

You have to show us that there are really two instances of debugger in
memory at once and that they were both created by Lookup.getDefault.
Otherwise you are not describing bug in core.
Comment 12 Jan Jancura 2004-01-13 12:27:08 UTC
Still reproducible.
I have added folowing code to EnterpriseDebugger  (package
org.netbeans.modules.debugger.multisession):
    {
        Thread.dumpStack();
    }
    
    protected void finalize () {
        System.out.println("EnterpriseDebugger.finally");
    }

And second instance of Enterprise Debugger has been created when I
have reinstalled Welcome Screen module.
Comment 13 Jan Jancura 2004-01-13 12:28:41 UTC
Created attachment 12855 [details]
And old instance has not been released.
Comment 14 Jaroslav Tulach 2004-01-13 17:18:21 UTC
Hanzi, this is excelent investigation thank you. We have tracked the
problem more thru FolderLookup and
InstanceDataObject.Ser.instanceCreate and find out that the sinner is
BinaryFS or module manager that changes the FileObject.lastModified()
time of your settings file when welcome screen module is enabled/disabled.

Reassigning to core/modules.
Comment 15 Jesse Glick 2004-01-13 18:21:40 UTC
Petr do you feel like taking this? :-)
Comment 16 Petr Nejedly 2004-01-14 10:56:41 UTC
I had been affraid you'll ask...
Some comments:
The problem is not specific to BinaryFS, the same thing happens when
you use all-layers.xml caching as there is only one timestamp in both
cases.
In BinaryFS, it is a bit more complicated, as there are MLFS delegates
replaced and I'm not sure how would the MFS react.

Generally, I'm not sure on correct behaviour. Is it a bug if the
InstanceDO recreates the instance without direct reason. How does the
clients recognize it is "without direct reason"
Is user just touching a layer file a direct reason.
Should the debugger (or any other client) break just because user
touched a file?
Comment 17 Jesse Glick 2004-01-14 15:50:52 UTC
It's definitely not acceptable for the debugger to break just because
someone touches a file defined a layer - that is why issue #38249 is
filed separately. BTW I am removing MERCURY kw from this bug and
downgrading it because the bug the user cares about is #38249, which
can (and should!) be fixed independently of this issue, which I
consider an RFE for a more conservative object recreation policy
(potentially helpful for performance).

The problem with the timestamp is that InstanceDataObject assumes that
if the timestamp of an instance file varies from call to call that the
file contents must have changed - significantly enough to warrant
producing a new instance. However a layer-based file of course has no
well-defined timestamp so this assumption is a poor one. We could
perhaps always return 0L as the lastmod timestamp for any layer-based
file and hope for the best.
Comment 18 Jan Jancura 2004-01-14 16:54:42 UTC
Jesse, 
I do not care about implementation details. I would like to see some
definition how Lookup works. So, as I understand it you are not able
to guarantee that Lookup will work correctly in described case. OK, I
will find some other solution. But, be so kind and mention it in
JavaDoc for Lookup, please.
Comment 19 Jaroslav Tulach 2004-01-15 14:57:02 UTC
I think that Hanz is right "the definition how Lookup works" should be
somewhere. And as far as I know a maximum effort has been taken to
construct only one instance for Lookup.Item. MetaInfLookup does that.
FolderLookup keeps the DataObject and delegates to it.
InstanceDataObject keeps the same instance if the file does not
change. Moreover I believe that the tests in
core/test/unit/src/org/netbeans/core/lookup check for this behaviour
as well. That is why I believe the requested behaviour is clear, it is
broken and it is serious problem. That is why I made this P2 again. We
should fix it asap, I am relatively bugs-free, so I can do it with a
little help of you.

As I said I think there already is a test for this, but it seems that
the test runs in  some kind of artificial environment and not in the
real IDE state (Plain top manager, memoryfs). Please help me simulate
the real state and I will fix the problem.

Btw. 1st idea I had is to run the tests in IDE mode, but the install
of modules failed. I have not investigated further.

Btw. 2nd idea would be to initialize the (binary) caching for the
test, but I rely on your guidance to tell me how.

Comment 20 Jesse Glick 2004-01-15 15:38:12 UTC
OK, if you think we should guarantee this Lookup semantics, fine with me.

Re. writing a test that would cover caching: I'm not sure offhand what
you need to do. You may need to restart core at least once (which
would be a huge headache for a test), but maybe not. Probably
netbeans.user must be set. Most of the logic which is relevant for
getting the test to actually test caches is going to be in
ModuleLayeredFileSystem; run with -Dorg.netbeans.core.projects=0 to
trace what is going on.
Comment 21 Jaroslav Tulach 2004-01-21 08:33:40 UTC
Now the problem can be reproduced. Just run:

ant -f core/test/build.xml -Dxtest.attribs=cache

to simulate the problem.


Checking in bootstrap/src/org/netbeans/JarClassLoader.java;
/cvs/core/bootstrap/src/org/netbeans/JarClassLoader.java,v  <-- 
JarClassLoader.java
new revision: 1.13; previous revision: 1.12
done
Processing log script arguments...
More commits to come...
Checking in test/cfg-unit.xml;
/cvs/core/test/cfg-unit.xml,v  <--  cfg-unit.xml
new revision: 1.51; previous revision: 1.50
done
Processing log script arguments...
More commits to come...
Checking in
test/unit/src/org/netbeans/core/lookup/InstanceDataObjectModuleTest7.java;
/cvs/core/test/unit/src/org/netbeans/core/lookup/InstanceDataObjectModuleTest7.java,v
 <--  InstanceDataObjectModuleTest7.java
new revision: 1.7; previous revision: 1.6
done
Checking in
test/unit/src/org/netbeans/core/lookup/InstanceDataObjectModuleTestHid.java;
/cvs/core/test/unit/src/org/netbeans/core/lookup/InstanceDataObjectModuleTestHid.java,v
 <--  InstanceDataObjectModuleTestHid.java
new revision: 1.6; previous revision: 1.5
done
Checking in
test/unit/src/org/netbeans/core/lookup/LookupClassLoaderTest.java;
/cvs/core/test/unit/src/org/netbeans/core/lookup/LookupClassLoaderTest.java,v
 <--  LookupClassLoaderTest.java
new revision: 1.5; previous revision: 1.4
done
Checking in
test/unit/src/org/netbeans/core/lookup/MetaInfServicesTest.java;
/cvs/core/test/unit/src/org/netbeans/core/lookup/MetaInfServicesTest.java,v
 <--  MetaInfServicesTest.java
new revision: 1.8; previous revision: 1.7
done
Comment 22 Jaroslav Tulach 2004-01-22 13:28:18 UTC
Created attachment 13024 [details]
Proposed fix
Comment 23 Jaroslav Tulach 2004-01-22 13:37:41 UTC
I am ready to commit the patch as it seems to fix the original
problem. It is based on proper (better) implementation of
FileObject.lastModified() for BinaryFS. I've enhanced the
CacheManagerTest to verify the correct behaviour of lastModified, but
I have not succeed to implement correct behaviour for all the existing
caches: BinaryFS and NonCaching differ in one case, XMLLayer cache is
completely broken. I would suggest to delete XMLLayerCacheManager as
it is obsoleted and I could fix the NonCaching and BinaryFS difference
if somebody tells me what is the correct behavior. But this probably
does not prevent the integration.

Format of Binary cache file has been enhanced to list all base URLs
(usually references to xml layer files) at the begining and each
MemFile has an integer pointer to the list.
Comment 24 Jesse Glick 2004-01-22 16:09:06 UTC
Not sure what

    lastModified = -10 - base;

is supposed to do; needs a comment I think. :-)

The patch looks OK, though I am not sure I understand why we even care
about lastModified for layer files. Can't you just always return 0L
for *any* file in XMLFileSystem or BinaryFS? That would certainly
ensure that the timestamp did not change during a cache recreation.
Who really cares when the layer.xml was last changed anyway? Is it
necessary for lastModified to change when you reload test modules, or
is it enough that disabling the module fires a file removal and
reenabling it fires a new file creation?

Re. XMLLayerCacheManagerImpl - it's true it should not be used any
more. It is still potentially useful to have around in case you want
to get a dump of merged layers for debugging purposes. I wouldn't be
upset if it were deleted.
Comment 25 Jaroslav Tulach 2004-01-22 18:14:52 UTC
lastModified is cache. When < 0 it is unitialized, when >= 0 it
contains the time. -10 could be any other suitable constant.

I was affraid of returning OL as this would not work when module is
reinstalled, its new version is downloaded. Then the resource file
could change, but the lastModified time would still remain the same.
That is why I implemented the solution that usually uses time from
some "real" files.

I'll delete the XMLLayerCacheManagerImpl and apply the patch tomorrow.
Comment 26 Jaroslav Tulach 2004-01-23 12:23:39 UTC
/cvs/openide/src/org/openide/filesystems/XMLFileSystem.java,v  <-- 
XMLFileSystem.java
new revision: 1.73; previous revision: 1.72
done
Processing log script arguments...
More commits to come...
Checking in
core/src/org/netbeans/core/projects/cache/BinaryCacheManager.java;
/cvs/core/src/org/netbeans/core/projects/cache/BinaryCacheManager.java,v
 <--  BinaryCacheManager.java
new revision: 1.7; previous revision: 1.6
done
Checking in core/src/org/netbeans/core/projects/cache/BinaryFS.java;
/cvs/core/src/org/netbeans/core/projects/cache/BinaryFS.java,v  <-- 
BinaryFS.java
new revision: 1.8; previous revision: 1.7
done
Checking in
core/src/org/netbeans/core/projects/cache/ParsingLayerCacheManager.java;
/cvs/core/src/org/netbeans/core/projects/cache/ParsingLayerCacheManager.java,v
 <--  ParsingLayerCacheManager.java
new revision: 1.9; previous revision: 1.8
done
Removing
core/src/org/netbeans/core/projects/cache/XMLLayerCacheManagerImpl.java;
/cvs/core/src/org/netbeans/core/projects/cache/XMLLayerCacheManagerImpl.java,v
 <--  XMLLayerCacheManagerImpl.java
new revision: delete; previous revision: 1.6
done
Processing log script arguments...
More commits to come...
Checking in core/test/cfg-unit.xml;
/cvs/core/test/cfg-unit.xml,v  <--  cfg-unit.xml
new revision: 1.53; previous revision: 1.52
done
Processing log script arguments...
More commits to come...
Checking in
core/test/unit/src/org/netbeans/core/lookup/InstanceDataObjectModule38420Test.java;
/cvs/core/test/unit/src/org/netbeans/core/lookup/InstanceDataObjectModule38420Test.java,v
 <--  InstanceDataObjectModule38420Test.java
new revision: 1.2; previous revision: 1.1
done
Checking in
core/test/unit/src/org/netbeans/core/lookup/InstanceDataObjectModuleTestHid.java;
/cvs/core/test/unit/src/org/netbeans/core/lookup/InstanceDataObjectModuleTestHid.java,v
 <--  InstanceDataObjectModuleTestHid.java
new revision: 1.7; previous revision: 1.6
done
Processing log script arguments...
More commits to come...
Checking in
core/test/unit/src/org/netbeans/core/projects/cache/BinaryCacheManagerTest.java;
/cvs/core/test/unit/src/org/netbeans/core/projects/cache/BinaryCacheManagerTest.java,v
 <--  BinaryCacheManagerTest.java
new revision: 1.5; previous revision: 1.4
done
Checking in
core/test/unit/src/org/netbeans/core/projects/cache/CacheManagerTestBaseHid.java;
/cvs/core/test/unit/src/org/netbeans/core/projects/cache/CacheManagerTestBaseHid.java,v
 <--  CacheManagerTestBaseHid.java
new revision: 1.5; previous revision: 1.4
done
Checking in
core/test/unit/src/org/netbeans/core/projects/cache/NonCacheManagerTest.java;
/cvs/core/test/unit/src/org/netbeans/core/projects/cache/NonCacheManagerTest.java,v
 <--  NonCacheManagerTest.java
new revision: 1.2; previous revision: 1.1
done
Removing
core/test/unit/src/org/netbeans/core/projects/cache/XMLLayerCacheManagerImplTest.java;
/cvs/core/test/unit/src/org/netbeans/core/projects/cache/XMLLayerCacheManagerImplTest.java,v
 <--  XMLLayerCacheManagerImplTest.java
new revision: delete; previous revision: 1.4
done
Comment 27 Marian Mirilovic 2004-02-26 17:19:10 UTC
fixed by reporter - verified