/******************************************************************************* * Copyright 2014 Miami-Dade County * * 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 cirmlive; import java.io.BufferedReader; import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Date; import java.util.Map; import java.util.SortedSet; import java.util.TreeMap; import javafx.application.Application; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.stage.Stage; import mjson.Json; import org.sharegov.cirm.stats.CirmClusterStatistics; import org.sharegov.cirm.stats.CirmClusterStatisticsStore; import org.sharegov.cirm.stats.CirmStatistics; import org.sharegov.cirm.stats.CirmStatisticsUtils; import org.sharegov.cirm.stats.CirmServerStatistics; import util.AllowAnySSL; /** * CirmLive loads all statistics data from all configured server and displays a UI to configure a pieChart for it. * Experimental, therefore Quick and dirty.... * * @author Thomas Hilpold */ public class CiRMLive extends Application { public final static String DEFAULT_CONFIG_JSON = "/CiRMLive.json"; public final static String TITLE = "Miami-Dade County CiRM Live ! - Experimental JavaFX Prototype v 2014.07.16 (hilpold)"; private String storeLocation; public final ObservableList<String> serverList = FXCollections.observableArrayList(); public final ObservableList<Date> dateList = FXCollections.observableArrayList(); public final ObservableList<Date> compareTodateList = FXCollections.observableArrayList(); public final ObservableList<String> componentList = FXCollections.observableArrayList(); public final ObservableList<String> actionList = FXCollections.observableArrayList("Created SRs","Updated SRs"); public final ObservableList<String> chartTypeList = FXCollections.observableArrayList("Total", "Success", "Failure"); private CirmClusterStatistics clusterStats; private CirmClusterStatisticsStore clusterStatsStore; //"UpdateAll" "Server" "Date" "Chart" "Total/Success/Failure" private Button updateAllBt = new Button("Load next Stats from all servers"); private Button saveBt = new Button("Save all data to store"); private ComboBox<String> serverCb = new ComboBox<>(serverList); private ComboBox<Date> dateCb = new ComboBox<>(dateList); private ComboBox<Date> compareTodateCb = new ComboBox<>(dateList); private ComboBox<String> componentCb = new ComboBox<>(componentList); private ComboBox<String> actionCb = new ComboBox<>(actionList); private ComboBox<String> charttypeCb = new ComboBox<>(chartTypeList); private CirmPieChart cirmPieChart = new CirmPieChart(); /** * Sets up SSL, ClusterStats and the store - needs to be done before UI. * Exits on failure. */ public void initClusterStats() { AllowAnySSL allow = new AllowAnySSL(); allow.installPermissiveTrustmanager(); try { clusterStats = new CirmClusterStatistics(); updateServerList(); initialize(readConfig()); clusterStatsStore = CirmClusterStatisticsStore.create(new File(storeLocation)); clusterStatsStore.readAllInto(clusterStats); if (serverList.size() > 0) { serverCb.getSelectionModel().select(0); updateDates(); charttypeCb.getSelectionModel().select(0); } } catch (Exception e ) { e.printStackTrace(); System.exit(-1); } } /** * Creates a Borderpane with some selection components and a piechart containing a full UI for working with clusterStats. * Call initclusterstats once before creating the scene. * @return */ public BorderPane createSceneContent() { updateAllBt.pressedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> { if (Boolean.TRUE.equals(newValue)) { updateAllData(); } }); saveBt.pressedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> { if (Boolean.TRUE.equals(newValue)) { saveAllDataToStore(); } }); //Set up scene BorderPane borderPane = new BorderPane(); HBox selectorBox = new HBox(updateAllBt, serverCb, dateCb, componentCb, actionCb, charttypeCb, new Label("Compare to: "), compareTodateCb, saveBt); serverCb.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() { @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { //updateComponents(); updateCurrentPieChart(); } }); dateCb.getSelectionModel().selectedIndexProperty().addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> { updateCurrentPieChart(); }); componentCb.getSelectionModel().selectedIndexProperty().addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> { updateActions(); updateCurrentPieChart(); }); actionCb.getSelectionModel().selectedIndexProperty().addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> { updateCurrentPieChart(); }); charttypeCb.getSelectionModel().selectedIndexProperty().addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> { updateCurrentPieChart(); }); compareTodateCb.getSelectionModel().selectedIndexProperty().addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> { updateCurrentPieChart(); }); borderPane.setTop(selectorBox); borderPane.setCenter(cirmPieChart.getChart()); cirmPieChart.getChart().setCache(true); //cirmPieChart.getChart().setDepthTest(DepthTest.ENABLE); return borderPane; } @Override public void start(Stage primaryStage) { initClusterStats(); setUserAgentStylesheet(STYLESHEET_CASPIAN); //retrieve all data from all servers now and store in history primaryStage.setTitle(TITLE); primaryStage.setWidth(1900); primaryStage.setHeight(1000); primaryStage.setScene(new Scene(createSceneContent())); primaryStage.show(); if (dateList.size() > 0) updateCurrentPieChart(); } /** * Loads all data from all servers, combines it into a cluster stats object * and updates piechart with the cluster stats data. */ public void updateAllData() { clusterStats.updateAll(); updateDates(); } private void updateDates() { SortedSet<Date> availableDates = clusterStats.getClusterDataHistory().getStatsDates(); dateList.clear(); compareTodateList.clear(); availableDates.stream().map((d) -> { dateList.add(0, d); return d; }).forEach((d) -> { compareTodateList.add(0, d); }); if (availableDates.size() > 0) { dateCb.getSelectionModel().select(0); updateComponents(); } } private void updateComponents() { CirmStatistics cl = clusterStats.getLastClusterStatistics(); componentList.clear(); componentList.add(CirmStatistics.ALL); Map<CirmStatistics.StatsKey, CirmStatistics.StatsValue> actions = cl.getAggregatedStatisticsFor(CirmStatistics.EACH, CirmStatistics.ALL, CirmStatistics.ALL); for (Map.Entry<CirmStatistics.StatsKey, CirmStatistics.StatsValue> e : actions.entrySet()) { componentList.add(e.getKey().getComponent()); } if (componentList.size() > 0) componentCb.getSelectionModel().select(0); updateActions(); } private void updateActions() { CirmStatistics cl = clusterStats.getLastClusterStatistics(); actionList.clear(); actionList.add(CirmStatistics.ALL); String component = componentCb.getSelectionModel().getSelectedItem(); if (component != null) { Map<CirmStatistics.StatsKey, CirmStatistics.StatsValue> actions = cl.getAggregatedStatisticsFor(component, CirmStatistics.EACH, CirmStatistics.ALL); for (Map.Entry<CirmStatistics.StatsKey, CirmStatistics.StatsValue> e : actions.entrySet()) { actionList.add(e.getKey().getAction()); } if (actionList.size() > 0) actionCb.getSelectionModel().select(0); } } /** * Updates the serverList dropDown from what servers the clusterstats hold + one for cluster. * (Won't change during runtime) */ public void updateServerList() { int nrOfServers = clusterStats.getNrOfServers(); serverList.clear(); serverList.add("All in Cluster"); for (int i = 0; i < nrOfServers; i++) { serverList.add(clusterStats.getServerDataHistory(i).getServerName()); } } /** * Tells the piechart which data to display based on what the user has selected in the UI. */ public void updateCurrentPieChart() { //Update pie chart based on selection //selected date Date selectedDate = dateCb.getSelectionModel().getSelectedItem(); //selected server int selectedServer = serverCb.getSelectionModel().getSelectedIndex(); //selected chart //int selectedChart = chartCb.getSelectionModel().getSelectedIndex(); String selectedComponent = componentCb.getSelectionModel().getSelectedItem(); String selectedAction = actionCb.getSelectionModel().getSelectedItem(); //selected charttype int selectedChartType = charttypeCb.getSelectionModel().getSelectedIndex(); boolean invalidSelection = (selectedDate == null || selectedServer < 0 || selectedComponent == null || selectedAction == null || selectedChartType < 0); if (invalidSelection){ System.err.println("PLEASE SPECIFIY CHART FULLY, some selection is missing"); return; } CirmServerStatistics serverDataHistory; if (selectedServer == 0) { serverDataHistory = clusterStats.getClusterDataHistory(); } else { serverDataHistory = clusterStats.getServerDataHistory(selectedServer - 1); } CirmStatistics data = serverDataHistory.getStatsAt(selectedDate); //CompareTo? Date selectedCompareDate = compareTodateCb.getSelectionModel().getSelectedItem(); if (selectedCompareDate != null) { CirmStatistics dataFrom = serverDataHistory.getStatsAt(selectedCompareDate); CirmStatistics diff = new CirmStatisticsUtils().calculateDiff(dataFrom, data); data = diff; } String chartTitle = serverList.get(selectedServer) + " " + selectedComponent + " / " + selectedAction + " " + chartTypeList.get(selectedChartType); if (selectedCompareDate == null) { cirmPieChart.setTitle(chartTitle, selectedDate); } else { cirmPieChart.setTitle(chartTitle, selectedCompareDate, selectedDate); } TreeMap<CirmStatistics.StatsKey, CirmStatistics.StatsValue> selectedData = data.getAggregatedStatisticsFor(selectedComponent, selectedAction, CirmStatistics.EACH); cirmPieChart.updateData(selectedData, selectedComponent, selectedAction, CirmPieChart.SHOW_DATA.values()[selectedChartType]); } /** * Saves all data to store, not overwriting existing snapshots. */ private void saveAllDataToStore() { try { clusterStatsStore.writeAllFrom(clusterStats); } catch (Exception e) { e.printStackTrace(); } } /** * Configures CiRMLIve basics based on a json configuration. * * @param configuration */ final void initialize(Json configuration) { if (!configuration.has("storeLocation")) throw new IllegalArgumentException("no storeLocation in " + configuration.toString()); storeLocation = configuration.at("storeLocation").asString(); File f = new File(storeLocation); if (!f.exists()) { f.mkdirs(); System.out.println("Directories " + storeLocation + " was created."); } if (!new File(storeLocation).exists()) throw new IllegalArgumentException("please create store directory: " + storeLocation); } /** * @param args the command line arguments */ public static void main(String[] args) { launch(args); } /** * Reads CiRMLive's config file - most of this should be in a util class. * @return * @throws Exception */ final static synchronized Json readConfig() throws Exception { InputStream is = CiRMLive.class.getResource(DEFAULT_CONFIG_JSON).openStream(); BufferedReader bfr = new BufferedReader(new InputStreamReader(is)); StringBuffer json = new StringBuffer(60); String line; while ((line = bfr.readLine()) != null) { json.append(line + "\r\n"); } bfr.close(); return Json.read(json.toString()); } }