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

AJC throws NPE when number of classpath entries is over the limit #125

Closed
raimondas67 opened this issue Feb 19, 2022 · 7 comments · Fixed by #127
Closed

AJC throws NPE when number of classpath entries is over the limit #125

raimondas67 opened this issue Feb 19, 2022 · 7 comments · Fixed by #127
Labels
bug Something isn't working
Milestone

Comments

@raimondas67
Copy link

  1. Java 11 (AdoptOpenJDK jdk-11.0.10.9-hotspot)
  2. aspectj-maven-plugin v1.14
  3. aspectjtools v1.9.7
  4. pom.xml snippet
org.codehaus.mojo aspectj-maven-plugin 1.14 11 11 11 true true xxx.xxx.xxx xxx-aspects **/*.aj **/*.class cantFindType=ignore ${project.build.directory}/classesToWeav process-classes compile org.aspectj aspectjtools 1.9.7 compile
  1. Stack trace:
    [ERROR] Failed to execute goal org.codehaus.mojo:aspectj-maven-plugin:1.14.0:compile (default) on project xxx: AJC compiler errors:
    [ERROR] abort ABORT -- (NullPointerException) null
    [ERROR] null
    [ERROR] java.lang.NullPointerException
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.batch.ClasspathJmod.getModulesDeclaringPackage(ClasspathJmod.java:146)
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.batch.ClasspathLocation.isPackage(ClasspathLocation.java:184)
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.batch.ClasspathJmod.findClass(ClasspathJmod.java:55)
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.batch.FileSystem.internalFindClass(FileSystem.java:544)
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.batch.FileSystem.findClass(FileSystem.java:464)
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.batch.FileSystem.findType(FileSystem.java:617)
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.env.IModuleAwareNameEnvironment.findType(IModuleAwareNameEnvironment.java:101)
    [ERROR] at org.aspectj.ajdt.internal.core.builder.StatefulNameEnvironment.findType(StatefulNameEnvironment.java:101)
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment.createPlainPackage(LookupEnvironment.java:1151)
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope.buildTypeBindings(CompilationUnitScope.java:136)
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment.buildTypeBindings(LookupEnvironment.java:487)
    [ERROR] at org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment.buildTypeBindings(AjLookupEnvironment.java:1471)
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.Compiler.internalBeginToCompile(Compiler.java:870)
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.Compiler.beginToCompile(Compiler.java:395)
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:449)
    [ERROR] at org.aspectj.org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:427)
    [ERROR] at org.aspectj.ajdt.internal.core.builder.AjBuildManager.performCompilation(AjBuildManager.java:1096)
    [ERROR] at org.aspectj.ajdt.internal.core.builder.AjBuildManager.performBuild(AjBuildManager.java:275)
    [ERROR] at org.aspectj.ajdt.internal.core.builder.AjBuildManager.batchBuild(AjBuildManager.java:188)
    [ERROR] at org.aspectj.ajdt.ajc.AjdtCommand.doCommand(AjdtCommand.java:103)
    [ERROR] at org.aspectj.ajdt.ajc.AjdtCommand.runCommand(AjdtCommand.java:47)
    [ERROR] at org.aspectj.tools.ajc.Main.run(Main.java:372)
    [ERROR] at org.aspectj.tools.ajc.Main.runMain(Main.java:250)
    [ERROR] at org.codehaus.mojo.aspectj.AbstractAjcCompiler.execute(AbstractAjcCompiler.java:568)
    [ERROR] at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137)
    [ERROR] at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:210)
    [ERROR] at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:156)
    [ERROR] at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:148)
    [ERROR] at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117)
    [ERROR] at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
    [ERROR] at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56)
    [ERROR] at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
    [ERROR] at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:305)
    [ERROR] at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192)
    [ERROR] at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105)
    [ERROR] at org.apache.maven.cli.MavenCli.execute(MavenCli.java:956)
    [ERROR] at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:288)
    [ERROR] at org.apache.maven.cli.MavenCli.main(MavenCli.java:192)
    [ERROR] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [ERROR] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    [ERROR] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    [ERROR] at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    [ERROR] at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
    [ERROR] at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
    [ERROR] at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
    [ERROR] at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
    [ERROR]

