com.exalttech.trex.ui.controllers.daemon.TRexDaemonDialogController.java Source code

Java tutorial

Introduction

Here is the source code for com.exalttech.trex.ui.controllers.daemon.TRexDaemonDialogController.java

Source

/**
 * *****************************************************************************
 * Copyright (c) 2016
 *
 * 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 com.exalttech.trex.ui.controllers.daemon;

import com.exalttech.trex.application.TrexApp;
import com.exalttech.trex.ui.dialog.DialogView;
import com.exalttech.trex.ui.models.datastore.Connection;
import com.exalttech.trex.ui.models.datastore.ConnectionsWrapper;
import com.exalttech.trex.ui.views.logs.LogType;
import com.exalttech.trex.ui.views.logs.LogsView;
import com.exalttech.trex.util.files.XMLFileManager;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.github.arteam.simplejsonrpc.client.JsonRpcClient;
import com.github.arteam.simplejsonrpc.client.Transport;
import com.google.common.base.Charsets;
import com.google.common.net.HttpHeaders;
import com.google.common.net.MediaType;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.*;
import java.util.List;

/**
 * TRex Daemon FXM controller
 *
 * @author Blagov
 */

public class TRexDaemonDialogController extends DialogView implements Initializable {
    static class JsonClientTransport implements Transport {
        private String hostname;
        private String port;
        CloseableHttpClient httpClient;

        public JsonClientTransport(String hostname, String port) {
            this.hostname = hostname;
            this.port = port;
            this.httpClient = HttpClients.createDefault();
        }

