diff --git a/.vscode/launch.json b/.vscode/launch.json index db170740d..fa2cfb0b9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -32,6 +32,13 @@ "outFiles": [ "${workspaceFolder}/editors/vscode/out/test/**/*.js" ] + }, + { + "type": "java", + "name": "Debug Tests", + "request": "attach", + "hostName": "localhost", + "port": 5005 } ] } diff --git a/build.gradle b/build.gradle index e69de29bb..70c252005 100644 --- a/build.gradle +++ b/build.gradle @@ -0,0 +1,7 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' version "$kotlinVersion" +} + +repositories { + mavenCentral() +} diff --git a/gradle.properties b/gradle.properties index 96e6215c0..678d4ebb0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ teamCityUrl=https://teamcity.jetbrains.com teamCityUsername=guest teamCityPassword=guest -kotlinVersion=1.3.11 -kotlinBuildType=Kotlin_130_CompilerAllPlugins -kotlinBuild=1.3.11-release-272 -kotlinPluginBuild=1.3.11-release-IJ2018.3-1 +kotlinVersion=1.3.40 +kotlinBuildType=Kotlin_1340_CompilerAllPlugins +kotlinBuild=1.3.41-release-150 +kotlinPluginBuild=1.3.41-release-IJ2019.2-1 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f6b961fd5..29953ea14 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bf3de2183..472ef88fb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-rc-1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew.bat b/gradlew.bat index f9553162f..e95643d6a 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,84 +1,84 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/server/build.gradle b/server/build.gradle index d646a3a81..9072f798d 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -2,7 +2,7 @@ plugins { id 'org.jetbrains.kotlin.jvm' id 'maven' id 'application' - id 'com.github.jk1.tcdeps' version '0.17' + id 'com.github.jk1.tcdeps' version '0.19' id 'com.jaredsburrows.license' version '0.8.42' } @@ -40,18 +40,19 @@ configurations { } dependencies { - def kotlinTeamCity = "$kotlinBuildType:$kotlinBuild:kotlin-plugin-${kotlinPluginBuild}.zip!/Kotlin" + def kotlinPluginTC = "$kotlinBuildType:$kotlinBuild:kotlin-plugin-${kotlinPluginBuild}.zip!/Kotlin" implementation project(':shared') implementation 'com.google.guava:guava:21.0' implementation 'org.eclipse.lsp4j:org.eclipse.lsp4j:0.5.0' implementation 'org.jetbrains.kotlin:kotlin-compiler' + implementation 'org.jetbrains.kotlin:kotlin-scripting-compiler' implementation 'org.jetbrains.kotlin:kotlin-reflect' implementation 'org.jetbrains:fernflower:1.0' implementation fileTree(dir: "$projectDir/lib-kotlin", include: ["*.jar"]) - kotlinJVMLib tc("$kotlinTeamCity/lib/kotlin-plugin.jar") - kotlinJVMLib tc("$kotlinTeamCity/lib/j2k.jar") + // TODO: Isolate J2K dependency from the plugin: + kotlinJVMLib tc("$kotlinPluginTC/lib/kotlin-plugin.jar") testImplementation 'org.hamcrest:hamcrest-all:1.3' testImplementation 'junit:junit:4.11' @@ -118,6 +119,7 @@ run { } test { + maxHeapSize = "1G" testLogging { events "failed" exceptionFormat "short" diff --git a/server/src/main/kotlin/org/javacs/kt/Compiler.kt b/server/src/main/kotlin/org/javacs/kt/Compiler.kt index fd90e8bf6..98aeccd20 100644 --- a/server/src/main/kotlin/org/javacs/kt/Compiler.kt +++ b/server/src/main/kotlin/org/javacs/kt/Compiler.kt @@ -7,7 +7,6 @@ import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.VirtualFileSystem import com.intellij.psi.PsiFileFactory import com.intellij.mock.MockProject -import org.jetbrains.kotlin.cli.common.script.CliScriptDefinitionProvider import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.jvm.compiler.CliBindingTrace import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles @@ -20,6 +19,7 @@ import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.container.ComponentProvider import org.jetbrains.kotlin.container.get +import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.psi.* @@ -27,11 +27,18 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTraceContext import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer import org.jetbrains.kotlin.resolve.TopDownAnalysisMode.TopLevelDeclarations +import org.jetbrains.kotlin.resolve.calls.components.InferenceSession import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory import org.jetbrains.kotlin.resolve.scopes.LexicalScope -import org.jetbrains.kotlin.script.KotlinScriptDefinition -import org.jetbrains.kotlin.script.ScriptDefinitionProvider +import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys +import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider +import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDependenciesProvider +import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar +import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition +import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider +import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider +import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices import org.jetbrains.kotlin.util.KotlinFrontEndException @@ -39,6 +46,7 @@ import java.nio.file.Path import java.nio.file.Paths import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock +import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import org.javacs.kt.util.KotlinLSException import org.javacs.kt.util.KotlinNullableNotNullManager import org.javacs.kt.util.LoggingMessageCollector @@ -51,7 +59,7 @@ class Compiler(classPath: Set) { val environment: KotlinCoreEnvironment private var parser: KtPsiFactory - private var scripts: CliScriptDefinitionProvider + private var scripts: ScriptDefinitionProvider private val localFileSystem: VirtualFileSystem companion object { @@ -67,6 +75,8 @@ class Compiler(classPath: Set) { configuration = KotlinCompilerConfiguration().apply { put(CommonConfigurationKeys.MODULE_NAME, JvmAbi.DEFAULT_MODULE_NAME) put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, LoggingMessageCollector) + add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar()) + add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition) addJvmClasspathRoots(classPath.map { it.toFile() }) }, configFiles = EnvironmentConfigFiles.JVM_CONFIG_FILES @@ -78,8 +88,7 @@ class Compiler(classPath: Set) { } parser = KtPsiFactory(environment.project) - scripts = ScriptDefinitionProvider.getInstance(environment.project) as CliScriptDefinitionProvider - scripts.setScriptDefinitions(listOf(KotlinScriptDefinition(Any::class))) + scripts = ScriptDefinitionProvider.getInstance(environment.project)!! localFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) } @@ -97,7 +106,6 @@ class Compiler(classPath: Set) { "default" -> JvmTarget.DEFAULT "1.6" -> JvmTarget.JVM_1_6 "1.8" -> JvmTarget.JVM_1_8 - // TODO: Add once Java 9+ is supported // "9" -> JvmTarget.JVM_9 // "10" -> JvmTarget.JVM_10 // "11" -> JvmTarget.JVM_11 @@ -181,6 +189,7 @@ class Compiler(classPath: Set) { expression, TypeUtils.NO_EXPECTED_TYPE, DataFlowInfo.EMPTY, + InferenceSession.default, trace, true) return Pair(trace.bindingContext, container) diff --git a/server/src/main/kotlin/org/javacs/kt/KotlinTextDocumentService.kt b/server/src/main/kotlin/org/javacs/kt/KotlinTextDocumentService.kt index 702811453..ee1fe4f59 100644 --- a/server/src/main/kotlin/org/javacs/kt/KotlinTextDocumentService.kt +++ b/server/src/main/kotlin/org/javacs/kt/KotlinTextDocumentService.kt @@ -268,6 +268,11 @@ class KotlinTextDocumentService( private fun clearDiagnostics(file: Path) { client.publishDiagnostics(PublishDiagnosticsParams(file.toUri().toString(), listOf())) } + + fun shutdownExecutors(awaitTermination: Boolean) { + async.shutdown(awaitTermination) + debounceLint.shutdown(awaitTermination) + } } private inline fun reportTime(block: () -> T): T { diff --git a/server/src/main/kotlin/org/javacs/kt/hover/Hovers.kt b/server/src/main/kotlin/org/javacs/kt/hover/Hovers.kt index f283fd95b..f952a4382 100644 --- a/server/src/main/kotlin/org/javacs/kt/hover/Hovers.kt +++ b/server/src/main/kotlin/org/javacs/kt/hover/Hovers.kt @@ -11,12 +11,6 @@ import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor -import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor -import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade -import org.jetbrains.kotlin.idea.project.languageVersionSettings -import org.jetbrains.kotlin.idea.references.mainReference -import org.jetbrains.kotlin.idea.resolve.frontendService import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf @@ -27,7 +21,6 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.callUtil.getType import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory -import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.javacs.kt.CompiledFile import org.javacs.kt.completion.DECL_RENDERER import org.javacs.kt.position.position diff --git a/server/src/test/kotlin/org/javacs/kt/CompilerTest.kt b/server/src/test/kotlin/org/javacs/kt/CompilerTest.kt index b9f4861c2..3e1c97a3c 100644 --- a/server/src/test/kotlin/org/javacs/kt/CompilerTest.kt +++ b/server/src/test/kotlin/org/javacs/kt/CompilerTest.kt @@ -69,4 +69,4 @@ private class FileToEdit { assertThat(target.name, hasToString("intFunction")) } -} \ No newline at end of file +} diff --git a/server/src/test/kotlin/org/javacs/kt/LanguageServerTestFixture.kt b/server/src/test/kotlin/org/javacs/kt/LanguageServerTestFixture.kt index 556fe2077..c1b484e71 100644 --- a/server/src/test/kotlin/org/javacs/kt/LanguageServerTestFixture.kt +++ b/server/src/test/kotlin/org/javacs/kt/LanguageServerTestFixture.kt @@ -3,6 +3,7 @@ package org.javacs.kt import org.eclipse.lsp4j.* import org.eclipse.lsp4j.services.LanguageClient import org.junit.Before +import org.junit.After import java.nio.file.Path import java.nio.file.Paths import java.util.concurrent.CompletableFuture @@ -37,6 +38,17 @@ abstract class LanguageServerTestFixture(relativeWorkspaceRoot: String) : Langua return languageServer } + + @After fun shutdownExecutors() { + languageServer.textDocumentService.shutdownExecutors(awaitTermination = true) + } + + @After fun printMemoryUsage() { + val rt = Runtime.getRuntime() + val total = rt.totalMemory().toDouble() / 1000000.0 + val free = rt.freeMemory().toDouble() / 1000000.0 + println("Memory after test: ${total - free} MB used / $total MB total") + } fun completionParams(relativePath: String, line: Int, column: Int): CompletionParams { val file = workspaceRoot.resolve(relativePath) @@ -104,12 +116,12 @@ abstract class LanguageServerTestFixture(relativeWorkspaceRoot: String) : Langua println(`object`.toString()) } - override fun logMessage(message: MessageParams?) { - println(message.toString()) - } + override fun logMessage(message: MessageParams?) = printMessage(message) - override fun showMessage(message: MessageParams?) { - println(message.toString()) + override fun showMessage(message: MessageParams?) = printMessage(message) + + private fun printMessage(message: MessageParams?) { + println("[${message?.type}] ${message?.message}") } } diff --git a/server/src/test/kotlin/org/javacs/kt/SimpleScriptTest.kt b/server/src/test/kotlin/org/javacs/kt/SimpleScriptTest.kt index 7a54cee96..175f24d12 100644 --- a/server/src/test/kotlin/org/javacs/kt/SimpleScriptTest.kt +++ b/server/src/test/kotlin/org/javacs/kt/SimpleScriptTest.kt @@ -1,10 +1,12 @@ package org.javacs.kt import org.jetbrains.kotlin.cli.common.repl.* -import org.jetbrains.kotlin.cli.jvm.repl.* import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration as KotlinCompilerConfiguration -import org.jetbrains.kotlin.script.* +import org.jetbrains.kotlin.scripting.definitions.* +import org.jetbrains.kotlin.scripting.repl.* +// TODO: In the future this import will be: +// import org.jetbrains.kotlin.scripting.compiler.plugin.repl.* import com.intellij.openapi.util.* import org.jetbrains.kotlin.cli.common.messages.* import org.junit.* diff --git a/server/src/test/resources/script/FunctionScript.kts b/server/src/test/resources/script/FunctionScript.kts index 41eaf7aa0..76adf3687 100644 --- a/server/src/test/resources/script/FunctionScript.kts +++ b/server/src/test/resources/script/FunctionScript.kts @@ -1,4 +1,4 @@ fun foo() { val first = 1 val second = 2 -} \ No newline at end of file +} diff --git a/settings.gradle b/settings.gradle index 1a829f23e..69101a5c2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,15 +1,3 @@ -// Workaround, see https://github.com/gradle/gradle/issues/1697#issuecomment-394359260 -// TODO: Variable plugin versions will be supported in Gradle 5.6, see https://github.com/gradle/gradle/issues/1697#issuecomment-506910915 -pluginManagement { - resolutionStrategy { - eachPlugin { - if (requested.id.namespace != null && requested.id.namespace.startsWith("org.jetbrains.kotlin")) { - useVersion kotlinVersion - } - } - } -} - rootProject.name = 'KotlinLanguageServer' include( diff --git a/shared/src/main/kotlin/org/javacs/kt/util/AsyncExecutor.kt b/shared/src/main/kotlin/org/javacs/kt/util/AsyncExecutor.kt index d183295d9..fee734bd9 100644 --- a/shared/src/main/kotlin/org/javacs/kt/util/AsyncExecutor.kt +++ b/shared/src/main/kotlin/org/javacs/kt/util/AsyncExecutor.kt @@ -1,9 +1,11 @@ package org.javacs.kt.util +import org.javacs.kt.LOG import java.time.Duration import java.util.function.Supplier import java.util.concurrent.CompletableFuture import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit private var threadCount = 0 @@ -21,4 +23,12 @@ class AsyncExecutor { defaultValue } }, workerThread) + + fun shutdown(awaitTermination: Boolean) { + workerThread.shutdown() + if (awaitTermination) { + LOG.info("Awaiting async termination...") + workerThread.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS) + } + } } diff --git a/shared/src/main/kotlin/org/javacs/kt/util/Debouncer.kt b/shared/src/main/kotlin/org/javacs/kt/util/Debouncer.kt index b6e8d874b..127f6c54d 100644 --- a/shared/src/main/kotlin/org/javacs/kt/util/Debouncer.kt +++ b/shared/src/main/kotlin/org/javacs/kt/util/Debouncer.kt @@ -1,5 +1,6 @@ package org.javacs.kt.util +import org.javacs.kt.LOG import java.time.Duration import java.util.function.Supplier import java.util.concurrent.ScheduledExecutorService @@ -32,4 +33,12 @@ class Debouncer( fun waitForPendingTask() { pendingTask?.get() } + + fun shutdown(awaitTermination: Boolean) { + executor.shutdown() + if (awaitTermination) { + LOG.info("Awaiting debouncer termination...") + executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS) + } + } }