The problem is that ClasspathJmod objects for JMOD files have zipFile=null

image

Problem can be solved by providing system property -Dorg.aspectj.weaver.openarchives=2000 when running maven build. It turns out due to big number of classpath entries some of these entries are closed during processing:

org.aspectj.org.eclipse.jdt.internal.compiler.batch.ClasspathJar

// AspectJ Extension
private void ensureOpen() throws IOException { //line 403
if (zipFile != null) return; // If its not null, the zip is already open
if (openArchives.size()>=maxOpenArchives) {
closeSomeArchives(openArchives.size()/10); // Close 10% of those open <-- closed and likely not re-initialized so that zipFile remains null
}
zipFile = new ZipFile(file);
openArchives.add(this);
}

@aclement
Copy link
Contributor

The fix here seems relatively straightforward I suspect. The Java Module support added in Java9 probably added new routes that are attempting to access the zipFile object. We should look at those routes and check if they are guarded by a call to ensureOpen() - if they are not then it should be added. Basically all attempts to access that zipFile field need to be guarded in case it has been closed due to too many archives being simultaneously open.

@kriegaex
Copy link
Contributor

kriegaex commented Feb 23, 2022

I have done quick local fix, adding ensureOpen() to several routes where theoretically there could be access to a closed jar/zip file. This fixes the problem for me in the test scenario I described on the mailing list, i.e. compilation of a simple class with -Dorg.aspectj.weaver.openarchives=20, which would otherwise always fail of the (too) small number of open archives. I am going to analyse if maybe I can remove snippet from a few routes where it is done multiple times, but I think better safe than sorry. I also had to make ensureOpen() protected to be able to call it from a subclass where it was necessary. I simply added this to most places, ...

// AspectJ Extension
try {
  ensureOpen();
}
catch (IOException e) {
  throw new RuntimeException(e);
}
// End AspectJ Extension

... rather than catching the error and logging it or returning null or false. I think we want compilation to fail if an error happens in that method, don't we, @aclement? In some places I saw that we return values instead or just fold the ensureOpen() call into an existing try-catch block. WDYT?


Update: Andy, shouldn't this be a set rather than a list?

private static List openArchives = new ArrayList();

There seems to be the danger of duplicated entries there, especially if ensureOpen() is called from many places and looks like this?

private void ensureOpen() throws IOException {
	if (zipFile != null) return; // If its not null, the zip is already open
	if (openArchives.size()>=maxOpenArchives) {
		closeSomeArchives(openArchives.size()/10); // Close 10% of those open
	}
	zipFile = new ZipFile(file);
	openArchives.add(this);
}

If zipFile != null, it will be added to the list unconditionally, which looks like it will not just bloat the list, but also make it inconsistent.

@kriegaex
Copy link
Contributor

@raimondas67, I deployed an AspectJ 1.9.9-SNAPSHOT to Sonatype OSSRH. Please test if your problem goes away without -Dorg.aspectj.weaver.openarchives=2000 and maybe even try to stress-test the situation more by setting a lower limit. I know, it is probably going to make compilation slower, but try to experiment first without the setting (defaults to 1000), then with 500, 250, 100 or even 50, if it still continues to work like that. I just want to know if the error is gone.

See #95 (comment) for an example project showing how to upgrade to the latest AspectJ version when using dev.aspectj:aspectj-maven-plugin:1.13.1. Just make sure to set the aspectjtools snapshot as a plugin dependency.

As for your second problem from the mailing list, I think you still have not created an issue for it. Can you help us to reproduce it?

kriegaex added a commit to kriegaex/aspectj that referenced this issue Feb 23, 2022
after fixing something related to eclipse-aspectj#125 there

Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
kriegaex added a commit to kriegaex/eclipse.jdt.core that referenced this issue Feb 23, 2022
ClasspathJar.openArchives needs more safeguards when accessing possibly
closed zip files (actually classpath JARs), making sure the get
re-opened by calling ClasspathJar.ensureOpen().