        @NotNull
        @Override
        public String pass(@NotNull String request) throws IOException {
            HttpPost post = new HttpPost("http://" + hostname + ":" + port);
            post.setEntity(new StringEntity(request, Charsets.UTF_8));
            post.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString());
            try (CloseableHttpResponse httpResponse = httpClient.execute(post)) {
                return EntityUtils.toString(httpResponse.getEntity(), Charsets.UTF_8);
            }
        }
    }

    @FXML
    private ComboBox<String> hostnamesComboBox;

    @FXML
    private TextField rpcPortTextField;

    @FXML
    private AnchorPane configContainer;

    @FXML
    private ConfigTreeView configTree;

    @FXML
    private LogsView logsView;

    @FXML
    private TitledPane configEditTitledPane;

    @FXML
    private Button connectButton;

    @FXML
    private Button disconnectButton;

    @FXML
    private Button stopTRexButton;

    @FXML
    private Button startTRexButton;

    @FXML
    private Button loadDefaultConfigButton;

    @FXML
    public TextArea yamlPreviewTextfield;

    private JsonRpcClient client;

    private List<MetaField> metadata;
    private Map<String, InterfaceInfo> interfacesInfo;

    private UserConfigModel userConfigModel;
    private String configFilename;

    private List<Control> controlsDisabledOnDisconnected;
    private List<Control> controlsDisabledOnConnected;
    private List<Control> controlsDisabledOnMetadataNotExists;
    private List<Control> controlsDisabledOnConfigInvalid;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        initHostnamesComboBox();
        controlsDisabledOnConnected = Arrays.asList(hostnamesComboBox, rpcPortTextField, connectButton);
        controlsDisabledOnDisconnected = Arrays.asList(disconnectButton, startTRexButton, stopTRexButton);
        controlsDisabledOnMetadataNotExists = Arrays.asList(configEditTitledPane, loadDefaultConfigButton);
        controlsDisabledOnConfigInvalid = Arrays.asList(startTRexButton);
        configEditTitledPane.expandedProperty().bind(configEditTitledPane.disableProperty().not());
        this.configFilename = System.getProperty("user.name");
        refreshControlsAvailability();
    }

    private void initHostnamesComboBox() {
        hostnamesComboBox.getItems().clear();
        ConnectionsWrapper connection = (ConnectionsWrapper) XMLFileManager.loadXML("connections.xml",
                ConnectionsWrapper.class);
        if (connection != null && connection.getConnectionList() != null) {
            hostnamesComboBox.getItems().clear();
            Connection lastUsed = null;
            for (Connection con : connection.getConnectionList()) {
                if (con.isLastUsed()) {
                    lastUsed = con;
                }
                con.setLastUsed(false);
                hostnamesComboBox.getItems().add(con.getIp());
            }
            if (lastUsed != null) {
                hostnamesComboBox.getSelectionModel().select(lastUsed.getIp());
            }
        }
    }

    private void tryToConnect() {
        initJsonRpcClient();
        boolean connected = false;
        try {
            connected = client.createRequest().id(getId()).method("connectivity_check").returnAs(Boolean.class)
                    .execute();
        } catch (RuntimeException ex) {
            ex.printStackTrace();
        }

        if (connected) {
            log(LogType.INFO,
                    MessageFormat.format("Connection to http://{0}:{1} established", getHostname(), getPort()));
            getMetadata();
        } else {
            this.client = null;
            log(LogType.ERROR, MessageFormat.format("Unable to access http://{0}:{1} requested host and port",
                    getHostname(), getPort()));
        }
        refreshControlsAvailability();
    }

    private void refreshControlsAvailability() {
        for (Control control : controlsDisabledOnConnected) {
            control.setDisable(isConnected());
        }

        for (Control control : controlsDisabledOnDisconnected) {
            control.setDisable(!isConnected());
        }

        for (Control control : controlsDisabledOnMetadataNotExists) {
            control.setDisable(this.metadata == null);
        }

        for (Control control : controlsDisabledOnConfigInvalid) {
            if (userConfigModel != null && !userConfigModel.isValid()) {
                control.setDisable(true);
            }
        }
    }

    private boolean isConnected() {
        return client != null;
    }

    private void disconnect() {
        if (isConnected()) {
            client = null;
            metadata = null;

            interfacesInfo = null;
            TrexApp.injector.getInstance(InterfaceInfoProvider.class).setInterfacesInfo(null);

            configContainer.getChildren().clear();
            log(LogType.INFO, MessageFormat.format("Disconnected from http://{0}:{1}", getHostname(), getPort()));
            refreshControlsAvailability();
        }
    }

    private void loadDefaultConfig() {
        try {
            String defaultConfigB64 = client.createRequest().id(getId()).method("get_trex_config")
                    .returnAs(String.class).execute();

            String decoded = new String(Base64.getDecoder().decode(defaultConfigB64.trim()), Charsets.US_ASCII);
            userConfigModel.fromYAMLString(decoded);
            initConfigTree();
            updateYAML();
        } catch (RuntimeException ex) {
            log(LogType.ERROR, MessageFormat.format("Unable to get default config from TRex Daemon: {0}", ex));
        } catch (IOException ex) {
            log(LogType.ERROR, MessageFormat.format("Unable to parse received default config YAML: {0}", ex));
        }
    }

    private void initJsonRpcClient() {
        this.client = new JsonRpcClient(new JsonClientTransport(getHostname(), getPort()));
    }

    private String getPort() {
        return rpcPortTextField.getText();
    }

    private String getHostname() {
        return hostnamesComboBox.getSelectionModel().getSelectedItem();
    }

    private void getMetadata() {
        try {
            metadata = client.createRequest().id(getId()).method("get_trex_config_metadata")
                    .returnAs(new TypeReference<List<MetaField>>() {
                    }).execute();
        } catch (RuntimeException ex) {
            ex.printStackTrace();
            log(LogType.ERROR, "Unable to get TRex config Metadata, custom config usage will not be available: "
                    + ex.getMessage());
            return;
        }

        try {
            interfacesInfo = client.createRequest().id(getId()).method("get_devices_info")
                    .returnAs(new TypeReference<Map<String, InterfaceInfo>>() {
                    }).execute();
            TrexApp.injector.getInstance(InterfaceInfoProvider.class).setInterfacesInfo(interfacesInfo);
        } catch (Exception ex) {
            ex.printStackTrace();
            log(LogType.ERROR, "Unable to get TRex devices info, interface selection dialog will not be available: "
                    + ex.getMessage());
        }

        initUserConfigModel();
        initConfigTree();
        updateYAML();
    }

    private void startTRex() {
        Boolean configUploaded = false;
        try {
            configUploaded = client.createRequest().id(getId()).method("push_file")
                    .param("filename", configFilename)
                    .param("bin_data",
                            Base64.getEncoder()
                                    .encodeToString(userConfigModel.getYAMLString().getBytes(Charsets.US_ASCII)))
                    .returnAs(Boolean.class).execute();

            if (!configUploaded) {
                log(LogType.ERROR, "Config upload to TRex host failed (TRex Daemon IO error)");
            }

            log(LogType.INFO, "Config uploaded successfully");
        } catch (RuntimeException ex) {
            log(LogType.ERROR, MessageFormat.format("Config upload to TRex host failed: {0}", ex));
        } catch (JsonProcessingException ex) {
            log(LogType.ERROR, MessageFormat.format("Config serialization failed: {0}", ex));
        }

        Map<String, Object> cmdParams = new HashMap<>();

        if (configUploaded) {
            try {
                String files_path = client.createRequest().id(getId()).method("get_files_path")
                        .returnAs(String.class).execute();

                cmdParams.put("cfg", Paths.get(files_path, configFilename));
            } catch (RuntimeException ex) {
                log(LogType.ERROR, MessageFormat.format("Unable to get user configs path: {0}", ex));
            }
        }

        try {
            Integer trexSessionHandler = client.createRequest().id(getId()).method("start_trex")
                    .param("trex_cmd_options", cmdParams).param("user", System.getProperty("user.name"))
                    .param("stateless", true).returnAs(Integer.class).param("timeout", 15).execute();
            log(LogType.INFO, "TRex started successfully");
        } catch (RuntimeException ex) {
            log(LogType.ERROR, MessageFormat.format("Unable to start TRex: {0} ", ex));
        }
    }

    private void stopTRex() {
        try {
            Boolean stopped = client.createRequest().id(getId()).method("force_trex_kill").returnAs(Boolean.class)
                    .execute();
            if (stopped) {
                log(LogType.INFO, "TRex stopped successfully");
            } else {
                log(LogType.INFO, "TRex is not running");
            }
        } catch (RuntimeException ex) {
            log(LogType.ERROR, MessageFormat.format("Unable to stop TRex: {0}", ex));
        }
    }

    private void initUserConfigModel() {
        userConfigModel = new UserConfigModel(metadata);
    }

    private void initConfigTree() {
        configContainer.getChildren().clear();

        if (userConfigModel != null) {
            configTree = new ConfigTreeView(userConfigModel, this::updateYAML);
            configContainer.getChildren().add(configTree);
        }
    }

    private void updateYAML() {
        try {
            yamlPreviewTextfield.setText(userConfigModel.getYAMLString());
            refreshControlsAvailability();
        } catch (JsonProcessingException e) {
            log(LogType.ERROR, MessageFormat.format("Unable to get YAML conents: {0}", e));
        }
    }

    private String getId() {
        String ALPHA_NUMERIC_STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < 8; i++) {
            int character = (int) (Math.random() * ALPHA_NUMERIC_STRING.length());
            builder.append(ALPHA_NUMERIC_STRING.charAt(character));
        }
        return builder.toString();
    }

    private void log(LogType type, String message) {
        logsView.append(type, message);
    }

    public void handleConnectClicked(ActionEvent actionEvent) {
        tryToConnect();
    }

    public void handleDisconnectClicked(ActionEvent actionEvent) {
        disconnect();
    }

    public void handleLoadDefaultConfigClicked(ActionEvent actionEvent) {
        loadDefaultConfig();
    }

    public void handleStartClicked(ActionEvent actionEvent) {
        startTRex();
    }

    public void handleStopClicked(ActionEvent actionEvent) {
        stopTRex();
    }

    @Override
    public void onEnterKeyPressed(Stage stage) {
        //Do nothing
    }

    @Override
    public void closeHandler() {
        disconnect();
    }
}