Skip to content

Commit

Permalink
#38 show error reports, new button in the top bar has a counter that
Browse files Browse the repository at this point in the history
increments when an error report is received.
  • Loading branch information
Andy Till committed Sep 11, 2015
1 parent 392f828 commit 8153b9e
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 92 deletions.
88 changes: 77 additions & 11 deletions src/main/java/erlyberly/TopBarView.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,39 @@
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.chart.PieChart.Data;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.Separator;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToolBar;
import javafx.scene.control.Tooltip;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import com.ericsson.otp.erlang.OtpErlangObject;

import de.jensd.fx.fontawesome.AwesomeIcon;
import de.jensd.fx.fontawesome.Icon;
import erlyberly.node.AppProcs;
Expand All @@ -44,6 +55,8 @@ public class TopBarView implements Initializable {

private static final KeyCodeCombination REFRESH_MODULES_SHORTCUT = new KeyCodeCombination(KeyCode.R, KeyCombination.SHORTCUT_DOWN);

private final SimpleIntegerProperty unreadCrashReportsProperty = new SimpleIntegerProperty(0);

@FXML
private ToggleButton hideProcessesButton;
@FXML
Expand All @@ -52,9 +65,11 @@ public class TopBarView implements Initializable {
private Button refreshModulesButton;
@FXML
private Button erlangMemoryButton;
@FXML
private Button crashReportsButton;
@FXML
private ToolBar topBox;

private EventHandler<ActionEvent> refreshModulesAction;

@Override
Expand All @@ -79,8 +94,14 @@ public void initialize(URL url, ResourceBundle r) {
erlangMemoryButton.setContentDisplay(ContentDisplay.TOP);
erlangMemoryButton.setGraphicTextGap(0d);
erlangMemoryButton.setTooltip(new Tooltip("Refresh Modules and Functions to show new, hot-loaded code (ctrl+r)"));
erlangMemoryButton.disableProperty().bind(ErlyBerly.nodeAPI().connectedProperty().not());

erlangMemoryButton.disableProperty().bind(ErlyBerly.nodeAPI().connectedProperty().not());

crashReportsButton.setGraphic(crashReportsGraphic());
crashReportsButton.setContentDisplay(ContentDisplay.TOP);
crashReportsButton.setGraphicTextGap(0d);
crashReportsButton.setTooltip(new Tooltip("Refresh Modules and Functions to show new, hot-loaded code (ctrl+r)"));
crashReportsButton.disableProperty().bind(ErlyBerly.nodeAPI().connectedProperty().not());
crashReportsButton.setOnAction((e) -> { showCrashReportWindow(); });
hideProcsProperty().addListener((Observable o) -> { toggleHideProcsText(); });
hideFunctionsProperty().addListener((Observable o) -> { toggleHideFuncsText(); });
erlangMemoryButton.setOnAction((e) -> { showErlangMemory(); });
Expand All @@ -92,8 +113,49 @@ public void initialize(URL url, ResourceBundle r) {

toggleHideProcsText();
toggleHideFuncsText();

ErlyBerly.nodeAPI()
.getCrashReports()
.addListener(this::traceLogsChanged);
}


public void traceLogsChanged(ListChangeListener.Change<? extends OtpErlangObject> e) {
while(e.next()) {
int size = e.getAddedSubList().size();
unreadCrashReportsProperty.set(unreadCrashReportsProperty.get() + size);
}
}
private void showCrashReportWindow() {
unreadCrashReportsProperty.set(0);

ListView<OtpErlangObject> crashReportListView;

crashReportListView = new ListView<OtpErlangObject>(ErlyBerly.nodeAPI().getCrashReports());
showWindow("Crash Reports", crashReportListView);
}

private Parent crashReportsGraphic() {
Icon icon;

icon = Icon.create().icon(AwesomeIcon.WARNING);
icon.setPadding(new Insets(0, 5, 0, 5));

Label reportCountLabel;

reportCountLabel = new Label("122");
reportCountLabel.setStyle("-fx-background-color:red; -fx-font-size:9; -fx-padding: 0 2 0 2; -fx-opacity:0.7");
reportCountLabel.setTextFill(Color.WHITE);

reportCountLabel.setText(unreadCrashReportsProperty.getValue().toString());
unreadCrashReportsProperty.addListener((o, oldv, newv) -> { reportCountLabel.setText(newv.toString()); });
reportCountLabel.visibleProperty().bind(unreadCrashReportsProperty.greaterThan(0));

StackPane stackPane = new StackPane(icon, reportCountLabel);
StackPane.setAlignment(reportCountLabel, Pos.TOP_RIGHT);
return stackPane;
}

private void showErlangMemory() {
ObservableList<PieChart.Data> data = FXCollections.observableArrayList();

Expand All @@ -112,18 +174,22 @@ private void showPieChart(ObservableList<PieChart.Data> data) {
pieChart = new PieChart(data);
pieChart.setTitle(title);

Stage pieStage = new Stage();
showWindow(title, pieChart);
}

private void showWindow(String title, Parent pieChart) {
Stage stage = new Stage();
Scene scene = new Scene(pieChart);

CloseWindowOnEscape.apply(scene, pieStage);
CloseWindowOnEscape.apply(scene, stage);

pieStage.setScene(scene);
pieStage.setWidth(800);
pieStage.setHeight(600);
pieStage.setTitle(title);
stage.setScene(scene);
stage.setWidth(800);
stage.setHeight(600);
stage.setTitle(title);

pieStage.show();
}
stage.show();
}

/**
* these have to be run after initialisation is complete or an exception occurs
Expand Down
99 changes: 72 additions & 27 deletions src/main/java/erlyberly/node/NodeAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import com.ericsson.otp.erlang.OtpAuthException;
import com.ericsson.otp.erlang.OtpConn;
import com.ericsson.otp.erlang.OtpErlangAtom;
import com.ericsson.otp.erlang.OtpErlangBinary;
import com.ericsson.otp.erlang.OtpErlangDecodeException;
import com.ericsson.otp.erlang.OtpErlangException;
import com.ericsson.otp.erlang.OtpErlangExit;
import com.ericsson.otp.erlang.OtpErlangInt;
import com.ericsson.otp.erlang.OtpErlangList;
import com.ericsson.otp.erlang.OtpErlangLong;
Expand All @@ -43,8 +47,11 @@
import erlyberly.TraceLog;

public class NodeAPI {
private static final OtpErlangAtom ERLYBERLY_ATOM = new OtpErlangAtom("erlyberly");

public interface RpcCallback<T> {
private static final OtpErlangAtom BET_SERVICES_MSG_ATOM = new OtpErlangAtom("add_locator");

public interface RpcCallback<T> {
void callback(T result);
}

Expand Down Expand Up @@ -75,6 +82,8 @@ public interface RpcCallback<T> {
private OtpMbox mbox;

private final AtomicBoolean connected = new AtomicBoolean();

private final ObservableList<OtpErlangObject> crashReports = FXCollections.observableArrayList();

public NodeAPI() {
traceManager = new TraceManager();
Expand All @@ -99,7 +108,11 @@ public NodeAPI connectionInfo(String remoteNodeName, String cookie) {
return this;
}

public SimpleObjectProperty<AppProcs> appProcsProperty() {
public ObservableList<OtpErlangObject> getCrashReports() {
return crashReports;
}

public SimpleObjectProperty<AppProcs> appProcsProperty() {
return appProcs;
}

Expand All @@ -124,6 +137,8 @@ public synchronized void connect() throws IOException, OtpErlangException, OtpAu
mbox = self.createMbox();

loadRemoteErlyberly();

addErrorLoggerHandler();

Platform.runLater(() -> { connectedProperty.set(true); });

Expand All @@ -134,7 +149,18 @@ public synchronized void connect() throws IOException, OtpErlangException, OtpAu
checkAliveThread.start();
}

class CheckAliveThread extends Thread {
private void addErrorLoggerHandler() throws IOException, OtpErlangException {
OtpErlangList args = OtpUtil.list(mbox.self());
sendRPC(
"error_logger", "add_report_handler",
OtpUtil.list(ERLYBERLY_ATOM, args)
);

// flush the return value
receiveRPC();
}

class CheckAliveThread extends Thread {
public CheckAliveThread() {
setDaemon(true);
setName("Erlyberly Check Alive");
Expand Down Expand Up @@ -175,17 +201,36 @@ private void loadRemoteErlyberly() throws IOException, OtpErlangException {
}

private OtpErlangObject receiveRPC() throws IOException, OtpErlangException {
OtpErlangObject result = OtpUtil.receiveRPC(mbox);
int timeout = 5000;
return receiveRPC(timeout);
}

// hack to support certain projects, don't ask...
if(result instanceof OtpErlangTuple) {
if(new OtpErlangAtom("add_locator").equals(((OtpErlangTuple) result).elementAt(0))) {
result = receiveRPC();
}
private OtpErlangObject receiveRPC(int timeout) throws OtpErlangExit,
OtpErlangDecodeException, IOException, OtpErlangException {
OtpErlangTuple receive = OtpUtil.receiveRPC(mbox, timeout);

if(receive == null)
return null;

if(isTupleTagged(atom("erlyberly_error_report"), receive)) {
Platform.runLater(() -> {
crashReports.add(receive.elementAt(1));
});
return receiveRPC();
}

if(!isTupleTagged(atom("rex"), receive))
throw new RuntimeException("Expected tuple tagged with atom rex but got " + receive);

OtpErlangObject result = receive.elementAt(1);

// hack to support certain projects, don't ask...
if(isTupleTagged(BET_SERVICES_MSG_ATOM, result)) {
result = receiveRPC();
}

return result;
}
}

private static byte[] loadBeamFile() throws IOException {
InputStream resourceAsStream = OtpUtil.class.getResourceAsStream(ERLYBERLY_BEAM_PATH);
Expand Down Expand Up @@ -231,23 +276,23 @@ public synchronized void retrieveProcessInfo(ArrayList<ProcInfo> processes) thro
}

private boolean ensureAlive() {
if(connection.isAlive())
return true;

Platform.runLater(() -> { connectedProperty.set(false); });

while(true) {
try {
connect();
break;
}
catch(Exception e) {
int millis = 50;
mySleep(millis);

}
}
return true;
try {
receiveRPC(0);
} catch (OtpErlangException | IOException e1) {
Platform.runLater(() -> { connectedProperty.set(false); });
while(true) {
try {
connect();
break;
}
catch(Exception e) {
int millis = 50;
mySleep(millis);

}
}
}
return true;
}

private void mySleep(int millis) {
Expand Down
16 changes: 6 additions & 10 deletions src/main/java/erlyberly/node/OtpUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -229,16 +229,12 @@ public static void sendRPC(OtpConn conn, OtpMbox m, OtpErlangAtom mod, OtpErlang
conn.send(m.self(), "rex", rpcMessage);
}

public static OtpErlangObject receiveRPC(OtpMbox mbox) throws OtpErlangExit, OtpErlangDecodeException {
OtpErlangTuple receive = (OtpErlangTuple) mbox.receive(5000);

// FIXME timeouts
if(receive == null)
return null;
if(!isTupleTagged(atom("rex"), receive))
throw new RuntimeException("Expected tuple tagged with atom rex but got " + receive);

return receive.elementAt(1);
public static OtpErlangTuple receiveRPC(OtpMbox mbox) throws OtpErlangExit, OtpErlangDecodeException {
return receiveRPC(mbox,5000);
}

public static OtpErlangTuple receiveRPC(OtpMbox mbox ,long timeout) throws OtpErlangExit, OtpErlangDecodeException {
return (OtpErlangTuple) mbox.receive(timeout);
}

public static OtpErlangObject tupleElement(int i, OtpErlangObject obj) {
Expand Down
Loading

0 comments on commit 8153b9e

Please sign in to comment.