Null pointer exception on quitting IntelliJ due to plugin cleanup on project close.
Answered
Hello,
I am experiencing a Null Pointer exception that appears to be caused when my plug in runs on project close.
Error:
2024-12-12 11:04:29,693 [ 49583] SEVERE - #c.i.u.c.BoundedTaskExecutor - Cannot invoke "com.intellij.openapi.application.Application.isReadAccessAllowed()" because the return value of "com.intellij.openapi.application.ApplicationManager.getApplication()" is null
java.lang.NullPointerException: Cannot invoke "com.intellij.openapi.application.Application.isReadAccessAllowed()" because the return value of "com.intellij.openapi.application.ApplicationManager.getApplication()" is null
at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.attemptComputation(NonBlockingReadActionImpl.java:548)
at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.executeSynchronously(NonBlockingReadActionImpl.java:492)
at com.intellij.openapi.application.impl.NonBlockingReadActionImpl.executeSynchronously(NonBlockingReadActionImpl.java:222)
at com.intellij.util.indexing.events.ChangedFilesCollector.processFilesInReadActionWithYieldingToWriteAction(ChangedFilesCollector.java:324)
at com.intellij.util.indexing.events.ChangedFilesCollector.lambda$ensureUpToDateAsync$2(ChangedFilesCollector.java:207)
at com.intellij.util.concurrency.BoundedTaskExecutor.doRun(BoundedTaskExecutor.java:249)
at com.intellij.util.concurrency.BoundedTaskExecutor.access$200(BoundedTaskExecutor.java:31)
at com.intellij.util.concurrency.BoundedTaskExecutor$1.executeFirstTaskAndHelpQueue(BoundedTaskExecutor.java:227)
at com.intellij.util.ConcurrencyUtil.runUnderThreadName(ConcurrencyUtil.java:218)
at com.intellij.util.concurrency.BoundedTaskExecutor$1.run(BoundedTaskExecutor.java:215)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:702)
at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:699)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1.run(Executors.java:699)
at java.base/java.lang.Thread.run(Thread.java:840)
2024-12-12 11:04:29,696 [ 49586] SEVERE - #c.i.u.c.BoundedTaskExecutor - IntelliJ IDEA 2023.2.6 Build #IC-232.10300.40
2024-12-12 11:04:29,696 [ 49586] SEVERE - #c.i.u.c.BoundedTaskExecutor - JDK: 17.0.10; VM: OpenJDK 64-Bit Server VM; Vendor: JetBrains s.r.o.
2024-12-12 11:04:29,696 [ 49586] SEVERE - #c.i.u.c.BoundedTaskExecutor - OS: Mac OS X
Context:
- I perform command-Q (quit) in IntelliJ
- I have a ProjectManagerListener which overrides the default projectClosing method.
- The projectClosing method checks if my plug-in is active and if so deactivates it.
- Deactivation includes writing a log file and creating a zip file within the project. In short, files in the project are changed by the plugin when it is being shutting down.
- The plugin completes its deactivation before the projectClose method of the same listener is called.
- The above null pointer exception occurs afterwards on application shutdown.
Hypothesis: The files being changed during plug=in deactivation causes the thread that checks for new files to run when it is not expected to.
Question: Is there a better place for me to do plug-in deactivation than at this point? Thanks!
Sincerely,
Alex
Please sign in to leave a comment.
Hi Alex,
Your observation seems correct to me.
The use case is unclear to me: why do you deactivate your plugin on project close, not on application close? Are users aware of plugin deactivation?
Hi Karol,
Thank you! The plug-in I am developing creates a testing (as in school/university) environment for a programming test. When a project is closed, the student's code is packaged up to be submitted. The student would be made aware of this.
If the project is closed without quitting intellij, then all is fine. It's when the application is closed, which causes the project to close first, which then sets up the race condition between the application shutting down and the file collector thread.
Would calling VfsUtil#markDirtyAndRefresh on all roots after all file activity is complete be the way to avoid this situation?
Quick update: I tried using VfsUtil#markDirtyAndRefresh. This did not solve the problem.
Thanks for clarification.
Did you pass the false flag for the
async
parameter?If it doesn't help, I would try to call
com.intellij.openapi.vfs.VirtualFileSystem.refresh(false)
.Hi Karol,
Thanks! I did pass false for async. I just tried
com.intellij.openapi.vfs.VirtualFileSystem.refresh(false)
, but alas, that did not fix the issue. It's not critical in the sense that the system shuts down and all requisite functionality is present. But, I would prefer to avoid null pointer exceptions as a general rule. :)Sincerely,
Alex
Another idea is to implement
VetoableProjectManagerListener
and return true only if you could create and successfully refreshed VFS, and show an error dialog otherwise. It can be useful for students to report unsuccessful project packaging to you, so you can gather it manually or something.Also, instead of
VirtualFileSystem.refresh()
, tryVirtualFileManager.syncRefresh()
. Keep in mind that it can be slow and cause UI freeze, but I think it is allowed in your specific usage context.Hi Karol,
I think I solved it in a relatively light-weight manner.
I added the VetoableProjectManagerListener, but it does not fire (does not call canClose()) I suspect this maybe because it is a recent addition, and is not hooked in the Community branch (which is what the sandbox is using). I note that this listener is not listed in the documentation, which is why I believe that it is “new”. I did add code is in this listener to ask the student to shut down the plug in before closing the project. This would be the preferred path. But, as a back-up
I have observed that AppLifecycleListener#appWillBeClosed is called before ProjectManagerListener#projectClosing, so if the user quits the IDE, then the former listener shuts down the plug in. Otherwise, if the user just closes the project, then the latter listener does the shut down.
Thanks again for all your help!
ttyl
Alex
Good to hear you solved it.
Just for reference,
VetoableProjectManagerListener
has been available since 2017. Maybe you registered it incorrectly? Its Javadoc says:Hi Karol,
Thanks! That fixed it. I missed the requirement in the Javadoc.
Note: I could not find the VetoableProjectManagerListener interface in the list of all extensions and listeners. (https://plugins.jetbrains.com/docs/intellij/intellij-platform-extension-point-list.html). I am not sure if that is intentional or not.
Thanks again for your help and have a good winter break!
Sincerely,
Alex