IJPL-149878 IJent WSL FS refactoring: encapsulate methods in IjentWslNioFsVmOptionsSetter

GitOrigin-RevId: 0f58e40340abb3add4753b5a3b407ca4544560b6
This commit is contained in:
Vladimir Lagunov
2024-06-27 14:10:12 +02:00
committed by intellij-monorepo-bot
parent 026a563625
commit 3d9dc83f8e
2 changed files with 139 additions and 140 deletions

View File

@@ -26,165 +26,164 @@ import java.io.IOException
import kotlin.concurrent.thread
@VisibleForTesting
class IjentWslNioFsVmOptionsSetter : ApplicationActivationListener {
companion object {
@VisibleForTesting
fun ensureInVmOptionsImpl(
isEnabled: Boolean,
forceProductionOptions: Boolean,
isEnabledByDefault: Boolean = isIjentWslFsEnabledByDefaultForProduct(ApplicationNamesInfo.getInstance().scriptName),
getOptionByPrefix: (String) -> String?,
): Collection<Pair<String, String?>> {
val changedOptions = mutableListOf<Pair<String, String?>>()
object IjentWslNioFsVmOptionsSetter {
fun ensureInVmOptionsImpl(
isEnabled: Boolean,
forceProductionOptions: Boolean,
isEnabledByDefault: Boolean = isIjentWslFsEnabledByDefaultForProduct(ApplicationNamesInfo.getInstance().scriptName),
getOptionByPrefix: (String) -> String?,
): Collection<Pair<String, String?>> {
val changedOptions = mutableListOf<Pair<String, String?>>()
run {
val prefix = "-D$IJENT_WSL_FILE_SYSTEM_REGISTRY_KEY="
val actualValue = getOptionByPrefix(prefix)?.toBooleanStrictOrNull() ?: false
if (actualValue != isEnabled || isEnabledByDefault && !isEnabled) {
changedOptions += prefix to isEnabled.toString()
}
run {
val prefix = "-D$IJENT_WSL_FILE_SYSTEM_REGISTRY_KEY="
val actualValue = getOptionByPrefix(prefix)?.toBooleanStrictOrNull() ?: false
if (actualValue != isEnabled || isEnabledByDefault && !isEnabled) {
changedOptions += prefix to isEnabled.toString()
}
var forceDefaultFs = false
run {
val prefix = "-Djava.nio.file.spi.DefaultFileSystemProvider="
val actualValue = getOptionByPrefix(prefix)
if (isEnabled) {
if (actualValue != MultiRoutingFileSystemProvider::class.java.name) {
changedOptions += prefix to MultiRoutingFileSystemProvider::class.java.name
}
}
else if (actualValue == MultiRoutingFileSystemProvider::class.java.name) {
// sun.nio.fs.WindowsFileSystemProvider doesn't have the constructor required for SPI.
// Also, it's not always possible to remove a VM option.
// Therefore, this special flag orders MultiRoutingFileSystemProvider to delegate everything to the default FS.
forceDefaultFs = true
}
}
//see idea/nativeHelpers/buildTypes/ijent/performance/IJentWslBenchmarkTests.kt:38
if (!forceProductionOptions && isEnabled && ApplicationManager.getApplication().isUnitTestMode) {
val prefix = "-Xbootclasspath/a:out/tests/classes/production/$IJENT_BOOT_CLASSPATH_MODULE"
val actualValue = getOptionByPrefix(prefix)
if (actualValue != "") {
changedOptions += prefix to ""
}
}
run {
val prefix = "-Djava.security.manager="
val actualValue = getOptionByPrefix(prefix)
if (isEnabled) {
if (actualValue != CoreBootstrapSecurityManager::class.java.name) {
changedOptions += prefix to CoreBootstrapSecurityManager::class.java.name
}
}
else {
// It's not always possible to remove a VM Option (if an option is defined in a product-level vmoptions file).
// However, CoreBootstrapSecurityManager does nothing potentially harmful.
// The option is kept as is.
}
}
run {
val prefix = "-Didea.force.default.filesystem="
val actualValue = getOptionByPrefix(prefix)
if (isEnabled) {
if (actualValue != null && actualValue != "false") {
// It's not always possible to remove a VM Option (if an option is defined in a product-leve vmoptions file).
// Therefore, this code sets an opposite option.
changedOptions += prefix to "false"
}
}
else if (forceDefaultFs && actualValue != "true") {
changedOptions += prefix to "true"
}
}
return changedOptions
}
fun ensureInVmOptions(): Collection<Pair<String, String?>> {
val isEnabled = WslIjentAvailabilityService.getInstance().useIjentForWslNioFileSystem()
var forceDefaultFs = false
run {
val prefix = "-Djava.nio.file.spi.DefaultFileSystemProvider="
val actualValue = getOptionByPrefix(prefix)
// In Dev Server, it's possible to customize VM options only through the Run Configuration.
// Invoking the action "Customize VM Options" won't have any effect because the Dev Server resets the file on restart.
val getEffectiveVmOptions = AppMode.isDevServer() || ApplicationManager.getApplication().isUnitTestMode
val changedOptions = ensureInVmOptionsImpl(isEnabled, false) { prefix ->
VMOptions.readOption(prefix, getEffectiveVmOptions)
}
for ((prefix, value) in changedOptions) {
try {
VMOptions.setOption(prefix, value)
}
catch (err: IOException) {
if (!ApplicationManager.getApplication().isUnitTestMode) {
throw err
}
if (isEnabled) {
if (actualValue != MultiRoutingFileSystemProvider::class.java.name) {
changedOptions += prefix to MultiRoutingFileSystemProvider::class.java.name
}
}
return changedOptions
else if (actualValue == MultiRoutingFileSystemProvider::class.java.name) {
// sun.nio.fs.WindowsFileSystemProvider doesn't have the constructor required for SPI.
// Also, it's not always possible to remove a VM option.
// Therefore, this special flag orders MultiRoutingFileSystemProvider to delegate everything to the default FS.
forceDefaultFs = true
}
}
//see idea/nativeHelpers/buildTypes/ijent/performance/IJentWslBenchmarkTests.kt:38
if (!forceProductionOptions && isEnabled && ApplicationManager.getApplication().isUnitTestMode) {
val prefix = "-Xbootclasspath/a:out/tests/classes/production/$IJENT_BOOT_CLASSPATH_MODULE"
val actualValue = getOptionByPrefix(prefix)
if (actualValue != "") {
changedOptions += prefix to ""
}
}
run {
val prefix = "-Djava.security.manager="
val actualValue = getOptionByPrefix(prefix)
if (isEnabled) {
if (actualValue != CoreBootstrapSecurityManager::class.java.name) {
changedOptions += prefix to CoreBootstrapSecurityManager::class.java.name
}
}
else {
// It's not always possible to remove a VM Option (if an option is defined in a product-level vmoptions file).
// However, CoreBootstrapSecurityManager does nothing potentially harmful.
// The option is kept as is.
}
}
run {
val prefix = "-Didea.force.default.filesystem="
val actualValue = getOptionByPrefix(prefix)
if (isEnabled) {
if (actualValue != null && actualValue != "false") {
// It's not always possible to remove a VM Option (if an option is defined in a product-leve vmoptions file).
// Therefore, this code sets an opposite option.
changedOptions += prefix to "false"
}
}
else if (forceDefaultFs && actualValue != "true") {
changedOptions += prefix to "true"
}
}
return changedOptions
}
@Service
private class ServiceScope(coroutineScope: CoroutineScope) : CoroutineScope by coroutineScope
fun ensureInVmOptions(): Collection<Pair<String, String?>> {
val isEnabled = WslIjentAvailabilityService.getInstance().useIjentForWslNioFileSystem()
override fun applicationActivated(ideFrame: IdeFrame) {
service<ServiceScope>().launch {
val changedOptions = ensureInVmOptions()
when {
changedOptions.isEmpty() -> {
IjentWslNioFsToggler.instanceAsync() // Implicitly activates IJent FS for all WSL distributions.
// In Dev Server, it's possible to customize VM options only through the Run Configuration.
// Invoking the action "Customize VM Options" won't have any effect because the Dev Server resets the file on restart.
val getEffectiveVmOptions = AppMode.isDevServer() || ApplicationManager.getApplication().isUnitTestMode
val changedOptions = ensureInVmOptionsImpl(isEnabled, false) { prefix ->
VMOptions.readOption(prefix, getEffectiveVmOptions)
}
for ((prefix, value) in changedOptions) {
try {
VMOptions.setOption(prefix, value)
}
catch (err: IOException) {
if (!ApplicationManager.getApplication().isUnitTestMode) {
throw err
}
}
}
PluginManagerCore.isRunningFromSources() || AppMode.isDevServer() ->
launch(Dispatchers.EDT + ModalityState.nonModal().asContextElement()) {
@Suppress("HardCodedStringLiteral") // Not sure if Dev Mode requires i18n
return changedOptions
}
internal class ApplicationListener : ApplicationActivationListener {
@Service
private class ServiceScope(coroutineScope: CoroutineScope) : CoroutineScope by coroutineScope
override fun applicationActivated(ideFrame: IdeFrame) {
service<ServiceScope>().launch {
val changedOptions = ensureInVmOptions()
when {
changedOptions.isEmpty() -> {
IjentWslNioFsToggler.instanceAsync() // Implicitly activates IJent FS for all WSL distributions.
}
PluginManagerCore.isRunningFromSources() || AppMode.isDevServer() ->
launch(Dispatchers.EDT + ModalityState.nonModal().asContextElement()) {
@Suppress("HardCodedStringLiteral") // Not sure if Dev Mode requires i18n
val doThat = Messages.OK == Messages.showOkCancelDialog(
null,
changedOptions.joinToString(
prefix = "This message is seen only in Dev Mode/Run from sources.<br/><br/>" +
"The value of the registry flag for IJent FS doesn't match system properties.<br/>" +
"Add the following VM options to the Dev Mode Run Configuration:<br/><pre>",
separator = "<br/>",
postfix = "</pre>",
) { (k, v) ->
"$k${v.orEmpty()}"
},
"IJent VM Options",
if (ApplicationManager.getApplication().isRestartCapable) "Restart" else "Shutdown",
"Cancel",
AllIcons.General.Warning,
)
if (doThat) {
thread(isDaemon = true) {
// If it wasn't called in a separate thread, it would block the coroutine scope,
// preventing the service to be disposed and preventing the IDE from exiting.
ApplicationManagerEx.getApplicationEx().restart(true)
}
}
}
else -> launch(Dispatchers.EDT + ModalityState.nonModal().asContextElement()) {
val doThat = Messages.OK == Messages.showOkCancelDialog(
null,
changedOptions.joinToString(
prefix = "This message is seen only in Dev Mode/Run from sources.<br/><br/>" +
"The value of the registry flag for IJent FS doesn't match system properties.<br/>" +
"Add the following VM options to the Dev Mode Run Configuration:<br/><pre>",
separator = "<br/>",
postfix = "</pre>",
) { (k, v) ->
"$k${v.orEmpty()}"
},
"IJent VM Options",
if (ApplicationManager.getApplication().isRestartCapable) "Restart" else "Shutdown",
"Cancel",
IdeBundle.message("ijent.wsl.fs.dialog.message"),
IdeBundle.message("ijent.wsl.fs.dialog.title"),
IdeBundle.message(if (ApplicationManager.getApplication().isRestartCapable) "ide.restart.action" else "ide.shutdown.action"),
IdeBundle.message("dialog.action.restart.cancel"),
AllIcons.General.Warning,
)
if (doThat) {
thread(isDaemon = true) {
// If it wasn't called in a separate thread, it would block the coroutine scope,
// preventing the service to be disposed and preventing the IDE from exiting.
ApplicationManagerEx.getApplicationEx().restart(true)
}
ApplicationManagerEx.getApplicationEx().restart(true)
}
}
else -> launch(Dispatchers.EDT + ModalityState.nonModal().asContextElement()) {
val doThat = Messages.OK == Messages.showOkCancelDialog(
null,
IdeBundle.message("ijent.wsl.fs.dialog.message"),
IdeBundle.message("ijent.wsl.fs.dialog.title"),
IdeBundle.message(if (ApplicationManager.getApplication().isRestartCapable) "ide.restart.action" else "ide.shutdown.action"),
IdeBundle.message("dialog.action.restart.cancel"),
AllIcons.General.Warning,
)
if (doThat) {
ApplicationManagerEx.getApplicationEx().restart(true)
}
}
}
}

View File

@@ -1891,7 +1891,7 @@
<listener class="com.intellij.diff.tools.combined.CombinedDiffAdvancedSettingsChangeListener"
topic="com.intellij.openapi.options.advanced.AdvancedSettingsChangeListener"/>
<listener class="com.intellij.execution.wsl.ijent.nio.toggle.IjentWslNioFsVmOptionsSetter"
<listener class="com.intellij.execution.wsl.ijent.nio.toggle.IjentWslNioFsVmOptionsSetter$ApplicationListener"
topic="com.intellij.openapi.application.ApplicationActivationListener"
os="windows"/>
<listener class="com.intellij.execution.wsl.ijent.nio.toggle.IjentWslNioFsRegistryListener"