/** * Initializes the xml-rpc communication. * * @param port the port where the communication should happen. * @param process this is the process that was spawned (server for the XML-RPC) * @throws MalformedURLException */ public PydevConsoleCommunication(int port, Process process, int clientPort) throws Exception { stdOutReader = new ThreadStreamReader(process.getInputStream()); stdErrReader = new ThreadStreamReader(process.getErrorStream()); stdOutReader.start(); stdErrReader.start(); // start the server that'll handle input requests this.webServer = new WebServer(clientPort); XmlRpcServer serverToHandleRawInput = this.webServer.getXmlRpcServer(); serverToHandleRawInput.setHandlerMapping( new XmlRpcHandlerMapping() { public XmlRpcHandler getHandler(String handlerName) throws XmlRpcNoSuchHandlerException, XmlRpcException { return PydevConsoleCommunication.this; } }); this.webServer.start(); IXmlRpcClient client = new ScriptXmlRpcClient(process, stdErrReader, stdOutReader); client.setPort(port); this.client = client; }
/** Called when the server is requesting some input from this class. */ public Object execute(XmlRpcRequest request) throws XmlRpcException { waitingForInput = true; inputReceived = null; boolean needInput = true; String stdOutContents = stdOutReader.getAndClearContents(); String stderrContents = stdErrReader.getAndClearContents(); // let the busy loop from execInterpreter free and enter a busy loop // in this function until execInterpreter gives us an input setNextResponse(new InterpreterResponse(stdOutContents, stderrContents, false, needInput)); // busy loop until we have an input while (inputReceived == null) { synchronized (lock) { try { lock.wait(10); } catch (InterruptedException e) { Log.log(e); } } } return inputReceived; }
/** * Executes a given line in the interpreter. * * @param command the command to be executed in the client */ public void execInterpreter( final String command, final ICallback<Object, InterpreterResponse> onResponseReceived, final ICallback<Object, Tuple<String, String>> onContentsReceived) { setNextResponse(null); if (waitingForInput) { inputReceived = command; waitingForInput = false; // the thread that we started in the last exec is still alive if we were waiting for an input. } else { // create a thread that'll keep locked until an answer is received from the server. Job job = new Job("PyDev Console Communication") { /** * Executes the needed command * * @return a tuple with (null, more) or (error, false) * @throws XmlRpcException */ private Tuple<String, Boolean> exec() throws XmlRpcException { if (client == null) { return new Tuple<String, Boolean>( "PydevConsoleCommunication.client is null (cannot communicate with server).", false); } Object[] execute = (Object[]) client.execute("addExec", new Object[] {command}); Object object = execute[0]; boolean more; String errorContents = null; if (object instanceof Boolean) { more = (Boolean) object; } else { String str = object.toString(); String lower = str.toLowerCase(); if (lower.equals("true") || lower.equals("1")) { more = true; } else if (lower.equals("false") || lower.equals("0")) { more = false; } else { more = false; errorContents = str; } } return new Tuple<String, Boolean>(errorContents, more); } @Override protected IStatus run(IProgressMonitor monitor) { boolean needInput = false; try { Tuple<String, Boolean> executed = null; // the 1st time we'll do a connection attempt, we can try to connect n times (until // the 1st time the connection // is accepted) -- that's mostly because the server may take a while to get started. int commAttempts = 0; int maximumAttempts = InteractiveConsolePrefs.getMaximumAttempts(); // System.out.println(maximumAttempts); while (true) { if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } executed = exec(); // executed.o1 is not null only if we had an error String refusedConnPattern = "Failed to read servers response"; // Was "refused", but it didn't // work on non English system // (in Spanish localized systems // it is "rechazada") // This string always works, // because it is hard-coded in // the XML-RPC library) if (executed.o1 != null && executed.o1.indexOf(refusedConnPattern) != -1) { if (firstCommWorked) { break; } else { if (commAttempts < maximumAttempts) { commAttempts += 1; Thread.sleep(250); executed.o1 = stdErrReader.getAndClearContents(); continue; } else { break; } } } else { break; } // unreachable code!! -- commented because eclipse will complain about it // throw new RuntimeException("Can never get here!"); } firstCommWorked = true; String errorContents = executed.o1; boolean more = executed.o2; String stdOutContents; if (errorContents == null) { errorContents = stdErrReader.getAndClearContents(); } else { errorContents += "\n" + stdErrReader.getAndClearContents(); } stdOutContents = stdOutReader.getAndClearContents(); setNextResponse( new InterpreterResponse(stdOutContents, errorContents, more, needInput)); } catch (Exception e) { Log.log(e); setNextResponse( new InterpreterResponse( "", "Exception while pushing line to console:" + e.getMessage(), false, needInput)); } return Status.OK_STATUS; } }; job.schedule(); } int i = 500; // only get contents each 500 millis... // busy loop until we have a response while (nextResponse == null) { synchronized (lock2) { try { lock2.wait(20); } catch (InterruptedException e) { // Log.log(e); } } i -= 20; if (i <= 0 && nextResponse == null) { i = 250; // after the first, get it each 250 millis String stderrContents = stdErrReader.getAndClearContents(); String stdOutContents = stdOutReader.getAndClearContents(); if (stdOutContents.length() > 0 || stderrContents.length() > 0) { onContentsReceived.call(new Tuple<String, String>(stdOutContents, stderrContents)); } } } onResponseReceived.call(nextResponse); }