Do not eager load non-eager module fragments.
When starting NB IDE, I have noticed that *-compat8 module fragments are enabled - although they should not. commit-validation checks that all dependencies in the distribution are up-to-date, so no module-auto-deps injection should inject compat8 module in standard NetBeans: they are intended for 3rd party plugins with obsolete (pre-9) dependencies.
I have found that for some reason (already forgotten, and not documented in comments / commit messages), all registered fragments are enabled, whent he host module is enabled. This might be because if a fragment should enable later, after it's host's ClassLoader is already in use, the whole IDE would have to restart, since the ClassLoader's answers to findResource calls might change and there's no way how to re-link already loaded classes.
I changed the enabling process, and tested it on:
- NetBeans 9 and NetBeans 11.0
nbjavacloading. Nbjavac impl is a fragment hosted in nbjavac library. For testing purposes, I've checked out and built the old NB distribution with module system patched with these changes. - Javafx. Javafx support consists of several fragments one of which is selected by its OS dependency; these fragments nest in the core javafx support module.
- Of course the regular test suite
The enable process now works that
- an
autoload fragmentis enabled only because of dependency, or because it provides required or needed feature. Autoload fragments that are not requested are not loaded into the host. - an
eager fragmentis enabled whenever all its dependencies are satisfied and its host is enabled.
I have added testcases, that are based on the JavaFX scenario:
- org.foo.javafx (core) that REQUIRES some platform impl
- two org.foo.javafx.[linux,windows] impls that both PROVIDE the platform impl, but each require a different OS token; so just one of them is selected
- an EAGER fragment that depends on org.foo.javafx.linux, so it should be automatically loaded whenever the linux fragment is
Summary of changes
In a dependency graph that is used to sort modules, the host module no longer depends on its fragments. Each fragment depends on its host. This ensures that host is enabled first, fragment modules are enabled later. Dependencies of fragments are injected into the host module, except hosting module itself and other fragments. Since the fragment's classloader items merge with the host, this dependency injection ensures that all libraries required by classes merged into host are enabled before the host module.
In a calculateParents all fragments are removed from the hosting module's parents, if they appear there. Fragment's dependencies are, to the contrary, added to the host's regular parents. This ensures that fragment classes injected into host's classloader load successfully (their library dependencies form parent classloaders) and that there's not circular delegation between host -> fragment -> host (a fragment typically depend on its host).
curious: did you measure how many modules are loaded less now? Or would this only occur during specific use cases?
curious: did you measure how many modules are loaded less now? Or would this only occur during specific use cases?
Not much of them::
org.openide.execution.compat8
org.netbeans.modules.project.ant.compat8/1
org.netbeans.modules.java.source.compat8
org.netbeans.api.progress.compat8
But the change should eventually avoid bytecode load patching magic in the regular modules, since no "compatibility mode" (= patches) will be requested for them - unless the user activates a plugin that depends on an obsolete API (then a restart would be required).