SettingsEditor marking dirty/modified immediately
I'm running into a general problem with setting editor dialog subclasses of SettingsEditor opened via my plugin. The problem is 2-fold and both problems are related:
Scenario: open up a new dialog powered by the SettingsEditor class. DO NOT touch any ui component that modifies the configuration/settings backed by the editor. Click 'Ok' to save the dialog.
1) I see no way to perform validation on the dialog because SettingsEditor#applyEditorTo is not invoked.
2) The configuration in the dialog is not saved. Again, this because the applyEditorTo() method is not invoked. See DeployToServerSettingsEditor#applyEditorTo for an example of how the configuration would be set. This leads to downstream NPEs due to the null configuration.
So the basic question is how can these editors be marked "dirty" or "modified" immediately upon loading so that validation can be performed upon saving (and the configuration saved)?
Please sign in to leave a comment.
How do you create and show SettingsEditor? Do you return it from RunConfiguration#getConfigurationEditor method?
Anyway ConfigurationException should be thrown from applyEditorTo method only in rare cases when entered data is so wrong that it cannot be saved. ConfugurationException blocks user from closing dialog, it may be inconvenient. In regular cases it's better to highlight incorrect values in UI but allow to save it and close the dialog. If you're implementing an editor for a run configuration there is 'RunConfiguration#checkConfiguration' method for that.
Hi Nikolay,
Thanks for getting back to me. In this particular case, I am returning the SettingsEditor from a subclass of DeploymentConfigurator#createEditor. So specifically I am dealing with SettingsEditor<DeploymentConfiguration>.
I take your point about limiting the use throwing ConfigurationException. However, there is also the related point:
I created a custom DeploymentSourceType which is loaded when the SettingsEditor first opens. If the user doesn't touch the UI (e.g. modify any fields that touch the configuration) then when the user tries to execute the deployment (clicks run) then there is a downstream NPE in DeployToServerRunConfiguration#checkConfiguration() because the configuration is never created. I worked around this problem by manually creating the DeploymentConfiguration in the run configuration (I have a handle on it by overriding DeploymentSourceType#setBuildBeforeRunTask). This is a bit hacky and I feel like this should be handled by the platform:
Its possible I am missing something, but there seems to be a general problem with the initialization of these settings editors: the validation and saving of the configuration behaves differently if the dialog is opened and then immediately executed than if the user were to actually trigger a UI event handler.
Hi Etan,
why your DeploymentSourceType is loaded only when the SettingsEditor opens? DeploymentSourceType is an extension point, its implementation should be registered in plugin.xml and their instances will be created by the platform when ExtensionPointName#getExtensions() is called.
And how can you get NPE in DeployToServerRunConfiguration#checkConfiguration? myDeploymentConfiguration is initialized from @NotNull method DeploymentConfigurator#createDefaultConfiguration so if myDeploymentSource is initialized it cannot be null.
Hi Nikolay,
You're right, I mispoke when I said that my custom DeploymentSourceType is loaded only when the SettingEditor opens. In fact, it is registered as an extension point (programmatically in my case, but this shouldn't matter).
I was originally going to reply with a barebones plugin setup to reproduce this issue. However, I noticed that the very same problem (the null pointer in DeployToServerRunConfiguration#checkConfiguration) is reproducible in bundled IJ UE plugins. Here are steps to reproduce the issue using the bundled Heroku plugin:
Tested on IJ UE 2016.1.1 (build 145.597)
1) Open a project that has ModuleDeploymentSourceType(s) (any simple project with modules should work)
2) Open Settings -> Build, Execution, Deployment -> Clouds
3) Create a new Heroku cloud. Input any email / password. Click OK to save.
4) In the Application Servers panel that appears, right click on the Heroku entry and click Deploy -> Create
5) DO NOT edit any of the UI elements in the Deployment Configuration window that opens. Just click 'Run'.
=> NullPointerException (See the bottom of this message for the stack trace)
If you can confirm this problem, let me know if you'd like me to file a bug. Thanks for your help.
Exception in thread "ApplicationImpl pooled thread 43" java.lang.NullPointerException
at com.intellij.remoteServer.impl.configuration.deployment.DeployToServerRunConfiguration.checkConfiguration(DeployToServerRunConfiguration.java:126)
at com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl.checkSettings(RunnerAndConfigurationSettingsImpl.java:435)
at com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl.checkSettings(RunnerAndConfigurationSettingsImpl.java:430)
at com.intellij.execution.impl.RunManagerImpl$7.fun(RunManagerImpl.java:1104)
at com.intellij.execution.impl.RunManagerImpl$7.fun(RunManagerImpl.java:1085)
at com.intellij.ui.DeferredIconImpl.evaluate(DeferredIconImpl.java:287)
at com.intellij.ui.DeferredIconImpl$2$1$1.run(DeferredIconImpl.java:158)
at com.intellij.ui.IconDeferrerImpl.evaluateDeferred(IconDeferrerImpl.java:122)
at com.intellij.ui.DeferredIconImpl$2$1.run(DeferredIconImpl.java:155)
at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:1178)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils$2.run(ProgressIndicatorUtils.java:80)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils$5.run(ProgressIndicatorUtils.java:141)
at com.intellij.openapi.progress.impl.CoreProgressManager$2.run(CoreProgressManager.java:142)
at com.intellij.openapi.progress.impl.CoreProgressManager.a(CoreProgressManager.java:446)
at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:392)
at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:54)
at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:127)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runWithWriteActionPriority(ProgressIndicatorUtils.java:137)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runInReadActionWithWriteActionPriority(ProgressIndicatorUtils.java:77)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runInReadActionWithWriteActionPriority(ProgressIndicatorUtils.java:102)
at com.intellij.ui.DeferredIconImpl$2.run(DeferredIconImpl.java:152)
at com.intellij.util.concurrency.BoundedTaskExecutor$2.run(BoundedTaskExecutor.java:187)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Sorry for the late reply, I've fixed the problem with deployment configuraion (https://youtrack.jetbrains.com/issue/IDEA-156073).
The problem was in com.intellij.remoteServer.impl.configuration.deployment.DeployToServerConfigurationType.DeployToServerConfigurationFactory#onNewConfigurationCreated method, which produced incomplete DeployToServerRunConfiguration (there myDeploymentConfiguration isn't initialized).