From 705b04b0dacd84297ccbf1c18eee273393a6b50d Mon Sep 17 00:00:00 2001 From: 0xboobface <0xboobface@gmail.com> Date: Tue, 13 Nov 2018 00:59:09 +0100 Subject: [PATCH] In RecordedModelsTab get online state from the recorder Get the online state of the models on the recorded models tab from the recorder instead of requesting it from each model. The recorder knows the state anyways, so there is no need to do the requests again. --- .../java/ctbrec/recorder/LocalRecorder.java | 16 +++ src/main/java/ctbrec/recorder/Recorder.java | 6 ++ .../java/ctbrec/recorder/RemoteRecorder.java | 98 +++++++++++++------ .../recorder/server/RecorderServlet.java | 13 +++ src/main/java/ctbrec/ui/JavaFxModel.java | 5 + .../java/ctbrec/ui/RecordedModelsTab.java | 72 +++++++++----- 6 files changed, 158 insertions(+), 52 deletions(-) diff --git a/src/main/java/ctbrec/recorder/LocalRecorder.java b/src/main/java/ctbrec/recorder/LocalRecorder.java index a35f2f56..2db79755 100644 --- a/src/main/java/ctbrec/recorder/LocalRecorder.java +++ b/src/main/java/ctbrec/recorder/LocalRecorder.java @@ -19,7 +19,9 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -239,6 +241,20 @@ public class LocalRecorder implements Recorder { } } + @Override + public List getOnlineModels() { + return getModelsRecording() + .stream() + .filter(m -> { + try { + return m.isOnline(); + } catch (IOException | ExecutionException | InterruptedException e) { + return false; + } + }) + .collect(Collectors.toList()); + } + @Override public void shutdown() { LOG.info("Shutting down"); diff --git a/src/main/java/ctbrec/recorder/Recorder.java b/src/main/java/ctbrec/recorder/Recorder.java index b22fe1a4..e1ffa76b 100644 --- a/src/main/java/ctbrec/recorder/Recorder.java +++ b/src/main/java/ctbrec/recorder/Recorder.java @@ -33,4 +33,10 @@ public interface Recorder { public void resumeRecording(Model model) throws IOException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException; public boolean isSuspended(Model model); + + /** + * Returns only the models from getModelsRecording(), which are online + * @return + */ + public List getOnlineModels(); } diff --git a/src/main/java/ctbrec/recorder/RemoteRecorder.java b/src/main/java/ctbrec/recorder/RemoteRecorder.java index 2e07d10c..957906c1 100644 --- a/src/main/java/ctbrec/recorder/RemoteRecorder.java +++ b/src/main/java/ctbrec/recorder/RemoteRecorder.java @@ -42,6 +42,7 @@ public class RemoteRecorder implements Recorder { private JsonAdapter modelRequestAdapter = moshi.adapter(ModelRequest.class); private List models = Collections.emptyList(); + private List onlineModels = Collections.emptyList(); private List sites; private Config config; @@ -145,39 +146,76 @@ public class RemoteRecorder implements Recorder { public void run() { running = true; while(running) { - try { - String msg = "{\"action\": \"list\"}"; - RequestBody body = RequestBody.create(JSON, msg); - Request.Builder builder = new Request.Builder() - .url("http://" + config.getSettings().httpServer + ":" + config.getSettings().httpPort + "/rec") - .post(body); - addHmacIfNeeded(msg, builder); - Request request = builder.build(); - Response response = client.execute(request); - String json = response.body().string(); - if(response.isSuccessful()) { - ModelListResponse resp = modelListResponseAdapter.fromJson(json); - if(resp.status.equals("success")) { - models = resp.models; - for (Model model : models) { - for (Site site : sites) { - if(site.isSiteForModel(model)) { - model.setSite(site); - } + syncModels(); + syncOnlineModels(); + sleep(); + } + } + + private void syncModels() { + try { + String msg = "{\"action\": \"list\"}"; + RequestBody body = RequestBody.create(JSON, msg); + Request.Builder builder = new Request.Builder() + .url("http://" + config.getSettings().httpServer + ":" + config.getSettings().httpPort + "/rec") + .post(body); + addHmacIfNeeded(msg, builder); + Request request = builder.build(); + Response response = client.execute(request); + String json = response.body().string(); + if(response.isSuccessful()) { + ModelListResponse resp = modelListResponseAdapter.fromJson(json); + if(resp.status.equals("success")) { + models = resp.models; + for (Model model : models) { + for (Site site : sites) { + if(site.isSiteForModel(model)) { + model.setSite(site); + } + } + } + lastSync = Instant.now(); + } else { + LOG.error("Server returned error: {} - {}", resp.status, resp.msg); + } + } else { + LOG.error("Couldn't synchronize with server. HTTP status: {} - {}", response.code(), json); + } + } catch (IOException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e) { + LOG.error("Couldn't synchronize with server", e); + } + } + + private void syncOnlineModels() { + try { + String msg = "{\"action\": \"listOnline\"}"; + RequestBody body = RequestBody.create(JSON, msg); + Request.Builder builder = new Request.Builder() + .url("http://" + config.getSettings().httpServer + ":" + config.getSettings().httpPort + "/rec") + .post(body); + addHmacIfNeeded(msg, builder); + Request request = builder.build(); + Response response = client.execute(request); + String json = response.body().string(); + if(response.isSuccessful()) { + ModelListResponse resp = modelListResponseAdapter.fromJson(json); + if(resp.status.equals("success")) { + onlineModels = resp.models; + for (Model model : models) { + for (Site site : sites) { + if(site.isSiteForModel(model)) { + model.setSite(site); } } - lastSync = Instant.now(); - } else { - LOG.error("Server returned error: {} - {}", resp.status, resp.msg); } } else { - LOG.error("Couldn't synchronize with server. HTTP status: {} - {}", response.code(), json); + LOG.error("Server returned error: {} - {}", resp.status, resp.msg); } - } catch (IOException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e) { - LOG.error("Couldn't synchronize with server", e); + } else { + LOG.error("Couldn't synchronize with server. HTTP status: {} - {}", response.code(), json); } - - sleep(); + } catch (IOException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e) { + LOG.error("Couldn't synchronize with server", e); } } @@ -219,7 +257,6 @@ public class RemoteRecorder implements Recorder { Response response = client.execute(request); String json = response.body().string(); if(response.isSuccessful()) { - LOG.debug(json); RecordingListResponse resp = recordingListResponseAdapter.fromJson(json); if(resp.status.equals("success")) { List recordings = resp.recordings; @@ -311,4 +348,9 @@ public class RemoteRecorder implements Recorder { m.setSuspended(false); } } + + @Override + public List getOnlineModels() { + return onlineModels; + } } diff --git a/src/main/java/ctbrec/recorder/server/RecorderServlet.java b/src/main/java/ctbrec/recorder/server/RecorderServlet.java index 16a4210f..ad6f81dd 100644 --- a/src/main/java/ctbrec/recorder/server/RecorderServlet.java +++ b/src/main/java/ctbrec/recorder/server/RecorderServlet.java @@ -85,6 +85,19 @@ public class RecorderServlet extends AbstractCtbrecServlet { } resp.getWriter().write("]}"); break; + case "listOnline": + resp.getWriter().write("{\"status\": \"success\", \"msg\": \"List of online models\", \"models\": ["); + modelAdapter = new ModelJsonAdapter(); + models = recorder.getOnlineModels(); + for (Iterator iterator = models.iterator(); iterator.hasNext();) { + Model model = iterator.next(); + resp.getWriter().write(modelAdapter.toJson(model)); + if(iterator.hasNext()) { + resp.getWriter().write(','); + } + } + resp.getWriter().write("]}"); + break; case "recordings": resp.getWriter().write("{\"status\": \"success\", \"msg\": \"List of recordings\", \"recordings\": ["); JsonAdapter recAdapter = moshi.adapter(Recording.class); diff --git a/src/main/java/ctbrec/ui/JavaFxModel.java b/src/main/java/ctbrec/ui/JavaFxModel.java index 9cd8420a..7d94afb4 100644 --- a/src/main/java/ctbrec/ui/JavaFxModel.java +++ b/src/main/java/ctbrec/ui/JavaFxModel.java @@ -20,6 +20,7 @@ import javafx.beans.property.SimpleBooleanProperty; */ public class JavaFxModel implements Model { private transient BooleanProperty onlineProperty = new SimpleBooleanProperty(); + private transient BooleanProperty recordingProperty = new SimpleBooleanProperty(); private transient BooleanProperty pausedProperty = new SimpleBooleanProperty(); private Model delegate; @@ -86,6 +87,10 @@ public class JavaFxModel implements Model { return onlineProperty; } + public BooleanProperty getRecordingProperty() { + return recordingProperty; + } + public BooleanProperty getPausedProperty() { return pausedProperty; } diff --git a/src/main/java/ctbrec/ui/RecordedModelsTab.java b/src/main/java/ctbrec/ui/RecordedModelsTab.java index 3d5dfd37..d028c6c3 100644 --- a/src/main/java/ctbrec/ui/RecordedModelsTab.java +++ b/src/main/java/ctbrec/ui/RecordedModelsTab.java @@ -15,11 +15,13 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.Function; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.Model; +import ctbrec.Recording; import ctbrec.recorder.Recorder; import ctbrec.sites.Site; import javafx.application.Platform; @@ -59,7 +61,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { static BlockingQueue queue = new LinkedBlockingQueue<>(); static ExecutorService threadPool = new ThreadPoolExecutor(2, 2, 10, TimeUnit.MINUTES, queue); - private ScheduledService> updateService; + private ScheduledService> updateService; private Recorder recorder; private List sites; @@ -103,12 +105,16 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { TableColumn online = new TableColumn<>("Online"); online.setCellValueFactory((cdf) -> cdf.getValue().getOnlineProperty()); online.setCellFactory(CheckBoxTableCell.forTableColumn(online)); - online.setPrefWidth(60); + online.setPrefWidth(100); + TableColumn recording = new TableColumn<>("Recording"); + recording.setCellValueFactory((cdf) -> cdf.getValue().getRecordingProperty()); + recording.setCellFactory(CheckBoxTableCell.forTableColumn(recording)); + recording.setPrefWidth(100); TableColumn paused = new TableColumn<>("Paused"); paused.setCellValueFactory((cdf) -> cdf.getValue().getPausedProperty()); paused.setCellFactory(CheckBoxTableCell.forTableColumn(paused)); - paused.setPrefWidth(60); - table.getColumns().addAll(name, url, online, paused); + paused.setPrefWidth(100); + table.getColumns().addAll(name, url, online, recording, paused); table.setItems(observableModels); table.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, event -> { popup = createContextMenu(); @@ -185,28 +191,24 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { updateService = createUpdateService(); updateService.setPeriod(new Duration(TimeUnit.SECONDS.toMillis(2))); updateService.setOnSucceeded((event) -> { - List models = updateService.getValue(); + List models = updateService.getValue(); if(models == null) { return; } - queue.clear(); - for (Model model : models) { - int index = observableModels.indexOf(model); - final JavaFxModel javaFxModel; + + for (JavaFxModel updatedModel : models) { + int index = observableModels.indexOf(updatedModel); if (index == -1) { - javaFxModel = new JavaFxModel(model); - observableModels.add(javaFxModel); + observableModels.add(updatedModel); } else { // make sure to update the JavaFX online property, so that the table cell is updated - javaFxModel = observableModels.get(index); + JavaFxModel oldModel = observableModels.get(index); + oldModel.setSuspended(updatedModel.isSuspended()); + oldModel.getOnlineProperty().set(updatedModel.getOnlineProperty().get()); + oldModel.getRecordingProperty().set(updatedModel.getRecordingProperty().get()); } - threadPool.submit(() -> { - try { - javaFxModel.getOnlineProperty().set(javaFxModel.isOnline()); - javaFxModel.setSuspended(model.isSuspended()); - } catch (IOException | ExecutionException | InterruptedException e) {} - }); } + for (Iterator iterator = observableModels.iterator(); iterator.hasNext();) { Model model = iterator.next(); if (!models.contains(model)) { @@ -219,15 +221,37 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { }); } - private ScheduledService> createUpdateService() { - ScheduledService> updateService = new ScheduledService>() { + private ScheduledService> createUpdateService() { + ScheduledService> updateService = new ScheduledService>() { @Override - protected Task> createTask() { - return new Task>() { + protected Task> createTask() { + return new Task>() { @Override - public List call() { + public List call() throws InvalidKeyException, NoSuchAlgorithmException, IllegalStateException, IOException { LOG.trace("Updating recorded models"); - return recorder.getModelsRecording(); + List recordings = recorder.getRecordings(); + List onlineModels = recorder.getOnlineModels(); + return recorder.getModelsRecording() + .stream() + .map(m -> new JavaFxModel(m)) + .peek(fxm -> { + for (Recording recording : recordings) { + if(recording.getStatus() == Recording.STATUS.RECORDING && + recording.getModelName().equals(fxm.getName())) + { + fxm.getRecordingProperty().set(true); + break; + } + } + + for (Model onlineModel : onlineModels) { + if(Objects.equals(onlineModel, fxm)) { + fxm.getOnlineProperty().set(true); + break; + } + } + }) + .collect(Collectors.toList()); } }; }