mamo.vanillaVotifier.VotifierServer.java Source code

Java tutorial

Introduction

Here is the source code for mamo.vanillaVotifier.VotifierServer.java

Source

/*
 * Copyright (C) 2016  Matteo Morena
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package mamo.vanillaVotifier;

import mamo.vanillaVotifier.event.*;
import mamo.vanillaVotifier.utils.RsaUtils;
import mamo.vanillaVotifier.utils.SubstitutionUtils;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.crypto.BadPaddingException;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketOptions;
import java.net.SocketTimeoutException;
import java.security.interfaces.RSAPublicKey;
import java.util.AbstractMap.SimpleEntry;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.PatternSyntaxException;

public class VotifierServer {
    @NotNull
    protected VanillaVotifier votifier;
    @NotNull
    protected CopyOnWriteArrayList<Listener> listeners;
    protected boolean running;
    @Nullable
    protected ServerSocket serverSocket;

    public VotifierServer(@NotNull VanillaVotifier votifier) {
        this.votifier = votifier;
        listeners = new CopyOnWriteArrayList<Listener>();
        listeners.add(new VotifierServerListener(votifier));
    }

    public synchronized void start() throws IOException {
        if (isRunning()) {
            throw new IllegalStateException("Server is already running!");
        }
        notifyListeners(new ServerStartingEvent());
        serverSocket = new ServerSocket();
        serverSocket.bind(votifier.getConfig().getInetSocketAddress());
        running = true;
        notifyListeners(new ServerStartedEvent());
        new Thread(new Runnable() {
            @Override
            public void run() {
                ExecutorService executorService = Executors.newSingleThreadExecutor();
                while (isRunning()) {
                    try {
                        final Socket socket = serverSocket.accept();
                        executorService.execute(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    notifyListeners(new ConnectionEstablishedEvent(socket));
                                    socket.setSoTimeout(SocketOptions.SO_TIMEOUT); // SocketException: handled by try/catch.
                                    BufferedWriter writer = new BufferedWriter(
                                            new OutputStreamWriter(socket.getOutputStream()));
                                    writer.write("VOTIFIER 2.9\n");
                                    writer.flush();
                                    BufferedInputStream in = new BufferedInputStream(socket.getInputStream()); // IOException: handled by try/catch.
                                    byte[] request = new byte[((RSAPublicKey) votifier.getConfig().getKeyPair()
                                            .getPublic()).getModulus().bitLength() / Byte.SIZE];
                                    in.read(request); // IOException: handled by try/catch.
                                    notifyListeners(new EncryptedInputReceivedEvent(socket, new String(request)));
                                    request = RsaUtils
                                            .getDecryptCipher(votifier.getConfig().getKeyPair().getPrivate())
                                            .doFinal(request); // IllegalBlockSizeException: can't happen.
                                    String requestString = new String(request);
                                    notifyListeners(new DecryptedInputReceivedEvent(socket, requestString));
                                    String[] requestArray = requestString.split("\n");
                                    if ((requestArray.length == 5 || requestArray.length == 6)
                                            && requestArray[0].equals("VOTE")) {
                                        notifyListeners(new VoteEventVotifier(socket, new Vote(requestArray[1],
                                                requestArray[2], requestArray[3], requestArray[4])));
                                        for (VoteAction voteAction : votifier.getConfig().getVoteActions()) {
                                            String[] params = new String[4];
                                            try {
                                                for (int i = 0; i < params.length; i++) {
                                                    params[i] = SubstitutionUtils.applyRegexReplacements(
                                                            requestArray[i + 1], voteAction.getRegexReplacements());
                                                }
                                            } catch (PatternSyntaxException e) {
                                                notifyListeners(new RegularExpressionPatternErrorException(e));
                                                params = new String[] { requestArray[1], requestArray[2],
                                                        requestArray[3], requestArray[4] };
                                            }
                                            if (voteAction.getCommandSender() instanceof RconCommandSender) {
                                                RconCommandSender commandSender = (RconCommandSender) voteAction
                                                        .getCommandSender();
                                                StrSubstitutor substitutor = SubstitutionUtils.buildStrSubstitutor(
                                                        new SimpleEntry<String, Object>("service-name", params[0]),
                                                        new SimpleEntry<String, Object>("user-name", params[1]),
                                                        new SimpleEntry<String, Object>("address", params[2]),
                                                        new SimpleEntry<String, Object>("timestamp", params[3]));
                                                for (String command : voteAction.getCommands()) {
                                                    String theCommand = substitutor.replace(command);
                                                    notifyListeners(new SendingRconCommandEvent(
                                                            commandSender.getRconConnection(), theCommand));
                                                    try {
                                                        notifyListeners(new RconCommandResponseEvent(
                                                                commandSender.getRconConnection(), commandSender
                                                                        .sendCommand(theCommand).getPayload()));
                                                    } catch (Exception e) {
                                                        notifyListeners(new RconExceptionEvent(
                                                                commandSender.getRconConnection(), e));
                                                    }
                                                }
                                            }
                                            if (voteAction.getCommandSender() instanceof ShellCommandSender) {
                                                ShellCommandSender commandSender = (ShellCommandSender) voteAction
                                                        .getCommandSender();
                                                HashMap<String, String> environment = new HashMap<String, String>();
                                                environment.put("voteServiceName", params[0]);
                                                environment.put("voteUserName", params[1]);
                                                environment.put("voteAddress", params[2]);
                                                environment.put("voteTimestamp", params[3]);
                                                for (String command : voteAction.getCommands()) {
                                                    notifyListeners(new SendingShellCommandEvent(command));
                                                    try {
                                                        commandSender.sendCommand(command, environment);
                                                        notifyListeners(new ShellCommandSentEvent());
                                                    } catch (Exception e) {
                                                        notifyListeners(new ShellCommandExceptionEvent(e));
                                                    }
                                                }
                                            }
                                        }
                                    } else {
                                        notifyListeners(new InvalidRequestEvent(socket, requestString));
                                    }
                                } catch (SocketTimeoutException e) {
                                    notifyListeners(new ReadTimedOutExceptionEvent(socket, e));
                                } catch (BadPaddingException e) {
                                    notifyListeners(new DecryptInputExceptionEvent(socket, e));
                                } catch (Exception e) {
                                    notifyListeners(new CommunicationExceptionEvent(socket, e));
                                }
                                try {
                                    socket.close();
                                    notifyListeners(new ConnectionClosedEvent(socket));
                                } catch (Exception e) { // IOException: catching just in case. Continue even if socket doesn't close.
                                    notifyListeners(new ConnectionCloseExceptionEvent(socket, e));
                                }
                            }
                        });
                    } catch (Exception e) {
                        if (running) { // Show errors only while running, to hide error while stopping.
                            notifyListeners(new ConnectionEstablishExceptionEvent(e));
                        }
                    }
                }
                executorService.shutdown();
                if (!executorService.isTerminated()) {
                    notifyListeners(new ServerAwaitingTaskCompletionEvent());
                    try {
                        executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
                    } catch (Exception e) {
                        // InterruptedException: can't happen.
                    }
                }
                notifyListeners(new ServerStoppedEvent());
            }
        }).start();
    }

    public synchronized void stop() throws IOException {
        if (!isRunning()) {
            throw new IllegalStateException("Server isn't running!");
        }
        notifyListeners(new ServerStoppingEvent());
        running = false;
        serverSocket.close();
    }

    public synchronized boolean isRunning() {
        return running;
    }

    @NotNull
    public List<Listener> getListeners() {
        return listeners;
    }

    public void notifyListeners(@NotNull Event event) {
        for (Listener listener : listeners) {
            listener.onEvent(event);
        }
    }
}