diff --git a/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/CompilerCallbackServicesFacadeServer.kt b/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/CompilerCallbackServicesFacadeServer.kt index 964d1f306e3..fe7470b0875 100644 --- a/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/CompilerCallbackServicesFacadeServer.kt +++ b/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/CompilerCallbackServicesFacadeServer.kt @@ -25,23 +25,15 @@ import org.jetbrains.kotlin.progress.CompilationCanceledStatus import org.jetbrains.kotlin.rmi.CompilerCallbackServicesFacade import org.jetbrains.kotlin.rmi.LoopbackNetworkInterface import org.jetbrains.kotlin.rmi.SOCKET_ANY_FREE_PORT -import java.rmi.server.UnicastRemoteObject public class CompilerCallbackServicesFacadeServer( val incrementalCompilationComponents: IncrementalCompilationComponents? = null, val compilationCancelledStatus: CompilationCanceledStatus? = null, port: Int = SOCKET_ANY_FREE_PORT -) : CompilerCallbackServicesFacade { - - init { - UnicastRemoteObject.exportObject(this, port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory) - } - - public fun disconnect() { - UnicastRemoteObject.unexportObject(this, true) - } - +) : CompilerCallbackServicesFacade, + java.rmi.server.UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory) +{ override fun hasIncrementalCaches(): Boolean = incrementalCompilationComponents != null override fun hasLookupTracker(): Boolean = incrementalCompilationComponents != null diff --git a/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/KotlinCompilerClient.kt b/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/KotlinCompilerClient.kt index 461555f221f..2bf4b1724ae 100644 --- a/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/KotlinCompilerClient.kt +++ b/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/KotlinCompilerClient.kt @@ -114,14 +114,7 @@ public object KotlinCompilerClient { public fun compile(compilerService: CompileService, targetPlatform: CompileService.TargetPlatform, args: Array, out: OutputStream): Int { val outStrm = RemoteOutputStreamServer(out) - val servicesFacade = CompilerCallbackServicesFacadeServer() - try { - return compilerService.remoteCompile(targetPlatform, args, servicesFacade, outStrm, CompileService.OutputFormat.PLAIN, outStrm) - } - finally { - servicesFacade.disconnect() - outStrm.disconnect() - } + return compilerService.remoteCompile(targetPlatform, args, CompilerCallbackServicesFacadeServer(), outStrm, CompileService.OutputFormat.PLAIN, outStrm) } @@ -132,22 +125,15 @@ public object KotlinCompilerClient { compilerOut: OutputStream, daemonOut: OutputStream, profiler: Profiler = DummyProfiler() - ): Int { - - val compilerOutStreamServer = RemoteOutputStreamServer(compilerOut) - val daemonOutStreamServer = RemoteOutputStreamServer(daemonOut) - val servicesFacade = CompilerCallbackServicesFacadeServer(incrementalCompilationComponents = callbackServices.incrementalCompilationComponents, - compilationCancelledStatus = callbackServices.compilationCanceledStatus) - try { - return profiler.withMeasure(this) { - compileService.remoteIncrementalCompile(targetPlatform, args, servicesFacade, compilerOutStreamServer, CompileService.OutputFormat.XML, daemonOutStreamServer) - } - } - finally { - servicesFacade.disconnect() - compilerOutStreamServer.disconnect() - daemonOutStreamServer.disconnect() - } + ): Int = profiler.withMeasure(this) { + compileService.remoteIncrementalCompile( + targetPlatform, + args, + CompilerCallbackServicesFacadeServer(incrementalCompilationComponents = callbackServices.incrementalCompilationComponents, + compilationCancelledStatus = callbackServices.compilationCanceledStatus), + RemoteOutputStreamServer(compilerOut), + CompileService.OutputFormat.XML, + RemoteOutputStreamServer(daemonOut)) } public val COMPILE_DAEMON_CLIENT_OPTIONS_PROPERTY: String = "kotlin.daemon.client.options" @@ -230,8 +216,9 @@ public object KotlinCompilerClient { println("Used memory $memAfter (${"%+d".format(memAfter - memBefore)} kb)") } finally { - servicesFacade.disconnect() - outStrm.disconnect() + // forcing RMI to unregister all objects and stop + java.rmi.server.UnicastRemoteObject.unexportObject(servicesFacade, true) + java.rmi.server.UnicastRemoteObject.unexportObject(outStrm, true) } } } @@ -284,7 +271,7 @@ public object KotlinCompilerClient { } .filterNotNull() .toList() - return when (daemons.size()) { + return when (daemons.size) { 0 -> null 1 -> daemons.first() else -> throw IllegalStateException("Multiple daemons serving the same compiler, reset with the cleanup required") @@ -311,13 +298,8 @@ public object KotlinCompilerClient { private fun startDaemon(compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, daemonOptions: DaemonOptions, reportingTargets: DaemonReportingTargets) { - val javaExecutable = File(System.getProperty("java.home"), "bin").let { - val javaw = File(it, "javaw.exe") - // TODO: doesn't seem reliable enough, consider more checks if OS is of windows flavor, etc. - if (javaw.exists() && javaw.isFile && javaw.canExecute()) javaw else File(it, "java") - } - // TODO add os detection to specify option more precisely - val platformSpecificOptions = listOf("-Djava.awt.headless=true") // hide daemon in OS X + val javaExecutable = File(File(System.getProperty("java.home"), "bin"), "java") + val platformSpecificOptions = listOf("-Djava.awt.headless=true") // hide daemon window val args = listOf( javaExecutable.absolutePath, "-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator)) + platformSpecificOptions + diff --git a/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/RemoteOutputStreamServer.kt b/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/RemoteOutputStreamServer.kt index 8b00b25c215..1793d66b080 100644 --- a/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/RemoteOutputStreamServer.kt +++ b/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/RemoteOutputStreamServer.kt @@ -20,19 +20,12 @@ import org.jetbrains.kotlin.rmi.LoopbackNetworkInterface import org.jetbrains.kotlin.rmi.RemoteOutputStream import org.jetbrains.kotlin.rmi.SOCKET_ANY_FREE_PORT import java.io.OutputStream -import java.rmi.server.UnicastRemoteObject -class RemoteOutputStreamServer(val out: OutputStream, port: Int = SOCKET_ANY_FREE_PORT) : RemoteOutputStream { - - init { - UnicastRemoteObject.exportObject(this, port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory) - } - - public fun disconnect() { - UnicastRemoteObject.unexportObject(this, true) - } - +class RemoteOutputStreamServer(val out: OutputStream, port: Int = SOCKET_ANY_FREE_PORT) +: RemoteOutputStream, + java.rmi.server.UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory) +{ override fun close() { out.close() } diff --git a/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/NetworkUtils.kt b/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/NetworkUtils.kt index 633e5c5f449..440af4c8206 100644 --- a/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/NetworkUtils.kt +++ b/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/NetworkUtils.kt @@ -50,15 +50,19 @@ public object LoopbackNetworkInterface { } } + // base socket factories by default don't implement equals properly (see e.g. http://stackoverflow.com/questions/21555710/rmi-and-jmx-socket-factories) + // so implementing it in derived classes using the fact that they are singletons - data class ServerLoopbackSocketFactory : RMIServerSocketFactory, Serializable { + class ServerLoopbackSocketFactory : RMIServerSocketFactory, Serializable { + override fun equals(other: Any?): Boolean = other === this || super.equals(other) @Throws(IOException::class) override fun createServerSocket(port: Int): ServerSocket = ServerSocket(port, SERVER_SOCKET_BACKLOG_SIZE, InetAddress.getByName(loopbackInetAddressName)) } - data class ClientLoopbackSocketFactory : RMIClientSocketFactory, Serializable { + class ClientLoopbackSocketFactory : RMIClientSocketFactory, Serializable { + override fun equals(other: Any?): Boolean = other === this || super.equals(other) @Throws(IOException::class) override fun createSocket(host: String, port: Int): Socket = Socket(InetAddress.getByName(loopbackInetAddressName), port) diff --git a/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/CompileServiceImpl.kt b/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/CompileServiceImpl.kt index b1a323ffbac..9090dd20de8 100644 --- a/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/CompileServiceImpl.kt +++ b/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/CompileServiceImpl.kt @@ -209,11 +209,4 @@ class CompileServiceImpl( if (!alive) throw IllegalStateException("Kotlin Compiler Service is not in alive state") body() } - - // sometimes used for debugging - fun spy(msg: String, body: () -> R): R { - val res = body() - log.info(msg + " = " + res.toString()) - return res - } } diff --git a/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/DaemonPermissions.kt b/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/DaemonPermissions.kt deleted file mode 100644 index 909e1a6dd1f..00000000000 --- a/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/DaemonPermissions.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.rmi.service - -import java.io.FilePermission -import java.net.SocketPermission -import java.security.CodeSource -import java.security.Permission -import java.security.PermissionCollection -import java.security.Policy -import java.util.ArrayList -import java.util.Collections -import java.util.Enumeration -import java.util.PropertyPermission - - -public class DaemonPolicy(val port: Int) : Policy() { - - private fun createPermissions(): PermissionCollection { - val perms = DaemonPermissionCollection() - - val socketPermission = SocketPermission("localhost:$port-", "connect, accept, resolve") - val propertyPermission = PropertyPermission("localhost:$port", "read") - //val filePermission = FilePermission("<>", "read") - - perms.add(socketPermission) - perms.add(propertyPermission) - //perms.add(filePermission) - - return perms - } - - private val perms: PermissionCollection by lazy { createPermissions() } - - override fun getPermissions(codesource: CodeSource?): PermissionCollection { - return perms - } -} - - -class DaemonPermissionCollection : PermissionCollection() { - val perms = ArrayList() - - override fun add(p: Permission) { - perms.add(p) - } - - override fun implies(p: Permission): Boolean = perms.any { implies(p) } - - override fun elements(): Enumeration = Collections.enumeration(perms) - - override fun isReadOnly(): Boolean = false -// -// companion object { -// -// private val serialVersionUID = 614300921365729272L -// } - -} - - -public fun setDaemonPpermissions(port: Int) { - Policy.setPolicy(DaemonPolicy(port)) -} - diff --git a/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt b/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt index 8d4583f6d60..05b05a59eb8 100644 --- a/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt @@ -200,6 +200,7 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { val clientAliveFile = createTempFile("kotlin-daemon-transitive-run-test", ".run") val runFilesPath = File(tmpdir, getTestName(true)).absolutePath val daemonOptions = DaemonOptions(runFilesPath = runFilesPath, clientAliveFlagPath = clientAliveFile.absolutePath) + val jar = tmpdir.absolutePath + File.separator + "hello.jar" val args = listOf( File(File(System.getProperty("java.home"), "bin"), "java").absolutePath, "-D$COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY", @@ -208,7 +209,8 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { KotlinCompilerClient::class.qualifiedName!!) + daemonOptions.mappers.flatMap { it.toArgs(COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) } + compilerId.mappers.flatMap { it.toArgs(COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) } + - File(getHelloAppBaseDir(), "hello.kt").absolutePath + File(getHelloAppBaseDir(), "hello.kt").absolutePath + + "-d" + jar try { KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions) var resOutput: String? = null diff --git a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/KotlinCompilerRunner.kt b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/KotlinCompilerRunner.kt index 8c6da3b0209..50e752af3b6 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/KotlinCompilerRunner.kt +++ b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/KotlinCompilerRunner.kt @@ -128,9 +128,9 @@ public object KotlinCompilerRunner { internal class DaemonConnection(public val daemon: CompileService?) internal object getDaemonConnection { - private var connection: DaemonConnection? = null + private @Volatile var connection: DaemonConnection? = null - operator fun invoke(environment: CompilerEnvironment, messageCollector: MessageCollector): DaemonConnection? { + @Synchronized operator fun invoke(environment: CompilerEnvironment, messageCollector: MessageCollector): DaemonConnection? { if (connection == null) { val libPath = CompilerRunnerUtil.getLibPath(environment.kotlinPaths, messageCollector) val compilerId = CompilerId.makeCompilerId(File(libPath, "kotlin-compiler.jar"))