tk.playerforcehd.networklib.bukkit.socket.client.NS_NetClientSocket.java Source code

Java tutorial

Introduction

Here is the source code for tk.playerforcehd.networklib.bukkit.socket.client.NS_NetClientSocket.java

Source

/*
 *     NetworkLib - A Spigot/BungeeCord plugin messaging Library
 *     Copyright (C) 2015 Pascal Zarrad
 *
 *     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 2 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, write to the Free Software Foundation, Inc.,
 *     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package tk.playerforcehd.networklib.bukkit.socket.client;

import com.google.gson.Gson;
import jdk.nashorn.internal.objects.annotations.Getter;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import tk.playerforcehd.networklib.bukkit.Main;
import tk.playerforcehd.networklib.bukkit.api.socket.NetSocket;
import tk.playerforcehd.networklib.bukkit.api.socket.SocketListener;
import tk.playerforcehd.networklib.bukkit.api.socket.client.ClientSocketManager;
import tk.playerforcehd.networklib.shared.socket.Cryptable;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.net.Socket;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author PlayerForceHD
 */
public class NS_NetClientSocket
        implements tk.playerforcehd.networklib.bukkit.api.socket.client.NetClientSocket, NetSocket, Cryptable {

    /**
     * The main instance of NetworkLib
     */
    private Main main;

    /**
     * This instance
     */
    private tk.playerforcehd.networklib.bukkit.api.socket.client.NetClientSocket netClientSocket;

    /**
     * The owner of this socket
     */
    private Plugin ownerPlugin;

    /**
     * The NS_ClientSocketManager of the NetworkLib
     */
    private ClientSocketManager clientSocketManager;

    /**
     * The host to connect to
     */
    private String host;

    /**
     * The port of the host
     */
    private int port;

    /**
     * The socket which will connect
     */
    private Socket socket;

    /**
     * The id of the socket
     */
    private int id;

    /**
     * The listeners of the socket
     */
    private Collection<SocketListener> socketListeners;

    /**
     * If log is true: The opening/closing of the socket will be logged
     */
    private boolean log;

    /**
     * GSon instance to convert object to JSon and back
     */
    private Gson gson;

    /**
     * The PrintWriter to write to the ServerSocket
     */
    private PrintWriter printWriter;

    /**
     * The reader to read incoming lines
     */
    private BufferedReader bufferedReader;

    /**
     * Async reader for the socket input
     */
    private ExecutorService readingService;

    /**
     * Async checker if the connection is still open
     */
    private ExecutorService isAliveCheckerService;

    /**
     * Information about the remote server
     */
    private NS_NetServerSocketInfo NSNetServerSocketInfo;

    /**
     * The key for the Network encryption
     */
    private String encryptionKey;

    /**
     * Enables the connection encryption
     */
    private boolean enableEncryption;

    public NS_NetClientSocket(Main main, Plugin ownerPlugin, ClientSocketManager clientSocketManager, String host,
            int port, int id, boolean log) {
        this.main = main;
        this.netClientSocket = this;
        this.ownerPlugin = ownerPlugin;
        this.clientSocketManager = clientSocketManager;
        this.host = host;
        this.port = port;
        this.socketListeners = new ArrayList<>();
        this.id = id;
        this.log = log;
        this.gson = new Gson();
        this.readingService = Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder()
                .namingPattern("NTLIB-SocketReader-" + this.ownerPlugin.getName() + "-%d").build());
        this.isAliveCheckerService = Executors.newCachedThreadPool(new BasicThreadFactory.Builder()
                .namingPattern("NTLIB-AliveChecker-" + this.ownerPlugin.getName() + "-%d").build());
    }

    /**
     * Opens the socket and starts to listen
     */
    @Override
    public void open() throws IOException {
        if (this.log) {
            this.main.getLogger().info("The plugin " + this.ownerPlugin.getDescription().getName()
                    + " is connecting to a Messaging Server  at: " + this.host + ":" + this.port + "...");
        }
        this.socket = new Socket(this.host, this.port);
        this.printWriter = new PrintWriter(this.socket.getOutputStream(), true);
        this.bufferedReader = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
        this.NSNetServerSocketInfo = new NS_NetServerSocketInfo(this.host, this.port);
        if (this.log) {
            this.main.getLogger().info("Connected to Messaging Server at: " + this.host + ":" + this.port);
        }
        this.isAliveCheckerService.execute(() -> {
            if (!socket.isConnected()) {
                close();
            }
        });
        sendMessageToSocket("serversocketinit:" + Bukkit.getServer().getPort());
        this.readingService.execute(() -> {
            String rdl;
            try {
                while (!this.socket.isClosed() && (rdl = bufferedReader.readLine()) != null) {
                    if (this.enableEncryption) {
                        try {
                            String decryptedString = decrypt(rdl, encryptionKey);
                            callListeners(decryptedString);
                        } catch (InvalidAlgorithmParameterException | InvalidKeyException | NoSuchPaddingException
                                | BadPaddingException | NoSuchAlgorithmException | IllegalBlockSizeException e) {
                            e.printStackTrace();
                        }
                    } else {
                        callListeners(rdl);
                    }
                }
            } catch (IOException e) {
                if (!e.getMessage().equalsIgnoreCase("socket closed")) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Close the NetSocket
     */
    @Override
    public void close() {
        this.clientSocketManager.closeClientSocket(this);
    }

    /**
     * Closes Socket & Streams
     */
    protected void shutdownSocket() {
        try {
            this.main.getLogger().info("Closing connection to Messaging Server at port: " + this.port);
            this.readingService.shutdownNow();
            this.socket.close();
            this.main.getLogger().info("The connection to the Messaging Server at port " + this.port
                    + " from Plugin " + this.ownerPlugin.getDescription().getName() + " has been closed!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Register a new SocketListener to listen to incoming messages (input)
     *
     * @param socketListener The listener to register
     */
    @Override
    public void registerSocketListener(SocketListener socketListener) {
        Validate.notNull(socketListener);
        if (!this.socketListeners.contains(socketListener)) {
            this.socketListeners.add(socketListener);
        }
    }

    /**
     * Unregister a SocketListener from the registered Listeners
     *
     * @param socketListener The listener to unregister
     */
    @Override
    public void unregisterListener(SocketListener socketListener) {
        Validate.notNull(socketListener);
        if (this.socketListeners.contains(socketListener)) {
            this.socketListeners.remove(socketListener);
        }
    }

    /**
     * Calls the SocketListeners with the message
     *
     * @param message The message which has received
     */
    private void callListeners(String message) {
        Validate.notNull(message);
        this.socketListeners.forEach(
                socketListener -> socketListener.received(NSNetServerSocketInfo, netClientSocket, message));
    }

    /**
     * Convert a Object
     *
     * @param toWrap The object to convert
     * @return The JSon string from the object
     */
    @Override
    public String convertObjectToJSON(Object toWrap) {
        Validate.notNull(toWrap);
        return this.gson.toJson(toWrap);
    }

    /**
     * Convert a JSon String to an Object (Object is instance of given class)
     *
     * @param jsonString  The JSon String to convert to an Object
     * @param objectClass The class of the object
     * @return The object of the JSon String
     */
    @Override
    public <O> O convertJSonToObject(String jsonString, Class objectClass) {
        Validate.notNull(jsonString);
        Validate.notNull(objectClass);
        return (O) this.gson.fromJson(jsonString, objectClass);
    }

    /**
     * Print a message to the ServerSocket
     *
     * @param message The message to print
     */
    @Override
    public void sendMessageToSocket(String message) {
        Validate.notNull(message);
        if (this.enableEncryption) {
            try {
                String encryptedMsg = encrypt(message, this.encryptionKey);
                this.printWriter.println(encryptedMsg);
            } catch (NoSuchPaddingException | InvalidKeyException e) {
                e.printStackTrace();
            } catch (BadPaddingException | NoSuchAlgorithmException | IllegalBlockSizeException
                    | UnsupportedEncodingException | InvalidAlgorithmParameterException e) {
            }
        } else {
            this.printWriter.println(message);
        }
    }

    /**
     * Starts to encrypt the connection
     *
     * @param key The key for the encryption (MUST be 128 bit)
     */
    @Override
    public void startConnectionEncryption(String key) throws InvalidKeyException {
        if (key.getBytes().length != 16) {
            throw new InvalidKeyException(
                    "The given key is not a 128 bit key! (Use 16 Characters, 1 char = 1 byte)");
        }
        this.encryptionKey = key;
        this.enableEncryption = true;
    }

    /**
     * Disables the encryption of the connection
     */
    @Override
    public void disableConnectionEncryption() {
        this.enableEncryption = false;
    }

    @Getter
    protected boolean isEnableEncryption() {
        return enableEncryption;
    }
}