All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.scattersphere.client.ClientCommandHandler Maven / Gradle / Ivy

/*
 * Scattersphere
 * Copyright 2014-2015, Scattersphere Project.
 *
 * 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 net.scattersphere.client;

import jline.console.ConsoleReader;
import jline.console.completer.StringsCompleter;
import net.scattersphere.api.Client;
import net.scattersphere.client.handler.*;
import net.scattersphere.data.DataSerializer;
import net.scattersphere.data.MD5;
import net.scattersphere.data.message.JobMessage;
import net.scattersphere.data.message.JobResponseMessage;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This is the command handler that is used by the CLI application.  It takes in a command from the {@link ConsoleReader},
 * parses it, and returns a meaningful result to the user.
 *
 * Created by kenji on 11/16/14.
 */
public class ClientCommandHandler {

    private final ConsoleReader reader;
    private final PrintWriter out;

    private final String username;
    private final String password;

    private Client client;
    private List commandHandlers;
    private boolean authAttempted;

    /**
     * Constructor, takes the console reader for commands.
     *
     * @param reader The {@link ConsoleReader} object to read from.
     * @param username The username to assign.
     * @param password The password to assign.
     */
    public ClientCommandHandler(ConsoleReader reader, String username, String password) {
        Objects.requireNonNull(reader);

        this.reader = reader;
        this.out = new PrintWriter(reader.getOutput());
        this.client = new Client();
        this.username = username;
        this.password = password;

        if (username == null && password == null) {
            this.authAttempted = true;
        } else {
            this.authAttempted = false;
        }

        this.commandHandlers = new ArrayList<>();

        reader.addCompleter(new StringsCompleter("connect", "disconnect", "status", "help", "quit", "exit",
                "start", "stop", "list all", "list queued", "list running", "list completed", "list stopped",
                "list failed", "status", "stream status", "stream size", "stream open", "stream close",
                "login"));

        commandHandlers.add(new ListCommandHandler(out, client));
        commandHandlers.add(new StartJobCommandHandler(out, client));
        commandHandlers.add(new QuitCommandHandler(out, client));
        commandHandlers.add(new HelpCommandHandler(out, client));
        commandHandlers.add(new StopJobCommandHandler(out, client));
        commandHandlers.add(new StatusCommandHandler(out, client));
        commandHandlers.add(new StreamCommandHandler(out, client));
        commandHandlers.add(new LoginCommandHandler(out, client));

        client.onConnect(x -> System.err.println("Connected to " + x))
              .onDisconnect(x -> System.err.println("Connection lost/unavailable from " + x))
              .onMessage((name, message) -> handleMessageReceived(message))
              .onAuthentication(this::handleAuth);
    }

    /**
     * This code handles a command that was sent from the command line.  It splits the command by space, sending
     * the first part of the command to a {@link CommandHandler}, but only if it can handle the command specified.
     * If it can handle the command, subsequent commands are sent to it in the form of an argument list, which is
     * an array of {@code String}s.
     *
     * @param command The command to handle.
     */
    public void handle(String command) {
        Objects.requireNonNull(command);

        if (command.length() == 0) {
            return;
        }

        List list = new ArrayList<>();
        Matcher m = Pattern.compile("([^\"]\\S*|\".+?\")\\s*").matcher(command);

        while(m.find()) {
            String val = m.group(1);

            if (val.startsWith("\"")) {
                val = val.substring(1);
            }

            if (val.endsWith("\"")) {
                val = val.substring(0, val.length() - 1);
            }

            list.add(val);
        }

        String commands[] = list.stream().toArray(String[]::new);
        boolean handled = false;

        for(CommandHandler handler : commandHandlers) {
            if (handler.canHandle(commands)) {
                handler.handle(commands);
                handled = true;
            }
        }

        if (!handled) {
            if (commands.length == 1) {
                String checkCommand = commands[0].toLowerCase();

                if (checkCommand.equals("disconnect")) {
                    client.getClientConnection("Main").disconnect();
                    return;
                } else if (checkCommand.equals("connect")) {
                    out.println("Usage: connect [ip:port]\n");
                    return;
                } else if (checkCommand.equals("status")) {
                    out.println(client.getClientConnection("Main").getConnectStatus() + "\n");
                    return;
                }
            } else if (commands.length > 1) {
                String checkCommand = commands[0].toLowerCase();

                switch (commands[0].toLowerCase()) {
                    case "connect":
                        client.addClient("Main", commands[1]);
                        return;
                }
            }

            out.println("Unknown command: " + command + "\n");
        }
    }

    private void handleAuth(boolean auth) {
        if (auth) {
            System.err.println("Authentication successful.");
            return;
        }

        if (authAttempted) {
            System.err.println("Authentication required.");
            return;
        }

        client.authenticate("Main", username, MD5.md5(password));
        authAttempted = true;
    }

    private void handleMessageReceived(JobMessage message) {
        Objects.requireNonNull(message);

        if (message.getMessage().equals(JobMessage.LIST_COMMAND)) {
            if (message.getPayload().length > 0) {
                List jobs = (List) DataSerializer.deserialize(message.getPayload());

                displayJobs(jobs);

                out.flush();
            } else {
                out.println("Unable to retrieve LIST payload.  Empty list?");
            }
        } else if (message.getMessage().equals(JobMessage.START_COMMAND)) {
            if (message.getPayload().length > 0) {
                String responsePayload = (String) DataSerializer.deserialize(message.getPayload());
                String jobId = responsePayload;

                out.println("Job queued: ID=" + jobId);
                out.flush();
            }
        } else if (message.getMessage().equals(JobMessage.STOP_COMMAND)) {
            if (message.getPayload().length > 0) {
                Boolean stopStatus = (Boolean) DataSerializer.deserialize(message.getPayload());

                out.println("Job stopped: result=" + stopStatus);
                out.flush();
            }
        } else if (message.getMessage().equals(JobMessage.STATUS_COMMAND)) {
            if (message.getPayload().length > 0) {
                String jobStatus = (String) DataSerializer.deserialize(message.getPayload());

                out.println("Job status: " + jobStatus);
                out.flush();
            }
        } else if (message.getMessage().equals(JobMessage.STREAM_COMMAND)) {
            if (message.getPayload().length > 0) {
                String streamResponse = (String) DataSerializer.deserialize(message.getPayload());

                out.println("Stream response: " + streamResponse);
                out.flush();
            }
        } else {
            out.println("Unexpected message received: " + message.getMessage());
        }

        out.flush();
    }

    // --- Formatted display functions

    private void displayJobs(List jobs) {
        List availableJobs = new ArrayList<>();
        List otherJobs = new ArrayList<>();

        for(JobResponseMessage job : jobs) {
            if ("AVAILABLE".equals(job.getJobStatus())) {
                availableJobs.add(job);
            } else {
                otherJobs.add(job);
            }
        }

        if (availableJobs.size() > 0) {
            out.println("Available jobs on this server:");

            availableJobs.forEach(x -> out.println(" - '" + x.getJobId() + "' (" + x.getJobDescription() + ")"));
        }

        if (otherJobs.size() > 0) {
            out.println("+--------------------------------------+----------------+");
            out.println("| Job ID                               | Status         |");
            out.println("+--------------------------------------+----------------+");

            otherJobs.forEach(x -> out.println(String.format("| %s | %-14s |", x.getJobId(), x.getJobStatus())));

            out.println("+--------------------------------------+----------------+");
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy