Java tutorial
/* * 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; } }