Relates to eclipse-aspectj/aspectj#125.

Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
Change-Id: If20bae3d6169559de97febc529465196367524b9
kriegaex added a commit to kriegaex/aspectj that referenced this issue Feb 23, 2022
This test fails when run against AspectJ 1.9.8 with JDT Core 1.9.8.RC3.
It passes when using the latest JDT Core 1.9.9-SNAPSHOT. It sets system
property 'org.aspectj.weaver.openarchives=20', provoking open classpath
JAR file exhaustion when compiling a simple class with AJC, i.e. JARs
are being forcibly closed and automatically re-opened, as soon as they
are needed. Before the JDT Core bugfix, this test causes:

java.lang.NullPointerException
  at ....compiler.batch.ClasspathJmod.getModulesDeclaringPackage

With the bugfix incorporated into AspectJ Tools, the problem is gone.

Note: New test dependency 'io.github.bmuskalla:scoped-system-properties'
helps to test compilation with the temporarily changed global system
property in isolation, saving the environment in a thread-local
variable and later cleanly restoring the original values again. If we
ever switch to parallel test execution, this would otherwise influence
other tests and potentially cause weird side effects. Better safe than
sorry.

Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
kriegaex added a commit to kriegaex/aspectj that referenced this issue Feb 23, 2022
This should make Bugs198Tests.testGitHub_125 green, fixing problem eclipse-aspectj#125
in AspectJ.

Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
@kriegaex
Copy link
Contributor

@aclement, I inspected the existing logic and think it is safe to continue using the list instead of a set, mainly because it is more predictable and chronological which classpath JARs get closed on cache exhaustion (first in, first out). We would have to use a sorted set or map with a sortable timestamp or so as the key, which would entail more changes than I want to implement now. But I think I was pretty thorough with where to put the safeguards, my checks also encompassing subclasses. I think that for now we are OK, at least no worse than before. If @raimondas67 also re-tests successfully, I think the two PRs for JDT Core and AspectJ are ready to be reviewed and merged, whenever you have cycles to look into them.

kriegaex added a commit to kriegaex/aspectj that referenced this issue Feb 24, 2022
…#125

This commit is not meant to be merged into the main branch, but used to
trigger the complete test suite with the lowest possible limit for the
open JAR file AspectJ introduced in JDT Core. As some changes in
eclipse-aspectj/eclipse.jdt.core#19 change behaviour (throwing
exceptions where before they were just logged and dummy results
returned), we want to find out if this breaks any existing tests when
provoking cache exhaustion, i.e. closing and re-opening of JARs.

Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
@kriegaex
Copy link
Contributor

kriegaex commented Feb 24, 2022

For the record: In addition to the regression test I added, which always runs with -Dorg.aspectj.weaver.openarchives=20, I also pushed a private branch with commit eab7758, resulting in the full build with hundreds of tests doing AJC compilation also with -Dorg.aspectj.weaver.openarchives=20. The build passed. Because old builds will eventually be deleted on GitHub, here is a screenshot:

image

@raimondas67
Copy link
Author

raimondas67 commented Feb 24, 2022 via email

@kriegaex
Copy link
Contributor

@raimondas67, please try not to use full quotes. You could also edit the issue description, using code blocks for better formatting.

Classpath related NPE exception is fixed with this 1.9.9-SNAPSHOT release

Thanks for the feedback, good news. 🙂

however, I’m still getting the same AJC compiler error

Yes, of course. I did not fix it, having too little information to reproduce it. I never thought that the two issues were related anyway, which is why I already asked you twice to create an extra issue with more information for the second issue. Thanks in advance.

aclement added a commit that referenced this issue Feb 25, 2022
Fix classpath JAR close & re-open problem in AJC
@kriegaex kriegaex added this to the 1.9.9 milestone Mar 21, 2022
@kriegaex kriegaex added the bug Something isn't working label Mar 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants