Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix class and resource loading in maven plugin #20344

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

mcollovati
Copy link
Collaborator

Description

Introduces a custom class loader for ClassFinder to ensure resources and classes are loaded first from project dependencies and then from the plugin classloader. Also, augments plugin classloader adding URLs of project artifacts to prevent class cast and class not found exceptions.

Fixes #19616
Fixes #19009

Type of change

  • Bugfix
  • Feature

Checklist

  • I have read the contribution guide: https://vaadin.com/docs/latest/guide/contributing/overview/
  • I have added a description following the guideline.
  • The issue is created in the corresponding repository and I have referenced it.
  • I have added tests to ensure my change is effective and works as intended.
  • New and existing tests are passing locally with my change.
  • I have performed self-review and corrected misspellings.

Additional for Feature type of change

  • Enhancement / new feature was discussed in a corresponding GitHub issue and Acceptance Criteria were created.

Introduces a custom class loader for ClassFinder to ensure resources and
classes are loaded first from project dependencies and then from the plugin
classloader. Also, augments plugin classloader adding URLs of project
artifacts to prevent class cast and class not found exceptions.

Fixes #19616
Fixes #19009
Copy link

github-actions bot commented Oct 25, 2024

Test Results

1 142 files  ± 0  1 142 suites  ±0   1h 20m 54s ⏱️ - 2m 34s
7 474 tests ± 0  7 424 ✅ ± 0  50 💤 ±0  0 ❌ ±0 
7 827 runs  +45  7 767 ✅ +43  60 💤 +2  0 ❌ ±0 

Results for commit bc3395e. ± Comparison against base commit e7d7e75.

♻️ This comment has been updated with latest results.

@mshabarov mshabarov requested review from tltv and Artur- October 28, 2024 12:37
@mcollovati
Copy link
Collaborator Author

Tested with maven 3.6.3, 3.8.6, 3.9.9 and 4.0.0-beta-4; seems to work correctly.
Tested also with mvnd 1.0.2; it requires custom handling because it provides a custom PluginRealmCache

Copy link

sonarcloud bot commented Oct 29, 2024

}

@SuppressWarnings("unchecked")
private void resetPluginClassLoader(ClassRealm classRealm) {
Copy link
Member

@tltv tltv Oct 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we do the reset instead by customizing URLClassLoader to support removeUrl.

ClassRealm seems to call super.addURL, which is implemented in URLClassLoader. URLCLassLoader uses internally URLClassPath field called ucp, which could be overridden by reflection to temporarily add support to remove urls that were added temporarily.
ClassRealm has already addUrl and getUrls() which means we can check beforehand which urls were actually added.

If I have understood this right, doing reset like this would mean that we don't have to mind caching.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting idea.
I suppose we can't just replace the ClassRealm ucp because some URLs might already have been loaded, making it more difficult to detect which URLs should be removed after execution.
But we can probably have a URLClassPath decorator with the added URLs, that first loads from itself and then delegates to the original ucp.

I'll give it a try.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like URLClassPath is from jdk.internal.loader, so not exported to external modules.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so reflection doesn't work. I saw this VM argument to enable it for ClassLoader where ucp apparently used to be in older Java versions: --add-opens java.base/jdk.internal.loader=ALL-UNNAMED.
But even if it could help, you are probably right it's not helping really as URLs might be already loaded and cached anyway.

This method is still something I would rather not add except as last option, but I don't have more suggestions atm.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--add-opens should be added by the end user in MAVEN_OPTS or similar, so it would also be some sort of breaking change.

Honestly, I run out of ideas on how to fix class loading issues in less tricky ways.
Forking the JVM to execute mojo logic might help, but my guess is that it will bring in many other kinds of problems.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PluginRealmCache is internal non-public API, which could change on every maintenance release of the maven-core in worst case scenario.

Maybe it would make sense to give a try with "forking JVM" way, especially if it could work for example easier with prepare-frontend goal first to start with. Assuming there would be differences between goals and prepare-frontend is simplest.

But anyway if we keep this method, I would like it to be extracted in internal utility class as a static method, and also split to methods for handling mvnd and default reset logic separately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
3 participants