Fix/plugin loading and cycles#1036
Open
TheWakz wants to merge 9 commits into
Open
Conversation
Stop caching InputStream instances in plugin URL connections. Each getInputStream call now opens a fresh stream from the backing ByteSource, avoiding partially-read or already-closed streams on repeated access.
Implement findResources so plugin resources are visible to APIs that enumerate resources. This improves compatibility with ServiceLoader, SPI discovery, and code that searches META-INF/services or other resources through ClassLoader#getResources.
Include the plugin id in generated plugin resource URLs. This avoids ambiguous resource URLs when multiple plugins contain resources with the same path but different contents.
Resolve plugin dependencies before enablement and pass the resolved dependency classloaders directly into each plugin classloader. This fixes class visibility for plugins loaded in the same batch, where dependencies exist in the staged load set but are not yet committed to the main plugin map.
…loading Register the plugin classloader as parallel-capable and synchronize local class lookup on getClassLoadingLock(name). Since the classloader is now marked as parallel-capable, both findClass and direct lookupClass calls must use the same fine-grained class loading lock discipline to avoid racing defineClass for the same type under concurrent loading.
Use dependencyLoader.loadClass(name) instead of calling findClass directly. This preserves parent delegation, class loading locks, and already-loaded class checks when resolving classes from plugin dependencies.
Store the plugin instance before invoking onEnable so rollback can call onDisable when enablement fails after partial initialization. This makes failed plugin loads clean up plugin state more reliably.
Track unloaded plugins during recursive unload. This prevents the same dependant plugin from being unloaded multiple times when it is reachable through multiple dependency paths, such as diamond dependency graphs.
…rsal Track currently visiting plugins during recursive enablement and dependant collection. This ensures that circular dependencies within the plugin graph are explicitly detected and rejected with an exception, preventing infinite loops or stack overflows during topology traversal.
97a193d to
6c80b7a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What's new
PluginClassLoaderImplas parallel-capable to prevent potential thread deadlocks during concurrent plugin loading.What's fixed & Refactored
1. Lifecycle & Topology Robustness
PluginException, preventing infinite loops orStackOverflowError.onEnable. If enablement fails after partial initialization, the system reliably callsonDisableto clean up the plugin state.2. Class Loading & Resource Isolation
dependencyLoader.loadClass(name)instead of callingfindClassdirectly. This preserves parent delegation, class loading locks, and already-loaded class checks.getClassLoadingLock(name)to align with the parallel-capable classloader discipline and avoid racingdefineClass.URL.getPath().InputStreamcaching in plugin URL connections with fresh streams from the backingByteSourceto prevent closed/partially-read stream bugs.