/** * Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package org.python.pydev.shared_interactive_console.console; import java.net.MalformedURLException; import java.net.URL; import java.util.concurrent.atomic.AtomicReference; import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.XmlRpcRequest; import org.apache.xmlrpc.client.AsyncCallback; import org.apache.xmlrpc.client.XmlRpcClient; import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; import org.python.pydev.shared_core.net.LocalHost; import org.python.pydev.shared_core.string.StringUtils; /** * 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 ScriptXmlRpcClient implements IXmlRpcClient { /** * Internal xml-rpc client (responsible for the actual communication with the server) */ private XmlRpcClient impl; /** * The process where the server is being executed. */ private Process process; /** * Constructor (see fields description) */ public ScriptXmlRpcClient(Process process) { this.impl = new XmlRpcClient(); this.process = process; } /** * Sets the port where the server is started. * @throws MalformedURLException */ @Override public void setPort(int port) throws MalformedURLException { XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); config.setServerURL(new URL("http://" + LocalHost.getLocalHost() + ":" + port)); this.impl.setConfig(config); } /** * 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 { if (process != null) { try { int exitValue = process.exitValue(); return StringUtils .format("Console already exited with value: %s while waiting for an answer.\n", exitValue); } catch (IllegalThreadStateException e) { // Ok, keep on going } } final AtomicReference<Object> result = new AtomicReference<Object>(null); //make an async call so that we can keep track of not actually having an answer. this.impl.executeAsync(command, args, new AsyncCallback() { @Override public void handleError(XmlRpcRequest request, Throwable error) { result.set(error.getMessage()); } @Override public void handleResult(XmlRpcRequest request, Object receivedResult) { result.set(receivedResult); } }); //busy loop waiting for the answer (or having the console die). while (result.get() == null) { try { if (process != null) { int exitValue = process.exitValue(); result.set(StringUtils.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.log(e1); } } } } return result.get(); } }