diff --git a/python/pydevSrc/com/jetbrains/python/console/pydev/IPydevXmlRpcClient.java b/python/pydevSrc/com/jetbrains/python/console/pydev/IPydevXmlRpcClient.java index 2477e9f9d60a..622e14a2f49e 100644 --- a/python/pydevSrc/com/jetbrains/python/console/pydev/IPydevXmlRpcClient.java +++ b/python/pydevSrc/com/jetbrains/python/console/pydev/IPydevXmlRpcClient.java @@ -19,4 +19,7 @@ public interface IPydevXmlRpcClient { * @throws XmlRpcException */ Object execute(String command, Object[] args) throws XmlRpcException; + + Object execute(String command, Object[] args, long timeoutMillis) throws XmlRpcException; + } diff --git a/python/pydevSrc/com/jetbrains/python/console/pydev/PydevXmlRpcClient.java b/python/pydevSrc/com/jetbrains/python/console/pydev/PydevXmlRpcClient.java deleted file mode 100644 index ad9991f51736..000000000000 --- a/python/pydevSrc/com/jetbrains/python/console/pydev/PydevXmlRpcClient.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.jetbrains.python.console.pydev; - -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.util.net.NetUtils; -import org.apache.xmlrpc.*; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Arrays; -import java.util.Vector; - -/** - * Subclass of XmlRpcClient that will monitor the process so that if the process is destroyed, we stop waiting - * for messages from it. - * - * @author Fabio - */ -public class PydevXmlRpcClient implements IPydevXmlRpcClient { - - /** - * Internal xml-rpc client (responsible for the actual communication with the server) - */ - private final XmlRpcClient impl; - - /** - * The process where the server is being executed. - */ - private final Process process; - - /** - * ItelliJ Logging - */ - private static final Logger LOG = Logger.getInstance(PydevXmlRpcClient.class.getName()); - - private static final long TIME_LIMIT = 60000; - - - /** - * Constructor (see fields description) - */ - public PydevXmlRpcClient(Process process, int port) throws MalformedURLException { - XmlRpc.setDefaultInputEncoding("UTF8"); //even though it uses UTF anyway - impl = new XmlRpcClientLite(NetUtils.getLocalHostString(), port); - //this.impl = new XmlRpcClient(url, new CommonsXmlRpcTransportFactory(url)); - this.process = process; - } - - /** - * Executes a command in the server. - *

- * Within this method, we should be careful about being able to return if the server dies. - * If we wanted to have a timeout, this would be the place to add it. - * - * @return the result from executing the given command in the server. - */ - @Override - public Object execute(String command, Object[] args) throws XmlRpcException { - final Object[] result = new Object[]{null}; - - //make an async call so that we can keep track of not actually having an answer. - impl.executeAsync(command, new Vector(Arrays.asList(args)), new AsyncCallback() { - - @Override - public void handleError(Exception error, URL url, String method) { - result[0] = new Object[]{error.getMessage()}; - } - - @Override - public void handleResult(Object recievedResult, URL url, String method) { - result[0] = recievedResult; - } - }); - - long started = System.currentTimeMillis(); - //busy loop waiting for the answer (or having the console die). - while (result[0] == null && System.currentTimeMillis() - started < TIME_LIMIT) { - try { - if (process != null) { - int exitValue = process.exitValue(); - result[0] = new Object[]{String.format("Console already exited with value: %s while waiting for an answer.\n", exitValue)}; - //ok, we have an exit value! - break; - } - } - catch (IllegalThreadStateException e) { - //that's ok... let's sleep a bit - synchronized (this) { - try { - wait(10); - } - catch (InterruptedException e1) { - LOG.error(e1); - } - } - } - } - if (result[0] == null) { - throw new XmlRpcException(-1, "Timeout while connecting to server"); - } - return result[0]; - } -} diff --git a/python/pydevSrc/com/jetbrains/python/console/pydev/PydevXmlRpcClient.kt b/python/pydevSrc/com/jetbrains/python/console/pydev/PydevXmlRpcClient.kt new file mode 100644 index 000000000000..6a01ff303beb --- /dev/null +++ b/python/pydevSrc/com/jetbrains/python/console/pydev/PydevXmlRpcClient.kt @@ -0,0 +1,101 @@ +package com.jetbrains.python.console.pydev + +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.progress.ProgressManager +import com.intellij.util.net.NetUtils +import org.apache.xmlrpc.* + +import java.net.MalformedURLException +import java.net.URL +import java.util.Arrays +import java.util.Vector +import java.util.concurrent.TimeUnit + +/** + * Subclass of XmlRpcClient that will monitor the process so that if the process is destroyed, we stop waiting + * for messages from it. + + * @author Fabio + */ +class PydevXmlRpcClient +/** + * Constructor (see fields description) + */ +@Throws(MalformedURLException::class) +constructor(private val process: Process, port: Int) : IPydevXmlRpcClient { + + + /** + * Internal xml-rpc client (responsible for the actual communication with the server) + */ + private val impl: XmlRpcClient + + + init { + XmlRpc.setDefaultInputEncoding("UTF8") //even though it uses UTF anyway + impl = XmlRpcClientLite(NetUtils.getLocalHostString(), port) + } + + + override fun execute(command: String, args: Array): Any { + return execute(command, args, TIME_LIMIT) + } + + /** + * Executes a command in the server. + * + * + * Within this method, we should be careful about being able to return if the server dies. + * If we wanted to have a timeout, this would be the place to add it. + + * @return the result from executing the given command in the server. + */ + @Throws(XmlRpcException::class) + override fun execute(command: String, args: Array, timeoutMillis: Long): Any { + val result = arrayOf(null) + + //make an async call so that we can keep track of not actually having an answer. + impl.executeAsync(command, Vector(Arrays.asList(*args)), object : AsyncCallback { + + override fun handleError(error: Exception, url: URL, method: String) { + result[0] = makeError(error.message ?: "Unknown Error") + } + + override fun handleResult(recievedResult: Any, url: URL, method: String) { + result[0] = recievedResult + } + }) + + val started = System.currentTimeMillis() + val progress = ProgressManager.getInstance().progressIndicator + //busy loop waiting for the answer (or having the console die). + while (result[0] == null && System.currentTimeMillis() - started < TIME_LIMIT) { + + progress?.let { + progress.checkCanceled() + } + val exitValue = process.waitFor(10, TimeUnit.MILLISECONDS) + if (exitValue) { + result[0] = makeError(String.format("Console already exited with value: %s while waiting for an answer.\n", exitValue)) + break + } + } + + return result[0] ?: throw XmlRpcException(-1, "Timeout while connecting to server") + + } + + fun makeError(error: String): Array { + return arrayOf(error) + } + + companion object { + + /** + * ItelliJ Logging + */ + private val LOG = Logger.getInstance(PydevXmlRpcClient::class.java.name) + + private val TIME_LIMIT: Long = 60000 + } +} diff --git a/python/python-pydev.iml b/python/python-pydev.iml index 4f1b6190919b..369b9f5258a2 100644 --- a/python/python-pydev.iml +++ b/python/python-pydev.iml @@ -12,6 +12,6 @@ + - - + \ No newline at end of file diff --git a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java index 3398c9780901..324f9e8c7e9b 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java @@ -137,7 +137,7 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl */ public synchronized void close() { if (this.myClient != null) { - new Task.Backgroundable(myProject, "Close console communication", true) { + new Task.Backgroundable(myProject, "Close Console Communication", true) { @Override public void run(@NotNull ProgressIndicator indicator) { try { @@ -326,7 +326,7 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl if (waitingForInput) { return "Unable to get description: waiting for input."; } - return myClient.execute(GET_DESCRIPTION, new Object[]{text}).toString(); + return myClient.execute(GET_DESCRIPTION, new Object[]{text}, 5000).toString(); } /** diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java index 43cf255f4ea6..6d39499c0f30 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java @@ -913,8 +913,12 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner { @Override public void run(@NotNull ProgressIndicator indicator) { if (myProcessHandler != null) { - UIUtil.invokeLaterIfNeeded(() -> closeCommunication()); + UIUtil.invokeAndWaitIfNeeded((Runnable)() -> closeCommunication()); + boolean processStopped = myProcessHandler.waitFor(5000L); + if (!processStopped && myProcessHandler.canKillProcess()) { + myProcessHandler.killProcess(); + } myProcessHandler.waitFor(); }