/** * erlyberly, erlang trace debugger * Copyright (C) 2016 Andy Till * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package erlyberly; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import com.ericsson.otp.erlang.OtpErlangException; import com.ericsson.otp.erlang.OtpErlangObject; import erlyberly.node.NodeAPI.RpcCallback; import javafx.application.Platform; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; import javafx.collections.transformation.SortedList; import javafx.scene.control.TableColumn.SortType; /** * Logic and processing for the entop control. */ public class ProcController { private final SimpleBooleanProperty polling; private final SimpleObjectProperty<ProcSort> procSortProperty; private ProcPollerThread procPollerThread; private final Object waiter; private int timeout = -1; private final SimpleStringProperty filter = new SimpleStringProperty(); private final ObservableList<ProcInfo> processes = FXCollections.observableArrayList(); private final FilteredList<ProcInfo> filteredProcesses = new FilteredList<ProcInfo>(processes); private final SortedList<ProcInfo> sortedProcesses = new SortedList<ProcInfo>(filteredProcesses); private volatile boolean temporarilySuspendPolling; public ProcController() { polling = new SimpleBooleanProperty(); procSortProperty = new SimpleObjectProperty<>(new ProcSort("reduc", SortType.DESCENDING)); procPollerThread = new ProcPollerThread(); waiter = new Object(); Platform.runLater(() -> { ErlyBerly.nodeAPI().connectedProperty().addListener((o) -> { startPollingThread(); } ); }); filter.addListener((o, ov, nv) -> { updateProcFilter(nv); }); } public void setListComparator(ObservableValue<? extends Comparator<? super ProcInfo>> tableComparator) { sortedProcesses.comparatorProperty().bind(tableComparator); } private void updateProcFilter(String filterText) { BasicSearch basicSearch = new BasicSearch(filterText); filteredProcesses.setPredicate((proc) -> { return isMatchingProcess(basicSearch, proc); }); } private boolean isMatchingProcess(BasicSearch basicSearch, ProcInfo proc) { return basicSearch.matches(proc.getPid(), proc.getProcessName()); } private void startPollingThread() { if(ErlyBerly.nodeAPI().connectedProperty().get() && !procPollerThread.isAlive()) { procPollerThread.start(); } } public SimpleStringProperty filterProperty() { return filter; } public void refreshOnce() { synchronized (waiter) { waiter.notify(); } } public void togglePolling() { if(timeout == -1) { timeout = 1000; refreshOnce(); } else { timeout = -1; } polling.set(timeout > 0); } public void clearProcesses(){ processes.clear(); } public ObservableList<ProcInfo> getProcs() { return sortedProcesses; } public SimpleBooleanProperty pollingProperty() { return polling; } public SimpleObjectProperty<ProcSort> procSortProperty() { return procSortProperty; } public SimpleBooleanProperty connectedProperty() { return ErlyBerly.nodeAPI().connectedProperty(); } public boolean isTemporarilySuspendPolling() { return temporarilySuspendPolling; } public void setTemporarilySuspendPolling(boolean temporarilySuspendPolling) { this.temporarilySuspendPolling = temporarilySuspendPolling; } private final class ProcPollerThread extends Thread { public ProcPollerThread() { // make sure we don't hang the VM on close because of this thread setDaemon(true); setName("Process Info Poller"); } @Override public void run() { while(true) { boolean connected = ErlyBerly.nodeAPI().isConnected(); if(!connected) continue; final ArrayList<ProcInfo> processList = new ArrayList<>(); if (!temporarilySuspendPolling) { try { ErlyBerly.nodeAPI().retrieveProcessInfo(processList); updateProcessList(processList); } catch (Exception e) { e.printStackTrace(); } } try { synchronized (waiter) { if(timeout > 0) waiter.wait(timeout); else waiter.wait(); } } catch (InterruptedException e1) { e1.printStackTrace(); } } } private void updateProcessList(final ArrayList<ProcInfo> processList) { Platform.runLater(new Runnable() { @Override public void run() { ProcSort procSort = procSortProperty.get(); if(procSort != null) { Comparator<ProcInfo> comparator = null; if("proc".equals(procSort.getSortField())) { comparator = new Comparator<ProcInfo>() { @Override public int compare(ProcInfo o1, ProcInfo o2) { return o1.getProcessName().compareTo(o2.getProcessName()); }}; } else if("reduc".equals(procSort.getSortField())) { comparator = new Comparator<ProcInfo>() { @Override public int compare(ProcInfo o1, ProcInfo o2) { return Long.compare(o1.getReductions(), o2.getReductions()); }}; } if(comparator != null) { if(procSort.getSortType() == SortType.DESCENDING) { comparator = Collections.reverseOrder(comparator); } Collections.sort(processList, comparator); } } processes.clear(); processes.addAll(processList); }}); } } public void processState(ProcInfo proc, RpcCallback<OtpErlangObject> callback) { new ProcessStateThread(proc.getPid(), callback).start(); } class ProcessStateThread extends Thread { private final String pidString; private final RpcCallback<OtpErlangObject> callback; public ProcessStateThread(String aPidString, RpcCallback<OtpErlangObject> aCallback) { pidString = aPidString; callback = aCallback; setDaemon(true); setName("Erlyberly Get Process State"); } @Override public void run() { try { OtpErlangObject processState = ErlyBerly.nodeAPI().getProcessState(pidString); Platform.runLater(() -> { callback.callback(processState); }); } catch (OtpErlangException | IOException e) { e.printStackTrace(); } } } }