/* * Copyright (c) 2014, the Dart project authors. * * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html * * 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.google.dart.server.internal.remote; import com.google.dart.server.AnalysisServerListener; import com.google.dart.server.internal.remote.processor.NotificationServerErrorProcessor; import com.google.gson.JsonObject; import java.io.PrintStream; /** * A thread which reads input from the {@link LineReaderStream} error stream and translates * unhandled exceptions into server error notifications. */ public class ServerErrorReaderThread extends Thread { private final LineReaderStream stream; private final NotificationServerErrorProcessor processor; private final PrintStream syserr; public ServerErrorReaderThread(LineReaderStream errorStream, AnalysisServerListener listener) { this(errorStream, new NotificationServerErrorProcessor(listener), System.err); } public ServerErrorReaderThread(LineReaderStream stream, NotificationServerErrorProcessor processor, PrintStream errorStream) { this.processor = processor; this.stream = stream; this.syserr = errorStream; setDaemon(true); setName("ServerErrorReaderThread"); } /** * Read and process lines from the stream. */ public void processStream() throws Exception { while (true) { String line = stream.readLine(); if (line == null) { return; } syserr.println(line); if (!line.startsWith("Unhandled exception:")) { continue; } /* * Construct a special response if server has crashed * * Unhandled exception: * Uncaught Error: RangeError: value 90 * Stack Trace: * #0 _StringBase.substring (dart:core-patch/string_patch.dart:230) * #1 ... etc ... * * #0 _rootHandleUncaughtError.<anonymous closure> (dart:async/zone.dart:820) * #1 _asyncRunCallbackLoop (dart:async/schedule_microtask.dart:41) * #2 ... etc ... * * -- or -- * * Unhandled exception: * The null object does not have a method 'accept'. * * NoSuchMethodError: method not found: 'accept' * Receiver: null * Arguments: [Instance of 'Foo'] * #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:45) * #1 ... etc ... */ // Exception message StringBuilder message = new StringBuilder(200); while (true) { line = readAvailableLine(); if (line == null) { break; } line = line.trim(); if (line.startsWith("#")) { break; } if (line.length() > 0) { if (message.length() > 0) { message.append("\n"); } message.append(line); } } // Stack trace StringBuilder stack = new StringBuilder(); if (line != null) { while (true) { stack.append(line); line = readAvailableLine(); if (line == null) { break; } stack.append("\n"); } } JsonObject response = new JsonObject(); response.addProperty("event", RemoteAnalysisServerImpl.SERVER_NOTIFICATION_ERROR); JsonObject paramsObject = new JsonObject(); paramsObject.addProperty("isFatal", true); paramsObject.addProperty("message", message.toString()); paramsObject.addProperty("stackTrace", stack.toString()); response.add("params", paramsObject); processor.process(response); } } @Override public void run() { while (true) { try { processStream(); return; } catch (Exception e) { // TODO (jwren) handle error messages e.printStackTrace(); } } } private String readAvailableLine() throws Exception { if (!stream.ready()) { return null; } String line = stream.readLine(); if (line != null) { syserr.println(line); } return line; } }