/* * Copyright 2014 TWO SIGMA OPEN SOURCE, LLC * * 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 com.twosigma.beaker.jvm.object; import com.twosigma.beaker.jvm.threads.BeakerOutputHandler; import com.twosigma.beaker.jvm.threads.BeakerStdOutErrHandler; import java.util.ArrayList; import java.util.List; import java.util.Observable; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import com.twosigma.jupyter.KernelFunctionality; import com.twosigma.jupyter.message.Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static com.google.common.base.Preconditions.checkNotNull; /** * Abstraction around an evaluation, for communication of the state over REST to the plugin. */ public class SimpleEvaluationObject extends Observable { private final static Logger logger = LoggerFactory.getLogger(SimpleEvaluationObject.class.getName()); private final KernelFunctionality.ExecuteCodeCallback executeCodeCallback; private Message jupyterMessage; private int executionCount; private EvaluationStatus status; private final String expression; private Object payload; private BeakerOutputHandler stdout; private BeakerOutputHandler stderr; private Queue<ConsoleOutput> consoleOutput = new ConcurrentLinkedQueue<ConsoleOutput>(); private ProgressReporting progressReporting; public SimpleEvaluationObject(String e, final KernelFunctionality.ExecuteCodeCallback executeCodeCallback) { expression = e; status = EvaluationStatus.QUEUED; this.executeCodeCallback = checkNotNull(executeCodeCallback); } public void executeCodeCallback() { this.executeCodeCallback.execute(this); } public synchronized void started() { setOutputHandler(); this.status = EvaluationStatus.RUNNING; setChanged(); notifyObservers(); } public synchronized void finished(Object r) { clrOutputHandler(); this.status = EvaluationStatus.FINISHED; payload = r; setChanged(); notifyObservers(); } public synchronized void error(Object r) { clrOutputHandler(); this.status = EvaluationStatus.ERROR; payload = r; setChanged(); notifyObservers(); } public synchronized void update(Object r) { this.status = EvaluationStatus.RUNNING; payload = r; setChanged(); notifyObservers(); } public String getExpression() { return expression; } public synchronized EvaluationStatus getStatus() { return status; } public synchronized Object getPayload() { return payload; } public void structuredUpdate(String message, int progress) { if(progressReporting ==null){ progressReporting = new ProgressReporting(); } progressReporting.structuredUpdate(message,progress); } public static enum EvaluationStatus { QUEUED, RUNNING, FINISHED, ERROR } public class SimpleOutputHandler implements BeakerOutputHandler { private boolean error; public SimpleOutputHandler(boolean error){ this.error = error; } @Override public void write(int b) { byte [] ba = new byte[1]; ba[0] = (byte) b; consoleOutput.add(new ConsoleOutput(error, new String(ba))); setChanged(); notifyObservers(); } @Override public void write(byte[] b) { consoleOutput.add(new ConsoleOutput(error, new String(b))); setChanged(); notifyObservers(); } @Override public void write(byte[] b, int off, int len) { consoleOutput.add(new ConsoleOutput(error, new String(b,off,len))); setChanged(); notifyObservers(); } } public synchronized BeakerOutputHandler getStdOutputHandler() { if (stdout == null) stdout = new SimpleOutputHandler(false); return stdout; } public synchronized BeakerOutputHandler getStdErrorHandler() { if (stderr == null) stderr = new SimpleOutputHandler(true); return stderr; } public void setOutputHandler() { BeakerStdOutErrHandler.setOutputHandler(getStdOutputHandler(), getStdErrorHandler()); } public void clrOutputHandler() { closeProgressUpdater(); BeakerStdOutErrHandler.clrOutputHandler(); } private void closeProgressUpdater() { if (progressReporting != null) { progressReporting.close(); progressReporting = null; } } public Message getJupyterMessage() { return jupyterMessage; } public void setJupyterMessage(Message jupyterMessage) { this.jupyterMessage = jupyterMessage; } public int getExecutionCount() { return executionCount; } public void setExecutionCount(int executionCount) { this.executionCount = executionCount; } public Queue<ConsoleOutput> getConsoleOutput() { return consoleOutput; } @Override public String toString() { return status.toString() + " Console messages size = " + consoleOutput.size(); } private static final int OUTPUT_QUEUE_SIZE = 20; private static final int MAX_LINE_LENGTH = 240; private int outputdataCount= 0; private String buildingout= ""; private List<Object> outputdata= new ArrayList<Object>(); private String buildingerr= ""; public List<Object> getOutputdata() { return outputdata; } public void appendOutput(String s) { if (getSize() > OUTPUT_QUEUE_SIZE) { try { Thread.sleep(500); } catch (InterruptedException e) { } } if (getSize() > OUTPUT_QUEUE_SIZE) { try { Thread.sleep(500); } catch (InterruptedException e) { } } doAppendOutput(s); } private synchronized int getSize() { return outputdataCount; } private synchronized void doAppendOutput(String s) { buildingout += s; String add = null; if (s.contains("\n")) { if (s.endsWith("\n")) { add = buildingout; buildingout = ""; } else { add = buildingout.substring(0, buildingout.lastIndexOf('\n')+1); buildingout = buildingout.substring(buildingout.lastIndexOf('\n')+1); } } if ( buildingout.length() > MAX_LINE_LENGTH) { add = buildingout; buildingout = ""; } if (add != null) { String [] v = add.split("\n"); for (String sv : v) { while (sv.length()>MAX_LINE_LENGTH) { String t = sv.substring(0, MAX_LINE_LENGTH); sv = sv.substring(MAX_LINE_LENGTH); if (outputdata.size() == 0 || !(outputdata.get(outputdata.size()-1) instanceof EvaluationStdOutput)) { outputdata.add(new EvaluationStdOutput(t+"\n")); } else { EvaluationStdOutput st = (EvaluationStdOutput) outputdata.get(outputdata.size()-1); st.payload += t+"\n"; } outputdataCount ++; } if (outputdata.size() == 0 || !(outputdata.get(outputdata.size()-1) instanceof EvaluationStdOutput)) { outputdata.add(new EvaluationStdOutput(sv+"\n")); } else { EvaluationStdOutput st = (EvaluationStdOutput) outputdata.get(outputdata.size()-1); st.payload += sv+"\n"; } outputdataCount ++; } setChanged(); notifyObservers(); } } public void appendError(String s) { if (getSize() > OUTPUT_QUEUE_SIZE) { try { Thread.sleep(500); } catch (InterruptedException e) { } } if (getSize() > OUTPUT_QUEUE_SIZE) { try { Thread.sleep(500); } catch (InterruptedException e) { } } doAppendError(s); } private synchronized void doAppendError(String s) { buildingerr += s; String add = null; if (s.contains("\n")) { if (s.endsWith("\n")) { add = buildingerr; buildingerr = ""; } else { add = buildingerr.substring(0, buildingerr.lastIndexOf('\n')+1); buildingerr = buildingerr.substring(buildingerr.lastIndexOf('\n')+1); } } if ( buildingerr.length() > MAX_LINE_LENGTH) { add = buildingerr; buildingerr = ""; } if (add != null) { /* * HACK to remove annoying stderr messages from third party libraries */ if ((add.contains("org.antlr.v4.runtime.misc.NullUsageProcessor") && add.contains("'RELEASE_6'")) || (add.contains("JavaSourceCompilerImpl compile"))) { String [] v = add.split("\n"); add = ""; for(String s2 : v) { if (!s2.contains("org.antlr.v4.runtime.misc.NullUsageProcessor") && !s2.contains("JavaSourceCompilerImpl compile")) add += s2 + "\n"; } } String [] v = add.split("\n"); for (String sv : v) { while (sv.length()>MAX_LINE_LENGTH) { String t = sv.substring(0, MAX_LINE_LENGTH); sv = sv.substring(MAX_LINE_LENGTH); if (outputdata.size() == 0 || !(outputdata.get(outputdata.size()-1) instanceof EvaluationStdError)) { outputdata.add(new EvaluationStdError(t+"\n")); } else { EvaluationStdError st = (EvaluationStdError) outputdata.get(outputdata.size()-1); st.payload += t+"\n"; } outputdataCount ++; } if (outputdata.size() == 0 || !(outputdata.get(outputdata.size()-1) instanceof EvaluationStdError)) { outputdata.add(new EvaluationStdError(sv+"\n")); } else { EvaluationStdError st = (EvaluationStdError) outputdata.get(outputdata.size()-1); st.payload += sv+"\n"; } outputdataCount ++; } setChanged(); notifyObservers(); } } public class EvaluationStdOutput { public String payload; public EvaluationStdOutput(String s) { payload = s; } } public class EvaluationStdError { public String payload; public EvaluationStdError(String s) { payload = s; } } }