/*******************************************************************************
* 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());
}
}