From fcc605be3a6015aabb756c50484db13ffd2ea3c0 Mon Sep 17 00:00:00 2001 From: OHopiak Date: Sun, 7 Jan 2018 17:46:37 +0000 Subject: [PATCH 01/37] checking a new branch --- src/main/java/com/fastchat/FastChat/server/Server.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/fastchat/FastChat/server/Server.java b/src/main/java/com/fastchat/FastChat/server/Server.java index 345c004..536257e 100644 --- a/src/main/java/com/fastchat/FastChat/server/Server.java +++ b/src/main/java/com/fastchat/FastChat/server/Server.java @@ -40,7 +40,7 @@ public void run() { manageClients(); receive(); Scanner in = new Scanner(System.in); - System.out.println("Type /help to get some information"); + System.out.println("Type /help to get more information"); while (running) { System.out.print("> "); String text; From 799791a6c6a9d9d216049c1fcbbad3cb4e35d9e5 Mon Sep 17 00:00:00 2001 From: OHopiak Date: Mon, 8 Jan 2018 02:17:41 +0200 Subject: [PATCH 02/37] Add locale to the project and started making a new GUI --- build.gradle | 11 +- .../java/com/fastchat/FastChat/Loader.java | 11 +- .../com/fastchat/FastChat/client/Client.java | 56 +++--- .../fastchat/FastChat/client/ClientGUI.form | 48 ++++++ .../fastchat/FastChat/client/ClientGUI.java | 161 ++++++++++++++++++ .../FastChat/client/ClientInterface.java | 53 ++++++ .../FastChat/client/ClientWindow.java | 93 ++++------ .../com/fastchat/FastChat/client/Login.java | 52 +++--- .../fastchat/FastChat/util/Localization.java | 53 ++++++ .../fastchat/FastChat/util/UTF8Control.java | 43 +++++ src/main/resources/default.properties | 5 + src/main/resources/ukrainian.properties | 6 + 12 files changed, 466 insertions(+), 126 deletions(-) create mode 100644 src/main/java/com/fastchat/FastChat/client/ClientGUI.form create mode 100644 src/main/java/com/fastchat/FastChat/client/ClientGUI.java create mode 100644 src/main/java/com/fastchat/FastChat/client/ClientInterface.java create mode 100644 src/main/java/com/fastchat/FastChat/util/Localization.java create mode 100644 src/main/java/com/fastchat/FastChat/util/UTF8Control.java create mode 100644 src/main/resources/default.properties create mode 100644 src/main/resources/ukrainian.properties diff --git a/build.gradle b/build.gradle index a891b9d..8e17f6e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'java' -apply plugin: 'maven' group = 'com.fastchat' version = '0.0.1' @@ -26,17 +25,17 @@ test { } repositories { - maven { url "http://jcenter.bintray.com" } - maven { url "http://repo.maven.apache.org/maven2" } + jcenter() } dependencies { - testCompile group: 'org.testng', name: 'testng', version: '6.9.12' + compile 'com.intellij:forms_rt:7.0.3' + testCompile 'org.testng:testng:6.9.12' } -task printJarName{ +task printJarName { dependsOn 'jar' doLast { println "${project.name}-${project.version}.jar" } -} \ No newline at end of file +} diff --git a/src/main/java/com/fastchat/FastChat/Loader.java b/src/main/java/com/fastchat/FastChat/Loader.java index 057852b..2b372ed 100644 --- a/src/main/java/com/fastchat/FastChat/Loader.java +++ b/src/main/java/com/fastchat/FastChat/Loader.java @@ -1,7 +1,8 @@ package com.fastchat.FastChat; -import com.fastchat.FastChat.client.Login; +import com.fastchat.FastChat.client.ClientGUI; import com.fastchat.FastChat.server.Server; +import com.fastchat.FastChat.util.Localization; public class Loader { @@ -9,6 +10,8 @@ public class Loader { private static final String description = "This is really cool tool for chatting\nrun without arguments to launch the client"; +// private static Class clientClass; + private static void usage() { System.out.println(usageString); } @@ -25,8 +28,12 @@ private static void help() { } public static void main(String[] args) { + + Localization.autoSetLocale(); +// Localization.setLocale(new Locale("uk")); + if (args.length == 0) { - Login.main(args); + ClientGUI gui = ClientGUI.getInstance(); } else if (args.length <= 3) { if (args[0].equals("-h") || args[0].equals("--help")) { help(); diff --git a/src/main/java/com/fastchat/FastChat/client/Client.java b/src/main/java/com/fastchat/FastChat/client/Client.java index 89ac57f..92af931 100644 --- a/src/main/java/com/fastchat/FastChat/client/Client.java +++ b/src/main/java/com/fastchat/FastChat/client/Client.java @@ -1,16 +1,11 @@ package com.fastchat.FastChat.client; +import javax.swing.*; import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.SocketException; -import java.net.UnknownHostException; - -import javax.swing.JOptionPane; +import java.net.*; public class Client { - + private String name; private int port; private int ID; @@ -18,53 +13,50 @@ public class Client { private InetAddress ip; private Thread send; private boolean running, kicked = false, banned = false; - + protected boolean openConnection(String address) { try { socket = new DatagramSocket(); ip = InetAddress.getByName(address); - } catch (UnknownHostException e) { - e.printStackTrace(); - return false; - } catch (SocketException e) { + } catch (UnknownHostException | SocketException e) { e.printStackTrace(); return false; } return true; } - + protected String receive() { byte[] data = new byte[1024]; DatagramPacket packet = new DatagramPacket(data, data.length); try { socket.receive(packet); - } catch (SocketException e1) {} catch (IOException e) { + } catch (IOException e) { e.printStackTrace(); } String message = new String(packet.getData()); message = message.split("/e/")[0]; return message; } - - protected void send(final byte[] data) { + + private void send(final byte[] data) { send = new Thread("Send") { public void run() { DatagramPacket packet = new DatagramPacket(data, data.length, ip, port); try { socket.send(packet); - } catch (SocketException e1) {} catch (IOException e) { + } catch (IOException e) { e.printStackTrace(); } } }; send.start(); } - + protected void send(String message) { message += "/e/"; send(message.getBytes()); } - + protected void disconnect(int id) { running = false; //socket.close(); @@ -82,53 +74,53 @@ protected void disconnect(int id) { System.exit(0); } } - + public String getName() { return name; } - + public void setID(int iD) { ID = iD; } - + public int getID() { return ID; } - + public int getPort() { return port; } - + public InetAddress getIp() { return ip; } - + public Client(String name, int port) { super(); this.name = name; this.port = port; } - + public boolean isRunning() { return running; } - + public void setRunning(boolean running) { this.running = running; } - + public boolean isKicked() { return kicked; } - + public void setKicked(boolean kicked) { this.kicked = kicked; } - + public boolean isBanned() { return banned; } - + public void setBanned(boolean banned) { this.banned = banned; } diff --git a/src/main/java/com/fastchat/FastChat/client/ClientGUI.form b/src/main/java/com/fastchat/FastChat/client/ClientGUI.form new file mode 100644 index 0000000..189dc30 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/client/ClientGUI.form @@ -0,0 +1,48 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java new file mode 100644 index 0000000..4389749 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java @@ -0,0 +1,161 @@ +package com.fastchat.FastChat.client; + +import com.fastchat.FastChat.util.Localization; + +import javax.swing.*; +import javax.swing.text.DefaultCaret; +import java.awt.*; +import java.awt.event.ActionListener; + +/** + * The basic GUI class, + * has a generated part that can't be edited + */ +public class ClientGUI extends ClientInterface { + private final static int WIDTH = 400; + private final static int HEIGHT = 600; + private final static String TITLE = Localization.get("window_title"); + private static ClientGUI clientGUI; + private JFrame frame; + private JTextField messagePrompt; + private JButton sendButton; + private JScrollPane messagePanel; + private JPanel clientPanel; + private JTextArea history; + + { +// GUI initializer generated by IntelliJ IDEA GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + + /** + * + */ + private ClientGUI() { + init(); + frame.setVisible(true); + } + + /** + * @return the singleton instance + */ + public static ClientGUI getInstance() { + if (clientGUI == null) + clientGUI = new ClientGUI(); + return clientGUI; + } + + /** + * @return generated MenuBar + */ + private static JMenuBar generateMenuBar() { + JMenuBar menuBar = new JMenuBar(); + + JMenu menu = new JMenu(Localization.get("menu_bar_menu")); + menuBar.add(menu); + JMenuItem onlineUsers = new JMenuItem(Localization.get("menu_bar_online_users")); + onlineUsers.addActionListener(event -> { +// client.send("/u/"); +// users.updateList(userArr); +// users.setVisible(true); + }); + menu.add(onlineUsers); + + JMenuItem settings = new JMenuItem(Localization.get("menu_bar_settings")); + settings.addActionListener(event -> { + + }); + menu.add(settings); + + return menuBar; + } + + /** + * + */ + private void init() { + ActionListener messageListener = actionEvent -> send(messagePrompt.getText()); + messagePrompt.addActionListener(messageListener); + sendButton.addActionListener(messageListener); + sendButton.setText(Localization.get("send_btn_text")); + ((DefaultCaret) history.getCaret()).setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); + + frame = new JFrame(TITLE); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + frame.setJMenuBar(generateMenuBar()); + + frame.setContentPane(this.clientPanel); + frame.pack(); + + frame.setSize(WIDTH, HEIGHT); + frame.setLocationRelativeTo(null); + } + + @Override + protected void send(String message) { + if (message.equals("")) return; +// message = client.getName() + ": " + message; + messagePrompt.setText(""); +// client.send("/m/" + message); + console(message); + } + + @Override + void console(String message) { + history.setCaretPosition(history.getDocument().getLength()); + history.append(message + "\r\n"); + } + + /** + * Method generated by IntelliJ IDEA GUI Designer + * >>> IMPORTANT!! <<< + * DO NOT edit this method OR call it in your code! + * + * @noinspection ALL + */ + private void $$$setupUI$$$() { + clientPanel = new JPanel(); + clientPanel.setLayout(new GridBagLayout()); + clientPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6), null)); + messagePanel = new JScrollPane(); + messagePanel.setBackground(new Color(-1)); + GridBagConstraints gbc; + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridwidth = 2; + gbc.weightx = 1.0; + gbc.weighty = 10.0; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(0, 0, 4, 0); + clientPanel.add(messagePanel, gbc); + history = new JTextArea(); + history.setEditable(false); + messagePanel.setViewportView(history); + messagePrompt = new JTextField(); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.weightx = 1.0; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + clientPanel.add(messagePrompt, gbc); + sendButton = new JButton(); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 1; + gbc.anchor = GridBagConstraints.EAST; + gbc.insets = new Insets(0, 3, 0, 0); + clientPanel.add(sendButton, gbc); + } + + /** + * @noinspection ALL + */ + public JComponent $$$getRootComponent$$$() { + return clientPanel; + } +} diff --git a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java new file mode 100644 index 0000000..7d0480e --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java @@ -0,0 +1,53 @@ +package com.fastchat.FastChat.client; + +public abstract class ClientInterface implements Runnable { + + protected Client client; + private Thread run, listen; + private OnlineUsers users; + + private String[] userArr = {"Name(id)"}; + + protected abstract void send(String message); + + abstract void console(String message); + + public void run() { + listen(); + } + + private void listen() { + listen = new Thread("Listen") { + public void run() { + while (client.isRunning()) { + String message = client.receive(); + if (message.startsWith("/c/")) { + client.setID(Integer.parseInt(message.substring(3))); + console("Connection is setted up successfully... ID is " + client.getID()); + } else if (message.startsWith("/m/")) { + console(message.substring(3)); + } else if (message.startsWith("/i/")) { + client.send("/i/" + client.getID()); + } else if (message.startsWith("/d/")) { + message = message.substring(3); + if (message.startsWith("0")) { + client.setKicked(true); + client.disconnect(client.getID()); + } else { + client.setBanned(true); + client.disconnect(client.getID()); + } + } else if (message.startsWith("/u/")) { + message = message.substring(3); + userArr = message.split("//"); + users.updateList(userArr); + } else { + console("Server: " + message); + } + } + } + }; + listen.start(); + } + +} diff --git a/src/main/java/com/fastchat/FastChat/client/ClientWindow.java b/src/main/java/com/fastchat/FastChat/client/ClientWindow.java index 9e2196e..c2d0191 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientWindow.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientWindow.java @@ -1,47 +1,34 @@ package com.fastchat.FastChat.client; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; - -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; -import javax.swing.JTextField; +import javax.swing.*; import javax.swing.border.EmptyBorder; import javax.swing.text.DefaultCaret; +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; public class ClientWindow extends JFrame implements Runnable { - + private static final long serialVersionUID = 5015815262080715846L; - + private final static int WIDTH = 800; private final static int HEIGHT = 600; - + private JPanel contentPane; private JTextField txtMessage; private JTextArea history; private DefaultCaret caret; - + private Thread run, listen; private Client client; private JMenuBar menuBar; private JMenu menu; private JMenuItem onlineUsers; private OnlineUsers users; - - private String[] userArr = { "Name(id)" }; - - public ClientWindow(String name, String address, int port) { + + private String[] userArr = {"Name(id)"}; + + ClientWindow(String name, String address, int port) { client = new Client(name, port); client.setRunning(true); createWindow(); @@ -57,28 +44,28 @@ public ClientWindow(String name, String address, int port) { } client.send(("/c/" + name)); } - + private void console(String message) { history.setCaretPosition(history.getDocument().getLength()); history.append(message + "\r\n"); } - + private void send(String message) { if (message.equals("")) return; message = client.getName() + ": " + message; txtMessage.setText(""); client.send("/m/" + message); } - + public void run() { listen(); } - + private void listen() { listen = new Thread("Listen") { public void run() { while (client.isRunning()) { - String message = client.receive(); + String message = client.receive(); if (message.startsWith("/c/")) { client.setID(Integer.parseInt(message.substring(3))); console("Connection is setted up successfully... ID is " + client.getID()); @@ -107,40 +94,38 @@ public void run() { }; listen.start(); } - + private void createWindow() { setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(WIDTH, HEIGHT); setLocationRelativeTo(null); setVisible(true); - + menuBar = new JMenuBar(); setJMenuBar(menuBar); - + menu = new JMenu("Menu"); menuBar.add(menu); - + users = new OnlineUsers(); users.updateList(userArr); onlineUsers = new JMenuItem("Online Users"); - onlineUsers.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent arg0) { - client.send("/u/"); - users.updateList(userArr); - users.setVisible(true); - } + onlineUsers.addActionListener(arg0 -> { + client.send("/u/"); + users.updateList(userArr); + users.setVisible(true); }); menu.add(onlineUsers); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); GridBagLayout gbl_contentPane = new GridBagLayout(); - gbl_contentPane.columnWidths = new int[] { 5, WIDTH - 30, 20, 0 }; - gbl_contentPane.rowHeights = new int[] { 5, HEIGHT - 30, 20, 5 }; - gbl_contentPane.columnWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE }; - gbl_contentPane.rowWeights = new double[] { 0.0, 1.0, 0.0, Double.MIN_VALUE }; + gbl_contentPane.columnWidths = new int[]{5, WIDTH - 30, 20, 0}; + gbl_contentPane.rowHeights = new int[]{5, HEIGHT - 30, 20, 5}; + gbl_contentPane.columnWeights = new double[]{0.0, 1.0, Double.MIN_VALUE}; + gbl_contentPane.rowWeights = new double[]{0.0, 1.0, 0.0, Double.MIN_VALUE}; contentPane.setLayout(gbl_contentPane); - + JScrollPane scrollPane = new JScrollPane(); GridBagConstraints gbc_scrollPane = new GridBagConstraints(); gbc_scrollPane.insets = new Insets(0, 0, 5, 5); @@ -149,19 +134,15 @@ public void actionPerformed(ActionEvent arg0) { gbc_scrollPane.gridy = 1; gbc_scrollPane.gridwidth = 2; contentPane.add(scrollPane, gbc_scrollPane); - + history = new JTextArea(); history.setEditable(false); caret = (DefaultCaret) history.getCaret(); caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); scrollPane.setViewportView(history); - + txtMessage = new JTextField(); - txtMessage.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - send(txtMessage.getText()); - } - }); + txtMessage.addActionListener(e -> send(txtMessage.getText())); GridBagConstraints gbc_textField = new GridBagConstraints(); gbc_textField.insets = new Insets(0, 0, 0, 5); gbc_textField.fill = GridBagConstraints.HORIZONTAL; @@ -170,13 +151,9 @@ public void actionPerformed(ActionEvent e) { contentPane.add(txtMessage, gbc_textField); txtMessage.setColumns(10); txtMessage.requestFocusInWindow(); - + JButton btnSend = new JButton("Send"); - btnSend.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - send(txtMessage.getText()); - } - }); + btnSend.addActionListener(e -> send(txtMessage.getText())); GridBagConstraints gbc_btnSend = new GridBagConstraints(); gbc_btnSend.insets = new Insets(0, 0, 0, 5); gbc_btnSend.gridx = 2; diff --git a/src/main/java/com/fastchat/FastChat/client/Login.java b/src/main/java/com/fastchat/FastChat/client/Login.java index 1b8f410..010dc3b 100644 --- a/src/main/java/com/fastchat/FastChat/client/Login.java +++ b/src/main/java/com/fastchat/FastChat/client/Login.java @@ -7,30 +7,30 @@ import java.awt.event.ActionListener; public class Login extends JFrame { - + private static final long serialVersionUID = -6824082102990197571L; - + private static Login currentLogin; private static ClientWindow clientWindow; - + private JPanel contentPane; private JTextField txtName; private JTextField txtAddress; private JTextField txtPort; - - public Login() { + + private Login() { setResizable(false); - + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(205, 295); setLocationRelativeTo(null); - + setVisible(true); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); contentPane.setLayout(null); - + txtName = new JTextField(); txtName.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { @@ -42,7 +42,7 @@ public void actionPerformed(ActionEvent e) { contentPane.add(txtName); txtName.setColumns(10); txtName.requestFocusInWindow(); // requesting focus - + txtAddress = new JTextField(); txtAddress.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { @@ -53,7 +53,7 @@ public void actionPerformed(ActionEvent e) { txtAddress.setColumns(10); txtAddress.setBounds(25, 90, 140, 20); contentPane.add(txtAddress); - + txtPort = new JTextField(); txtPort.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { @@ -64,7 +64,7 @@ public void actionPerformed(ActionEvent arg0) { txtPort.setColumns(10); txtPort.setBounds(25, 154, 140, 20); contentPane.add(txtPort); - + final JButton btnSubmit = new JButton("Submit"); btnSubmit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { @@ -73,17 +73,17 @@ public void actionPerformed(ActionEvent arg0) { }); btnSubmit.setBounds(53, 216, 89, 23); contentPane.add(btnSubmit); - + JLabel lblName = new JLabel("Name:"); lblName.setHorizontalAlignment(SwingConstants.CENTER); lblName.setBounds(72, 11, 46, 14); contentPane.add(lblName); - + JLabel lblAddress = new JLabel("Address:"); lblAddress.setHorizontalAlignment(SwingConstants.CENTER); lblAddress.setBounds(72, 72, 46, 14); contentPane.add(lblAddress); - + JLabel lblPort = new JLabel("Port:"); lblPort.setHorizontalAlignment(SwingConstants.CENTER); lblPort.setBounds(72, 135, 46, 14); @@ -92,7 +92,7 @@ public void actionPerformed(ActionEvent arg0) { txtAddress.setText("localhost"); txtPort.setText("1234"); } - + private void submit() { String name = txtName.getText(); String address = txtAddress.getText(); @@ -100,38 +100,34 @@ private void submit() { clientWindow = new ClientWindow(name, address, port); this.setVisible(false); } - + public void reset() { this.setVisible(true); txtAddress.setText(""); txtPort.setText(""); txtAddress.requestFocusInWindow(); } - + public static void main(String[] args) { - EventQueue.invokeLater(() -> { - try { - currentLogin = new Login(); - } catch (Exception e) { - e.printStackTrace(); - } - }); + EventQueue.invokeLater(Login::getCurrentLogin); } public static Login getCurrentLogin() { + if (currentLogin == null) + currentLogin = new Login(); return currentLogin; } - + public static void setCurrentLogin(Login login) { currentLogin = login; } - + public static ClientWindow getClientWindow() { return clientWindow; } - + public static void setClientWindow(ClientWindow clientWindow) { Login.clientWindow = clientWindow; } - + } diff --git a/src/main/java/com/fastchat/FastChat/util/Localization.java b/src/main/java/com/fastchat/FastChat/util/Localization.java new file mode 100644 index 0000000..9736b6c --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/util/Localization.java @@ -0,0 +1,53 @@ +package com.fastchat.FastChat.util; + +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class Localization { + + private static final ResourceBundle DEFAULT_BUNDLE = ResourceBundle.getBundle("default", new UTF8Control()); + private static String bundleName = "default"; + private static ResourceBundle bundle = ResourceBundle.getBundle(bundleName, new UTF8Control()); + + public static String get(String key) { + String result = ""; + try { + result = bundle.getString(key); + } catch (MissingResourceException ex) { + try { + result = DEFAULT_BUNDLE.getString(key); + } catch (MissingResourceException ignore) { + } + } + return result; + } + + private static void setBundle(String bundleNewName) { + if (!bundleNewName.equals(bundleName)) { + + if (Localization.class.getResource(String.format("/%s.properties", bundleNewName)) != null) { + bundleName = bundleNewName; + bundle = ResourceBundle.getBundle(bundleName, new UTF8Control()); + } else { + bundleName = "default"; + bundle = DEFAULT_BUNDLE; + } + } + } + + public static void autoSetLocale() { + String language = System.getProperty("user.language"); + setLocale(new Locale(language)); + } + + public static void setLocale(Locale locale) { + switch (locale.getLanguage()) { + case "uk": + setBundle("ukrainian"); + break; + default: + setBundle("default"); + } + } +} diff --git a/src/main/java/com/fastchat/FastChat/util/UTF8Control.java b/src/main/java/com/fastchat/FastChat/util/UTF8Control.java new file mode 100644 index 0000000..55a3f63 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/util/UTF8Control.java @@ -0,0 +1,43 @@ +package com.fastchat.FastChat.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.util.Locale; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; + +public class UTF8Control extends ResourceBundle.Control { + public ResourceBundle newBundle(String baseName, Locale locale, String format, + ClassLoader loader, boolean reload) throws IOException { + + // The below is a copy of the default implementation. + String bundleName = toBundleName(baseName, locale); + String resourceName = toResourceName(bundleName, "properties"); + ResourceBundle bundle = null; + InputStream stream = null; + if (reload) { + URL url = loader.getResource(resourceName); + if (url != null) { + URLConnection connection = url.openConnection(); + if (connection != null) { + connection.setUseCaches(false); + stream = connection.getInputStream(); + } + } + } else { + stream = loader.getResourceAsStream(resourceName); + } + if (stream != null) { + try { + // Only this line is changed to make it to read properties files as UTF-8. + bundle = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8")); + } finally { + stream.close(); + } + } + return bundle; + } +} \ No newline at end of file diff --git a/src/main/resources/default.properties b/src/main/resources/default.properties new file mode 100644 index 0000000..5c19937 --- /dev/null +++ b/src/main/resources/default.properties @@ -0,0 +1,5 @@ +send_btn_text=Send +window_title=FastChat +menu_bar_menu=Menu +menu_bar_online_users=Online Users +menu_bar_settings=Settings diff --git a/src/main/resources/ukrainian.properties b/src/main/resources/ukrainian.properties new file mode 100644 index 0000000..1f23971 --- /dev/null +++ b/src/main/resources/ukrainian.properties @@ -0,0 +1,6 @@ +send_btn_text=Надіслати +window_title=FastChat (ШвидкоЧат) +menu_bar_menu=Меню +menu_bar_online_users=Користувачі онлайн +menu_bar_settings=Налаштування + From 153d5b95d6750271166bbc3b8118617b1161b5e7 Mon Sep 17 00:00:00 2001 From: OHopiak Date: Mon, 8 Jan 2018 01:53:30 +0000 Subject: [PATCH 03/37] added java coverage support --- build.gradle | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/build.gradle b/build.gradle index 8e17f6e..85fd4b2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'java' +apply plugin: 'jacoco' group = 'com.fastchat' version = '0.0.1' @@ -39,3 +40,11 @@ task printJarName { println "${project.name}-${project.version}.jar" } } + +jacocoTestReport{ + reports { + html.enabled true + xml.enabled true + csv.enabled true + } +} From 8cd0974ca9f6f03e01afb7f77b683ae8078d1ca2 Mon Sep 17 00:00:00 2001 From: Orest Hopiak Date: Mon, 8 Jan 2018 04:12:08 +0200 Subject: [PATCH 04/37] Added coverage support --- Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index c5e0b67..82ccfe9 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,8 +8,9 @@ pipeline { } stage('test') { steps { - sh 'gradle test' + sh 'gradle check jacoco' junit(testResults: '**/build/test-results/**/*.xml', allowEmptyResults: true) + jacoco(execPattern: '**/build/**.exec') } } stage('deploy') { From 34413f849eea3c2f8faf604ce4a29385a609238d Mon Sep 17 00:00:00 2001 From: OHopiak Date: Mon, 8 Jan 2018 02:53:24 +0000 Subject: [PATCH 05/37] updated travis config --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 45e9d6b..370207e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,3 +2,6 @@ language: java jdk: - oraclejdk8 + +after_success: + - bash <(curl -s https://codecov.io/bash) From e52a02a3c449eb89a968e2f866a0225f6d82abf9 Mon Sep 17 00:00:00 2001 From: OHopiak Date: Mon, 8 Jan 2018 02:59:37 +0000 Subject: [PATCH 06/37] updated travis config --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 370207e..444b90a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: java +cache: gradle + jdk: - oraclejdk8 From 362ca9eafa4799877ce71a430314dafff41fd71f Mon Sep 17 00:00:00 2001 From: OHopiak Date: Tue, 9 Jan 2018 22:49:08 +0200 Subject: [PATCH 07/37] Add locale to the project and started making a new GUI --- FastChat.uml | 85 +++------ build.gradle | 11 +- .../java/com/fastchat/FastChat/Loader.java | 17 +- .../com/fastchat/FastChat/client/Client.java | 4 +- .../fastchat/FastChat/client/ClientGUI.java | 80 +++++++-- .../FastChat/client/ClientInterface.java | 84 ++++++++- .../FastChat/client/ClientWindow.java | 169 ------------------ .../com/fastchat/FastChat/client/Login.java | 133 -------------- .../fastchat/FastChat/client/LoginGUI.form | 154 ++++++++++++++++ .../fastchat/FastChat/client/LoginGUI.java | 139 ++++++++++++++ .../FastChat/client/LoginInterface.java | 11 ++ .../fastchat/FastChat/client/OnlineUsers.java | 10 +- .../com/fastchat/FastChat/server/Server.java | 8 +- .../fastchat/FastChat/util/Localization.java | 3 + src/main/resources/default.properties | 6 + src/main/resources/norwegian.properties | 6 + src/main/resources/ukrainian.properties | 7 +- 17 files changed, 526 insertions(+), 401 deletions(-) delete mode 100644 src/main/java/com/fastchat/FastChat/client/ClientWindow.java delete mode 100644 src/main/java/com/fastchat/FastChat/client/Login.java create mode 100644 src/main/java/com/fastchat/FastChat/client/LoginGUI.form create mode 100644 src/main/java/com/fastchat/FastChat/client/LoginGUI.java create mode 100644 src/main/java/com/fastchat/FastChat/client/LoginInterface.java create mode 100644 src/main/resources/norwegian.properties diff --git a/FastChat.uml b/FastChat.uml index b793435..ce7ca48 100644 --- a/FastChat.uml +++ b/FastChat.uml @@ -3,78 +3,37 @@ JAVA - com.fastchat.FastChat.Loader - com.fastchat.FastChat.client.Login - com.fastchat.FastChat.client.Client - com.fastchat.FastChat.server.Server.Status - com.fastchat.FastChat.server.Server - com.fastchat.FastChat.client.OnlineUsers - com.fastchat.FastChat.server.ServerClient - com.fastchat.FastChat.client.ClientWindow + com.fastchat.FastChat.Loader + com.fastchat.FastChat.client.ClientGUI + com.fastchat.FastChat.client.LoginInterface + com.fastchat.FastChat.client.LoginGUI + com.fastchat.FastChat.client.Client + com.fastchat.FastChat.server.Server.Status + com.fastchat.FastChat.server.Server + com.fastchat.FastChat.client.OnlineUsers + com.fastchat.FastChat.server.ServerClient + com.fastchat.FastChat.client.ClientInterface - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - + + - - + + + com.fastchat.FastChat.Loader + - Methods Inner Classes All diff --git a/build.gradle b/build.gradle index 8e17f6e..9d810ef 100644 --- a/build.gradle +++ b/build.gradle @@ -8,6 +8,10 @@ description = """FastChat - a tool for instant chatting that not always works as sourceCompatibility = 1.8 targetCompatibility = 1.8 +configurations { + libraries +} + jar { manifest { attributes( @@ -17,6 +21,9 @@ jar { ) } + from { + configurations.libraries.collect { it.isDirectory() ? it : zipTree(it) } + } } test { @@ -29,8 +36,10 @@ repositories { } dependencies { - compile 'com.intellij:forms_rt:7.0.3' + libraries 'com.jgoodies:jgoodies-forms:1.6.0' +// libraries 'com.intellij:forms_rt:7.0.3' testCompile 'org.testng:testng:6.9.12' + configurations.compile.extendsFrom(configurations.libraries) } task printJarName { diff --git a/src/main/java/com/fastchat/FastChat/Loader.java b/src/main/java/com/fastchat/FastChat/Loader.java index 2b372ed..88366d5 100644 --- a/src/main/java/com/fastchat/FastChat/Loader.java +++ b/src/main/java/com/fastchat/FastChat/Loader.java @@ -1,9 +1,12 @@ package com.fastchat.FastChat; -import com.fastchat.FastChat.client.ClientGUI; +import com.fastchat.FastChat.client.LoginGUI; +import com.fastchat.FastChat.client.LoginInterface; import com.fastchat.FastChat.server.Server; import com.fastchat.FastChat.util.Localization; +import java.awt.*; + public class Loader { private static final String usageString = "Usage: java -jar FastChat.jar [ -s [PORT] [IP] ]"; @@ -29,11 +32,17 @@ private static void help() { public static void main(String[] args) { - Localization.autoSetLocale(); -// Localization.setLocale(new Locale("uk")); + Localization.autoSetLocale(); //English +// Localization.setLocale(new Locale("uk")); //Ukrainian +// Localization.setLocale(new Locale("nb")); //Norwegian if (args.length == 0) { - ClientGUI gui = ClientGUI.getInstance(); +// EventQueue.invokeLater(Login::getCurrentLogin); + EventQueue.invokeLater(() -> { + System.out.println("[*] Starting Login"); + LoginInterface login = new LoginGUI(); + + }); } else if (args.length <= 3) { if (args[0].equals("-h") || args[0].equals("--help")) { help(); diff --git a/src/main/java/com/fastchat/FastChat/client/Client.java b/src/main/java/com/fastchat/FastChat/client/Client.java index 92af931..91aba5e 100644 --- a/src/main/java/com/fastchat/FastChat/client/Client.java +++ b/src/main/java/com/fastchat/FastChat/client/Client.java @@ -61,8 +61,8 @@ protected void disconnect(int id) { running = false; //socket.close(); if (kicked || banned) { - Login.getCurrentLogin().setVisible(true); - Login.getClientWindow().setVisible(false); +// ((LoginGUI)LoginGUI.getCurrentLogin()).reset(); +// ((ClientGUI)LoginGUI.getClientInterface()).getFrame().setVisible(false); JOptionPane.showMessageDialog(null, (kicked ? "You were kicked out of server\nTry to reconnect again" : "You were banned on this server!\nYou can't reconnect unless the server restarts!"), diff --git a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java index 4389749..c73ca7a 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java @@ -6,6 +6,8 @@ import javax.swing.text.DefaultCaret; import java.awt.*; import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; /** * The basic GUI class, @@ -16,12 +18,6 @@ public class ClientGUI extends ClientInterface { private final static int HEIGHT = 600; private final static String TITLE = Localization.get("window_title"); private static ClientGUI clientGUI; - private JFrame frame; - private JTextField messagePrompt; - private JButton sendButton; - private JScrollPane messagePanel; - private JPanel clientPanel; - private JTextArea history; { // GUI initializer generated by IntelliJ IDEA GUI Designer @@ -30,16 +26,27 @@ public class ClientGUI extends ClientInterface { $$$setupUI$$$(); } + private JFrame frame; + private JTextField messagePrompt; + private JButton sendButton; + private JScrollPane messagePanel; + private JPanel clientPanel; + private JTextArea history; + + /** * */ private ClientGUI() { - init(); - frame.setVisible(true); + super(); + } + + private ClientGUI(String name, String address, int port) { + super(name, address, port); } /** - * @return the singleton instance + * @return The singleton instance */ public static ClientGUI getInstance() { if (clientGUI == null) @@ -47,6 +54,22 @@ public static ClientGUI getInstance() { return clientGUI; } + /** + * @param name The username + * @param address The url (not tested) or an IP + * @param port The port to connect to + * @return The singleton instance + */ + public static ClientGUI getInstance(String name, String address, int port) { + if (clientGUI == null + || clientGUI.getName().equals(name) + || clientGUI.getAddress().equals(address) + || clientGUI.getPort() == port + ) + clientGUI = new ClientGUI(name, address, port); + return clientGUI; + } + /** * @return generated MenuBar */ @@ -72,17 +95,25 @@ private static JMenuBar generateMenuBar() { return menuBar; } + public JFrame getFrame() { + return frame; + } + /** * */ - private void init() { + @Override + public void init() { ActionListener messageListener = actionEvent -> send(messagePrompt.getText()); messagePrompt.addActionListener(messageListener); sendButton.addActionListener(messageListener); + sendButton.setText(Localization.get("send_btn_text")); + ((DefaultCaret) history.getCaret()).setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); - frame = new JFrame(TITLE); + if (frame == null) + frame = new JFrame(TITLE); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setJMenuBar(generateMenuBar()); @@ -92,21 +123,36 @@ private void init() { frame.setSize(WIDTH, HEIGHT); frame.setLocationRelativeTo(null); + + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + client.disconnect(client.getID()); + } + }); + + super.init(); + + frame.setVisible(true); + } + + @Override + void console(String message) { + history.setCaretPosition(history.getDocument().getLength()); + history.append(message + "\r\n"); } @Override protected void send(String message) { if (message.equals("")) return; -// message = client.getName() + ": " + message; + message = client.getName() + ": " + message; messagePrompt.setText(""); -// client.send("/m/" + message); - console(message); + client.send("/m/" + message); } @Override - void console(String message) { - history.setCaretPosition(history.getDocument().getLength()); - history.append(message + "\r\n"); + protected void onBreak() { + history.setBackground(new Color(55, 55, 55)); + messagePrompt.setEditable(false); } /** diff --git a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java index 7d0480e..104acc2 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java @@ -1,13 +1,51 @@ package com.fastchat.FastChat.client; +import com.fastchat.FastChat.util.Localization; + public abstract class ClientInterface implements Runnable { - protected Client client; + Client client; private Thread run, listen; private OnlineUsers users; + private String name, address; + private int port; + private String[] userArr = {"Name(id)"}; + ClientInterface() { + this("Anon", "localhost", 1234); + } + + ClientInterface(String name, String address, int port) { + this.name = name; + this.address = address; + this.port = port; + +// init(); + } + + protected abstract void onBreak(); + + protected void init() { + client = new Client(name, port); + client.setRunning(true); + + console(String.format("%s %s:%d...", Localization.get("client_attempting_to_connect"), address, port)); + + boolean connected = client.openConnection(address); + + if (!connected) { + String client_connection_failed = Localization.get("client_connection_failed"); + System.err.println(client_connection_failed); + console(client_connection_failed); + onBreak(); + } else { + this.serve(); + } + client.send(("/c/" + name)); + } + protected abstract void send(String message); abstract void console(String message); @@ -50,4 +88,48 @@ public void run() { listen.start(); } + private void serve() { + run = new Thread(this, "Run"); + run.start(); + } + + public Thread getRun() { + return run; + } + + public Thread getListen() { + return listen; + } + + public OnlineUsers getUsers() { + return users; + } + + public String[] getUserArr() { + return userArr; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } } diff --git a/src/main/java/com/fastchat/FastChat/client/ClientWindow.java b/src/main/java/com/fastchat/FastChat/client/ClientWindow.java deleted file mode 100644 index c2d0191..0000000 --- a/src/main/java/com/fastchat/FastChat/client/ClientWindow.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.fastchat.FastChat.client; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import javax.swing.text.DefaultCaret; -import java.awt.*; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; - -public class ClientWindow extends JFrame implements Runnable { - - private static final long serialVersionUID = 5015815262080715846L; - - private final static int WIDTH = 800; - private final static int HEIGHT = 600; - - private JPanel contentPane; - private JTextField txtMessage; - private JTextArea history; - private DefaultCaret caret; - - private Thread run, listen; - private Client client; - private JMenuBar menuBar; - private JMenu menu; - private JMenuItem onlineUsers; - private OnlineUsers users; - - private String[] userArr = {"Name(id)"}; - - ClientWindow(String name, String address, int port) { - client = new Client(name, port); - client.setRunning(true); - createWindow(); - console("Attempting to conect to " + address + ":" + port + "..."); - boolean connected = client.openConnection(address); - if (!connected) { - System.err.println("Connection failed!"); - console("Connection failed!"); - txtMessage.setEditable(false); - } else { - run = new Thread(this, "Run"); - run.start(); - } - client.send(("/c/" + name)); - } - - private void console(String message) { - history.setCaretPosition(history.getDocument().getLength()); - history.append(message + "\r\n"); - } - - private void send(String message) { - if (message.equals("")) return; - message = client.getName() + ": " + message; - txtMessage.setText(""); - client.send("/m/" + message); - } - - public void run() { - listen(); - } - - private void listen() { - listen = new Thread("Listen") { - public void run() { - while (client.isRunning()) { - String message = client.receive(); - if (message.startsWith("/c/")) { - client.setID(Integer.parseInt(message.substring(3))); - console("Connection is setted up successfully... ID is " + client.getID()); - } else if (message.startsWith("/m/")) { - console(message.substring(3)); - } else if (message.startsWith("/i/")) { - client.send("/i/" + client.getID()); - } else if (message.startsWith("/d/")) { - message = message.substring(3); - if (message.startsWith("0")) { - client.setKicked(true); - client.disconnect(client.getID()); - } else { - client.setBanned(true); - client.disconnect(client.getID()); - } - } else if (message.startsWith("/u/")) { - message = message.substring(3); - userArr = message.split("//"); - users.updateList(userArr); - } else { - console("Server: " + message); - } - } - } - }; - listen.start(); - } - - private void createWindow() { - setDefaultCloseOperation(EXIT_ON_CLOSE); - setSize(WIDTH, HEIGHT); - setLocationRelativeTo(null); - setVisible(true); - - menuBar = new JMenuBar(); - setJMenuBar(menuBar); - - menu = new JMenu("Menu"); - menuBar.add(menu); - - users = new OnlineUsers(); - users.updateList(userArr); - onlineUsers = new JMenuItem("Online Users"); - onlineUsers.addActionListener(arg0 -> { - client.send("/u/"); - users.updateList(userArr); - users.setVisible(true); - }); - menu.add(onlineUsers); - contentPane = new JPanel(); - contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); - setContentPane(contentPane); - GridBagLayout gbl_contentPane = new GridBagLayout(); - gbl_contentPane.columnWidths = new int[]{5, WIDTH - 30, 20, 0}; - gbl_contentPane.rowHeights = new int[]{5, HEIGHT - 30, 20, 5}; - gbl_contentPane.columnWeights = new double[]{0.0, 1.0, Double.MIN_VALUE}; - gbl_contentPane.rowWeights = new double[]{0.0, 1.0, 0.0, Double.MIN_VALUE}; - contentPane.setLayout(gbl_contentPane); - - JScrollPane scrollPane = new JScrollPane(); - GridBagConstraints gbc_scrollPane = new GridBagConstraints(); - gbc_scrollPane.insets = new Insets(0, 0, 5, 5); - gbc_scrollPane.fill = GridBagConstraints.BOTH; - gbc_scrollPane.gridx = 1; - gbc_scrollPane.gridy = 1; - gbc_scrollPane.gridwidth = 2; - contentPane.add(scrollPane, gbc_scrollPane); - - history = new JTextArea(); - history.setEditable(false); - caret = (DefaultCaret) history.getCaret(); - caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); - scrollPane.setViewportView(history); - - txtMessage = new JTextField(); - txtMessage.addActionListener(e -> send(txtMessage.getText())); - GridBagConstraints gbc_textField = new GridBagConstraints(); - gbc_textField.insets = new Insets(0, 0, 0, 5); - gbc_textField.fill = GridBagConstraints.HORIZONTAL; - gbc_textField.gridx = 1; - gbc_textField.gridy = 2; - contentPane.add(txtMessage, gbc_textField); - txtMessage.setColumns(10); - txtMessage.requestFocusInWindow(); - - JButton btnSend = new JButton("Send"); - btnSend.addActionListener(e -> send(txtMessage.getText())); - GridBagConstraints gbc_btnSend = new GridBagConstraints(); - gbc_btnSend.insets = new Insets(0, 0, 0, 5); - gbc_btnSend.gridx = 2; - gbc_btnSend.gridy = 2; - contentPane.add(btnSend, gbc_btnSend); - addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - client.disconnect(client.getID()); - } - }); - setTitle("FastChat Client. User: " + client.getName()); - } -} diff --git a/src/main/java/com/fastchat/FastChat/client/Login.java b/src/main/java/com/fastchat/FastChat/client/Login.java deleted file mode 100644 index 010dc3b..0000000 --- a/src/main/java/com/fastchat/FastChat/client/Login.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.fastchat.FastChat.client; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -public class Login extends JFrame { - - private static final long serialVersionUID = -6824082102990197571L; - - private static Login currentLogin; - private static ClientWindow clientWindow; - - private JPanel contentPane; - private JTextField txtName; - private JTextField txtAddress; - private JTextField txtPort; - - private Login() { - setResizable(false); - - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - setSize(205, 295); - setLocationRelativeTo(null); - - setVisible(true); - contentPane = new JPanel(); - contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); - setContentPane(contentPane); - contentPane.setLayout(null); - - txtName = new JTextField(); - txtName.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - txtAddress.requestFocusInWindow(); - } - }); - txtName.setToolTipText("Name"); - txtName.setBounds(25, 30, 140, 20); - contentPane.add(txtName); - txtName.setColumns(10); - txtName.requestFocusInWindow(); // requesting focus - - txtAddress = new JTextField(); - txtAddress.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - txtPort.requestFocusInWindow(); - } - }); - txtAddress.setToolTipText("Address"); - txtAddress.setColumns(10); - txtAddress.setBounds(25, 90, 140, 20); - contentPane.add(txtAddress); - - txtPort = new JTextField(); - txtPort.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent arg0) { - submit(); - } - }); - txtPort.setToolTipText("Port"); - txtPort.setColumns(10); - txtPort.setBounds(25, 154, 140, 20); - contentPane.add(txtPort); - - final JButton btnSubmit = new JButton("Submit"); - btnSubmit.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent arg0) { - submit(); - } - }); - btnSubmit.setBounds(53, 216, 89, 23); - contentPane.add(btnSubmit); - - JLabel lblName = new JLabel("Name:"); - lblName.setHorizontalAlignment(SwingConstants.CENTER); - lblName.setBounds(72, 11, 46, 14); - contentPane.add(lblName); - - JLabel lblAddress = new JLabel("Address:"); - lblAddress.setHorizontalAlignment(SwingConstants.CENTER); - lblAddress.setBounds(72, 72, 46, 14); - contentPane.add(lblAddress); - - JLabel lblPort = new JLabel("Port:"); - lblPort.setHorizontalAlignment(SwingConstants.CENTER); - lblPort.setBounds(72, 135, 46, 14); - contentPane.add(lblPort); - txtName.setText("Orest"); - txtAddress.setText("localhost"); - txtPort.setText("1234"); - } - - private void submit() { - String name = txtName.getText(); - String address = txtAddress.getText(); - int port = Integer.parseInt(txtPort.getText()); - clientWindow = new ClientWindow(name, address, port); - this.setVisible(false); - } - - public void reset() { - this.setVisible(true); - txtAddress.setText(""); - txtPort.setText(""); - txtAddress.requestFocusInWindow(); - } - - public static void main(String[] args) { - EventQueue.invokeLater(Login::getCurrentLogin); - } - - public static Login getCurrentLogin() { - if (currentLogin == null) - currentLogin = new Login(); - return currentLogin; - } - - public static void setCurrentLogin(Login login) { - currentLogin = login; - } - - public static ClientWindow getClientWindow() { - return clientWindow; - } - - public static void setClientWindow(ClientWindow clientWindow) { - Login.clientWindow = clientWindow; - } - -} diff --git a/src/main/java/com/fastchat/FastChat/client/LoginGUI.form b/src/main/java/com/fastchat/FastChat/client/LoginGUI.form new file mode 100644 index 0000000..ba692a4 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/client/LoginGUI.form @@ -0,0 +1,154 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/com/fastchat/FastChat/client/LoginGUI.java b/src/main/java/com/fastchat/FastChat/client/LoginGUI.java new file mode 100644 index 0000000..82f05e2 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/client/LoginGUI.java @@ -0,0 +1,139 @@ +package com.fastchat.FastChat.client; + +import com.fastchat.FastChat.util.Localization; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; + +import javax.swing.*; +import java.awt.event.ActionListener; + +public class LoginGUI extends LoginInterface { + + private static final String title = "Login"; + + private JFrame frame; + private JPanel loginPanel; + private JLabel lblName; + private JTextField txtName; + private JTextField txtAddress; + private JTextField txtPort; + private JButton submitButton; + private JLabel lblAddress; + private JLabel lblPort; + + { +// GUI initializer generated by IntelliJ IDEA GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + + public LoginGUI() { + init(); + } + + private void init() { + frame = new JFrame(title); + frame.setContentPane(loginPanel); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.pack(); + frame.setSize(205, 295); + frame.setResizable(false); + frame.setLocationRelativeTo(null); + + txtName.addActionListener(e -> txtAddress.requestFocusInWindow()); + txtAddress.addActionListener(e -> txtPort.requestFocusInWindow()); + ActionListener submitListener = e -> { + String name = txtName.getText(); + String address = txtAddress.getText(); + int port = Integer.parseInt(txtPort.getText()); + submit(name, address, port); + frame.setVisible(false); + }; + txtPort.addActionListener(submitListener); + submitButton.addActionListener(submitListener); + + lblName.setText(Localization.get("login_lbl_name") + ":"); + lblAddress.setText(Localization.get("login_lbl_address") + ":"); + lblPort.setText(Localization.get("login_lbl_port") + ":"); + submitButton.setText(Localization.get("login_submit")); + + txtName.setText("Anon"); + txtAddress.setText("localhost"); + txtPort.setText("1234"); + + frame.setVisible(true); + } + + @Override + protected void submit(String name, String address, int port) { + clientInterface = ClientGUI.getInstance(name, address, port); + clientInterface.init(); + } + + public void reset() { + frame.setVisible(true); +// txtAddress.setText("localhost"); +// txtPort.setText("1234"); + txtAddress.requestFocusInWindow(); + } + + /** + * Method generated by IntelliJ IDEA GUI Designer + * >>> IMPORTANT!! <<< + * DO NOT edit this method OR call it in your code! + * + * @noinspection ALL + */ + private void $$$setupUI$$$() { + loginPanel = new JPanel(); + loginPanel.setLayout(new FormLayout("fill:d:grow", "center:d:grow,top:4dlu:noGrow,center:d:noGrow,top:4dlu:noGrow,center:max(d;4px):noGrow,top:4dlu:noGrow,center:d:grow,top:4dlu:noGrow,center:max(d;4px):noGrow,top:4dlu:noGrow,center:max(d;4px):noGrow,top:4dlu:noGrow,center:d:grow,top:4dlu:noGrow,center:max(d;4px):noGrow,top:4dlu:noGrow,center:max(d;4px):noGrow,top:4dlu:noGrow,center:d:grow,top:4dlu:noGrow,center:max(d;4px):noGrow,top:4dlu:noGrow,center:d:grow")); + loginPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5), null)); + lblName = new JLabel(); + lblName.setText("Name:"); + CellConstraints cc = new CellConstraints(); + loginPanel.add(lblName, cc.xy(1, 3, CellConstraints.CENTER, CellConstraints.DEFAULT)); + txtName = new JTextField(); + txtName.setColumns(15); + txtName.setText(""); + loginPanel.add(txtName, cc.xy(1, 5, CellConstraints.CENTER, CellConstraints.DEFAULT)); + lblAddress = new JLabel(); + lblAddress.setText("Address:"); + loginPanel.add(lblAddress, cc.xy(1, 9, CellConstraints.CENTER, CellConstraints.DEFAULT)); + txtAddress = new JTextField(); + txtAddress.setColumns(15); + loginPanel.add(txtAddress, cc.xy(1, 11, CellConstraints.CENTER, CellConstraints.DEFAULT)); + lblPort = new JLabel(); + lblPort.setText("Port:"); + loginPanel.add(lblPort, cc.xy(1, 15, CellConstraints.CENTER, CellConstraints.DEFAULT)); + txtPort = new JTextField(); + txtPort.setColumns(15); + loginPanel.add(txtPort, cc.xy(1, 17, CellConstraints.CENTER, CellConstraints.DEFAULT)); + submitButton = new JButton(); + submitButton.setText("Submit"); + loginPanel.add(submitButton, cc.xy(1, 21, CellConstraints.CENTER, CellConstraints.DEFAULT)); + final JSeparator separator1 = new JSeparator(); + separator1.setEnabled(true); + separator1.setVisible(false); + loginPanel.add(separator1, cc.xy(1, 1, CellConstraints.FILL, CellConstraints.FILL)); + final JSeparator separator2 = new JSeparator(); + separator2.setVisible(false); + loginPanel.add(separator2, cc.xy(1, 7, CellConstraints.FILL, CellConstraints.FILL)); + final JSeparator separator3 = new JSeparator(); + separator3.setVisible(false); + loginPanel.add(separator3, cc.xy(1, 13, CellConstraints.FILL, CellConstraints.FILL)); + final JSeparator separator4 = new JSeparator(); + separator4.setVisible(false); + loginPanel.add(separator4, cc.xy(1, 19, CellConstraints.FILL, CellConstraints.FILL)); + final JSeparator separator5 = new JSeparator(); + separator5.setVisible(false); + loginPanel.add(separator5, cc.xy(1, 23, CellConstraints.FILL, CellConstraints.FILL)); + } + + /** + * @noinspection ALL + */ + public JComponent $$$getRootComponent$$$() { + return loginPanel; + } +} diff --git a/src/main/java/com/fastchat/FastChat/client/LoginInterface.java b/src/main/java/com/fastchat/FastChat/client/LoginInterface.java new file mode 100644 index 0000000..85dbf66 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/client/LoginInterface.java @@ -0,0 +1,11 @@ +package com.fastchat.FastChat.client; + +public abstract class LoginInterface { + ClientInterface clientInterface; + + protected abstract void submit(String name, String address, int port); + + public ClientInterface getClientInterface() { + return clientInterface; + } +} diff --git a/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java b/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java index bdc8090..ea7277f 100644 --- a/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java +++ b/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java @@ -1,12 +1,10 @@ package com.fastchat.FastChat.client; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; +import com.fastchat.FastChat.util.Localization; -import javax.swing.JFrame; -import javax.swing.JList; -import javax.swing.JPanel; +import javax.swing.*; import javax.swing.border.EmptyBorder; +import java.awt.*; public class OnlineUsers extends JFrame { @@ -19,7 +17,7 @@ public OnlineUsers() { setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setSize(278, 409); setLocationRelativeTo(null); - setTitle("Users Online"); + setTitle(Localization.get("menu_bar_online_users")); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); diff --git a/src/main/java/com/fastchat/FastChat/server/Server.java b/src/main/java/com/fastchat/FastChat/server/Server.java index 536257e..e6d718f 100644 --- a/src/main/java/com/fastchat/FastChat/server/Server.java +++ b/src/main/java/com/fastchat/FastChat/server/Server.java @@ -22,6 +22,10 @@ public class Server implements Runnable { @SuppressWarnings("FieldCanBeLocal") private Thread run, manage, send, receive; + private enum Status { + DISCONNECTED, TIMED_OUT, KICKED, BANNED + } + public Server(int port, String address) { System.out.printf("Server started at %s:%d\n", address, port); this.port = port; @@ -271,8 +275,4 @@ private void disconnectClient(int id, Status status) { public int getPort() { return port; } - - private enum Status { - DISCONNECTED, TIMED_OUT, KICKED, BANNED - } } diff --git a/src/main/java/com/fastchat/FastChat/util/Localization.java b/src/main/java/com/fastchat/FastChat/util/Localization.java index 9736b6c..99b91a2 100644 --- a/src/main/java/com/fastchat/FastChat/util/Localization.java +++ b/src/main/java/com/fastchat/FastChat/util/Localization.java @@ -46,6 +46,9 @@ public static void setLocale(Locale locale) { case "uk": setBundle("ukrainian"); break; + case "nb": + setBundle("norwegian"); + break; default: setBundle("default"); } diff --git a/src/main/resources/default.properties b/src/main/resources/default.properties index 5c19937..335c963 100644 --- a/src/main/resources/default.properties +++ b/src/main/resources/default.properties @@ -3,3 +3,9 @@ window_title=FastChat menu_bar_menu=Menu menu_bar_online_users=Online Users menu_bar_settings=Settings +client_attempting_to_connect=Attempting to conect to +client_connection_failed=Connection failed! +login_lbl_name=Name +login_lbl_address=Address (IP) +login_lbl_port=Port +login_submit=Submit diff --git a/src/main/resources/norwegian.properties b/src/main/resources/norwegian.properties new file mode 100644 index 0000000..7b031fe --- /dev/null +++ b/src/main/resources/norwegian.properties @@ -0,0 +1,6 @@ +#send_btn_text=Send +window_title=FastChat (RaskChat) +menu_bar_menu=Meny +#menu_bar_online_users=Online Users +menu_bar_settings=Instillinger + diff --git a/src/main/resources/ukrainian.properties b/src/main/resources/ukrainian.properties index 1f23971..2a8d8aa 100644 --- a/src/main/resources/ukrainian.properties +++ b/src/main/resources/ukrainian.properties @@ -3,4 +3,9 @@ window_title=FastChat (ШвидкоЧат) menu_bar_menu=Меню menu_bar_online_users=Користувачі онлайн menu_bar_settings=Налаштування - +client_attempting_to_connect=Спроба під'єднання до +client_connection_failed=Не вдалося під'єднатися! +login_lbl_name=Ім'я +login_lbl_address=Адреса (IP) +login_lbl_port=Порт +login_submit=Відправити From cf9d3e8b45a6ef4b7479962be2e7c32178fcb641 Mon Sep 17 00:00:00 2001 From: OHopiak Date: Thu, 11 Jan 2018 01:56:43 +0200 Subject: [PATCH 08/37] added a Dockerfile to run as node --- deploy/Dockerfile | 11 +++++++++++ .../java/com/fastchat/FastChat/util/Networking.java | 4 ++++ 2 files changed, 15 insertions(+) create mode 100644 deploy/Dockerfile create mode 100644 src/main/java/com/fastchat/FastChat/util/Networking.java diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 0000000..7fa7417 --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,11 @@ +FROM gradle:latest + +ENV GRADLE_BUILD_DIR /home/gradle/workspace + +COPY . ${GRADLE_BUILD_DIR} + +USER root +RUN chown gradle:gradle -R ${GRADLE_BUILD_DIR} +USER gradle + +WORKDIR ${GRADLE_BUILD_DIR} diff --git a/src/main/java/com/fastchat/FastChat/util/Networking.java b/src/main/java/com/fastchat/FastChat/util/Networking.java new file mode 100644 index 0000000..e0935ef --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/util/Networking.java @@ -0,0 +1,4 @@ +package com.fastchat.FastChat.util; + +public class Networking { +} From dee0c4b3a383e197db896721c05cad9bf00fdad9 Mon Sep 17 00:00:00 2001 From: Orest Hopiak Date: Thu, 11 Jan 2018 01:58:59 +0200 Subject: [PATCH 09/37] Running in a docker node --- Jenkinsfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 82ccfe9..cc6994c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,10 @@ pipeline { - agent any + agent { + dockerfile { + filename './deploy/Dockerfile' + } + + } stages { stage('build') { steps { From 58cba2ffbb9bfd82dad83222d2f67beaa7beec17 Mon Sep 17 00:00:00 2001 From: Orest Hopiak Date: Thu, 11 Jan 2018 02:00:10 +0200 Subject: [PATCH 10/37] trying again to use Dockerfile --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index cc6994c..f522f7a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,7 @@ pipeline { agent { dockerfile { - filename './deploy/Dockerfile' + filename '**/Dockerfile' } } From 730aee1a6e965636dd64cad7a39cb6af8140ed46 Mon Sep 17 00:00:00 2001 From: Orest Hopiak Date: Thu, 11 Jan 2018 02:00:58 +0200 Subject: [PATCH 11/37] stopped using dockerfile --- Jenkinsfile | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index f522f7a..82ccfe9 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,10 +1,5 @@ pipeline { - agent { - dockerfile { - filename '**/Dockerfile' - } - - } + agent any stages { stage('build') { steps { From 115b7ff25ede3a9a76354c8292a65bff8729e81b Mon Sep 17 00:00:00 2001 From: OHopiak Date: Thu, 11 Jan 2018 02:21:40 +0200 Subject: [PATCH 12/37] checking another dockerfile --- deploy/Dockerfile | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/deploy/Dockerfile b/deploy/Dockerfile index 7fa7417..3062fde 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -1,11 +1,5 @@ FROM gradle:latest -ENV GRADLE_BUILD_DIR /home/gradle/workspace - -COPY . ${GRADLE_BUILD_DIR} - USER root -RUN chown gradle:gradle -R ${GRADLE_BUILD_DIR} +RUN mkdir /opt/artifacts && chown -R gradle:gradle /opt/artifacts USER gradle - -WORKDIR ${GRADLE_BUILD_DIR} From 23e443d9cc722b00dd3315139380df8eb0f9488f Mon Sep 17 00:00:00 2001 From: Orest Hopiak Date: Thu, 11 Jan 2018 02:34:31 +0200 Subject: [PATCH 13/37] testing another method of running in docker --- Jenkinsfile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 82ccfe9..9e279c4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,11 @@ pipeline { - agent any + agent { + docker { + image 'gradle_runner' + args 'bash' + } + + } stages { stage('build') { steps { From ade698933a35f72131973d7d55a237927a7f5900 Mon Sep 17 00:00:00 2001 From: Orest Hopiak Date: Thu, 11 Jan 2018 02:37:45 +0200 Subject: [PATCH 14/37] ... --- Jenkinsfile | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 9e279c4..82ccfe9 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,11 +1,5 @@ pipeline { - agent { - docker { - image 'gradle_runner' - args 'bash' - } - - } + agent any stages { stage('build') { steps { From caf39ff586553a0138120619e9b1c5bc69fb74ed Mon Sep 17 00:00:00 2001 From: OHopiak Date: Thu, 11 Jan 2018 02:49:08 +0200 Subject: [PATCH 15/37] another docker runner --- deploy/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deploy/Dockerfile b/deploy/Dockerfile index 3062fde..686740f 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -2,4 +2,6 @@ FROM gradle:latest USER root RUN mkdir /opt/artifacts && chown -R gradle:gradle /opt/artifacts +RUN apt update && apt install -y ssh +RUN systemctl enable ssh USER gradle From 9053fe71d72b88704f37195b0f6fcf70d6fb97d3 Mon Sep 17 00:00:00 2001 From: OHopiak Date: Thu, 11 Jan 2018 03:08:10 +0200 Subject: [PATCH 16/37] fixed dockerfile --- deploy/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deploy/Dockerfile b/deploy/Dockerfile index 686740f..08f2bbf 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -3,5 +3,4 @@ FROM gradle:latest USER root RUN mkdir /opt/artifacts && chown -R gradle:gradle /opt/artifacts RUN apt update && apt install -y ssh -RUN systemctl enable ssh -USER gradle +#USER gradle From ec5a107d4a8baa56858e7fa770ea2b2b49238134 Mon Sep 17 00:00:00 2001 From: Orest Hopiak Date: Thu, 11 Jan 2018 03:10:25 +0200 Subject: [PATCH 17/37] Added Jenkinsfile --- Jenkinsfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 82ccfe9..91922d7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,10 @@ pipeline { - agent any + agent { + node { + label 'gradle' + } + + } stages { stage('build') { steps { From 1c9287fa8687f05aee4d243b57a4132697af6775 Mon Sep 17 00:00:00 2001 From: Orest Hopiak Date: Thu, 11 Jan 2018 03:11:41 +0200 Subject: [PATCH 18/37] fix Jenkinsfile --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 91922d7..8805ec7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -13,7 +13,7 @@ pipeline { } stage('test') { steps { - sh 'gradle check jacoco' + sh 'gradle check jacocoTestReport' junit(testResults: '**/build/test-results/**/*.xml', allowEmptyResults: true) jacoco(execPattern: '**/build/**.exec') } From 16be2f04a82d4b53c94504a65fab40fb5fbbd000 Mon Sep 17 00:00:00 2001 From: Orest Hopiak Date: Thu, 11 Jan 2018 03:13:59 +0200 Subject: [PATCH 19/37] add a deploy script execution to Jenkinsfile --- Jenkinsfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfile b/Jenkinsfile index 8805ec7..6d18859 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -22,6 +22,7 @@ pipeline { steps { sh 'gradle build' archiveArtifacts '**/build/libs/*.jar' + sh './scripts/deploy' } } } From 717da599ad8b55c089b2346f97a9407519bb81b8 Mon Sep 17 00:00:00 2001 From: OHopiak Date: Thu, 11 Jan 2018 04:18:43 +0200 Subject: [PATCH 20/37] updated deploy script --- build.gradle | 21 ++++++++++++++++----- scripts/deploy | 31 ++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index f67e99b..3df918d 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,18 @@ dependencies { configurations.compile.extendsFrom(configurations.libraries) } +jacocoTestReport{ + reports { + html.enabled true + xml.enabled true + csv.enabled true + } +} + +javadoc{ + failOnError = false +} + task printJarName { dependsOn 'jar' doLast { @@ -50,10 +62,9 @@ task printJarName { } } -jacocoTestReport{ - reports { - html.enabled true - xml.enabled true - csv.enabled true +task printArtifactsDirectory { + dependsOn 'jar' + doLast { + println "${project.name}/${project.version}" } } diff --git a/scripts/deploy b/scripts/deploy index 558701e..4e0daaa 100755 --- a/scripts/deploy +++ b/scripts/deploy @@ -1,2 +1,31 @@ #!/usr/bin/env bash -echo "DEPLOY" \ No newline at end of file +set -e + +VERBOSE="true" +# artifacts_dir="/opt/artifacts" +artifacts_dir="build/artifacts" + +function info (){ + [ -n "$VERBOSE" ] && echo -e "\e[1;34m[*]\e[39m $@\e[0m" +} + +function error () { + echo -e "\e[1;31m[!]\e[39m $@\e[0m" 1>&2 +} + +if [ ! -d "$artifacts_dir" ]; then + error "Directory $artifacts_dir doesn't exist" + exit 1 +fi + +version_dir="${artifacts_dir}/`gradle printArtifactsDirectory -q 2>/dev/null | sed -e 's/\.\.//g'`" +artifact_jar="build/libs/`gradle printJarName -q 2>/dev/null`" +artifact_docs="build/docs/javadoc" +artifact_tests="build/reports/tests/test" + +mkdir -p $version_dir +cp -r $artifact_jar $version_dir +cp -r $artifact_docs $version_dir/docs +cp -r $artifact_tests $version_dir/tests + + From 7a417749aac137897cdaaab8bb42252e77ba20b0 Mon Sep 17 00:00:00 2001 From: OHopiak Date: Thu, 11 Jan 2018 04:25:01 +0200 Subject: [PATCH 21/37] mistake in deploy script --- scripts/deploy | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/deploy b/scripts/deploy index 4e0daaa..6f231ec 100755 --- a/scripts/deploy +++ b/scripts/deploy @@ -2,8 +2,7 @@ set -e VERBOSE="true" -# artifacts_dir="/opt/artifacts" -artifacts_dir="build/artifacts" +artifacts_dir="/opt/artifacts" function info (){ [ -n "$VERBOSE" ] && echo -e "\e[1;34m[*]\e[39m $@\e[0m" From ffccce244520496c99d72658a9edb013a48a9dc0 Mon Sep 17 00:00:00 2001 From: Orest Hopiak Date: Thu, 11 Jan 2018 04:27:22 +0200 Subject: [PATCH 22/37] forgot to build javadoc --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 6d18859..3414a69 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -20,7 +20,7 @@ pipeline { } stage('deploy') { steps { - sh 'gradle build' + sh 'gradle build javadoc' archiveArtifacts '**/build/libs/*.jar' sh './scripts/deploy' } From cdc62999e604578a921dc98db989ea0422c538ad Mon Sep 17 00:00:00 2001 From: OHopiak Date: Sun, 4 Feb 2018 15:47:00 +0200 Subject: [PATCH 23/37] Changed client structure to use interfaces --- FastChat.uml | 97 +++++++++++++++---- README.md | 1 + build.gradle | 8 +- scripts/deploy | 1 - .../com/fastchat/FastChat/beans/User.java | 4 + .../com/fastchat/FastChat/client/Client.java | 35 +++---- .../fastchat/FastChat/client/ClientGUI.java | 23 ++--- .../FastChat/client/ClientInterface.java | 10 +- .../fastchat/FastChat/client/OnlineUsers.java | 18 ++-- .../com/fastchat/FastChat/server/Server.java | 13 +-- .../fastchat/FastChat/util/Networking.java | 17 ++++ src/main/resources/norwegian.properties | 7 +- 12 files changed, 158 insertions(+), 76 deletions(-) create mode 100644 src/main/java/com/fastchat/FastChat/beans/User.java diff --git a/FastChat.uml b/FastChat.uml index ce7ca48..50a1500 100644 --- a/FastChat.uml +++ b/FastChat.uml @@ -3,38 +3,101 @@ JAVA - com.fastchat.FastChat.Loader - com.fastchat.FastChat.client.ClientGUI - com.fastchat.FastChat.client.LoginInterface - com.fastchat.FastChat.client.LoginGUI - com.fastchat.FastChat.client.Client - com.fastchat.FastChat.server.Server.Status - com.fastchat.FastChat.server.Server - com.fastchat.FastChat.client.OnlineUsers - com.fastchat.FastChat.server.ServerClient - com.fastchat.FastChat.client.ClientInterface + com.fastchat.FastChat.Loader + com.fastchat.FastChat.client.ClientGUI + com.fastchat.FastChat.client.LoginInterface + com.fastchat.FastChat.client.LoginGUI + com.fastchat.FastChat.client.Client + com.fastchat.FastChat.server.Server.Status + com.fastchat.FastChat.server.Server + com.fastchat.FastChat.client.OnlineUsers + com.fastchat.FastChat.server.ServerClient + com.fastchat.FastChat.client.ClientInterface - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + com.fastchat.FastChat.Loader Inner Classes + Methods All private diff --git a/README.md b/README.md index 6785f21..5771fb2 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Jenkins: [![Build Status](http://18.194.57.194:8080/job/FastChat/job/master/4/badge/icon)](http://18.194.57.194:8080/job/FastChat/job/master/4/) This is the simple chatting tool + Feel free to contribute to this project Requirements ------------ diff --git a/build.gradle b/build.gradle index 3df918d..b2ed8be 100644 --- a/build.gradle +++ b/build.gradle @@ -9,6 +9,7 @@ description = """FastChat - a tool for instant chatting that not always works as sourceCompatibility = 1.8 targetCompatibility = 1.8 + configurations { libraries } @@ -43,15 +44,18 @@ dependencies { configurations.compile.extendsFrom(configurations.libraries) } -jacocoTestReport{ +jacocoTestReport { reports { + //noinspection GroovyAssignabilityCheck html.enabled true + //noinspection GroovyAssignabilityCheck xml.enabled true + //noinspection GroovyAssignabilityCheck csv.enabled true } } -javadoc{ +javadoc { failOnError = false } diff --git a/scripts/deploy b/scripts/deploy index 6f231ec..ec7107e 100755 --- a/scripts/deploy +++ b/scripts/deploy @@ -27,4 +27,3 @@ cp -r $artifact_jar $version_dir cp -r $artifact_docs $version_dir/docs cp -r $artifact_tests $version_dir/tests - diff --git a/src/main/java/com/fastchat/FastChat/beans/User.java b/src/main/java/com/fastchat/FastChat/beans/User.java new file mode 100644 index 0000000..d5d1085 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/beans/User.java @@ -0,0 +1,4 @@ +package com.fastchat.FastChat.beans; + +public class User { +} diff --git a/src/main/java/com/fastchat/FastChat/client/Client.java b/src/main/java/com/fastchat/FastChat/client/Client.java index 91aba5e..e4ae272 100644 --- a/src/main/java/com/fastchat/FastChat/client/Client.java +++ b/src/main/java/com/fastchat/FastChat/client/Client.java @@ -1,5 +1,7 @@ package com.fastchat.FastChat.client; +import com.fastchat.FastChat.util.Networking; + import javax.swing.*; import java.io.IOException; import java.net.*; @@ -12,7 +14,13 @@ public class Client { private DatagramSocket socket; private InetAddress ip; private Thread send; - private boolean running, kicked = false, banned = false; + private boolean running = false, kicked = false, banned = false; + + Client(String name, int port) { + super(); + this.name = name; + this.port = port; + } protected boolean openConnection(String address) { try { @@ -39,16 +47,7 @@ protected String receive() { } private void send(final byte[] data) { - send = new Thread("Send") { - public void run() { - DatagramPacket packet = new DatagramPacket(data, data.length, ip, port); - try { - socket.send(packet); - } catch (IOException e) { - e.printStackTrace(); - } - } - }; + send = Networking.send(socket, data, ip, port); send.start(); } @@ -79,14 +78,14 @@ public String getName() { return name; } - public void setID(int iD) { - ID = iD; - } - public int getID() { return ID; } + public void setID(int iD) { + ID = iD; + } + public int getPort() { return port; } @@ -95,12 +94,6 @@ public InetAddress getIp() { return ip; } - public Client(String name, int port) { - super(); - this.name = name; - this.port = port; - } - public boolean isRunning() { return running; } diff --git a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java index c73ca7a..75484d1 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java @@ -19,13 +19,6 @@ public class ClientGUI extends ClientInterface { private final static String TITLE = Localization.get("window_title"); private static ClientGUI clientGUI; - { -// GUI initializer generated by IntelliJ IDEA GUI Designer -// >>> IMPORTANT!! <<< -// DO NOT EDIT OR ADD ANY CODE HERE! - $$$setupUI$$$(); - } - private JFrame frame; private JTextField messagePrompt; private JButton sendButton; @@ -33,6 +26,12 @@ public class ClientGUI extends ClientInterface { private JPanel clientPanel; private JTextArea history; + { +// GUI initializer generated by IntelliJ IDEA GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } /** * @@ -71,18 +70,20 @@ public static ClientGUI getInstance(String name, String address, int port) { } /** + * Creates a menu bar for the client window + * * @return generated MenuBar */ - private static JMenuBar generateMenuBar() { + private JMenuBar generateMenuBar() { JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu(Localization.get("menu_bar_menu")); menuBar.add(menu); JMenuItem onlineUsers = new JMenuItem(Localization.get("menu_bar_online_users")); onlineUsers.addActionListener(event -> { -// client.send("/u/"); -// users.updateList(userArr); -// users.setVisible(true); + client.send("/u/"); + users.updateList(userArr); + users.setVisible(true); }); menu.add(onlineUsers); diff --git a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java index 104acc2..57584d7 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java @@ -5,14 +5,12 @@ public abstract class ClientInterface implements Runnable { Client client; + OnlineUsers users; + String[] userArr = {"Name(id)"}; private Thread run, listen; - private OnlineUsers users; - private String name, address; private int port; - private String[] userArr = {"Name(id)"}; - ClientInterface() { this("Anon", "localhost", 1234); } @@ -93,6 +91,10 @@ private void serve() { run.start(); } + public Client getClient() { + return client; + } + public Thread getRun() { return run; } diff --git a/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java b/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java index ea7277f..29dbf82 100644 --- a/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java +++ b/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java @@ -7,11 +7,11 @@ import java.awt.*; public class OnlineUsers extends JFrame { - + private static final long serialVersionUID = -3779778679932068169L; private JPanel contentPane; private JList list; - + public OnlineUsers() { setType(Type.UTILITY); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); @@ -22,20 +22,20 @@ public OnlineUsers() { contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); GridBagLayout gbl_contentPane = new GridBagLayout(); - gbl_contentPane.columnWidths = new int[] { 0, 0 }; - gbl_contentPane.rowHeights = new int[] { 0, 0 }; - gbl_contentPane.columnWeights = new double[] { 1.0, Double.MIN_VALUE }; - gbl_contentPane.rowWeights = new double[] { 1.0, Double.MIN_VALUE }; + gbl_contentPane.columnWidths = new int[]{0, 0}; + gbl_contentPane.rowHeights = new int[]{0, 0}; + gbl_contentPane.columnWeights = new double[]{1.0, Double.MIN_VALUE}; + gbl_contentPane.rowWeights = new double[]{1.0, Double.MIN_VALUE}; contentPane.setLayout(gbl_contentPane); - - list = new JList(); + + list = new JList<>(); GridBagConstraints gbc_list = new GridBagConstraints(); gbc_list.fill = GridBagConstraints.BOTH; gbc_list.gridx = 0; gbc_list.gridy = 0; contentPane.add(list, gbc_list); } - + public void updateList(String[] users) { list.setListData(users); } diff --git a/src/main/java/com/fastchat/FastChat/server/Server.java b/src/main/java/com/fastchat/FastChat/server/Server.java index e6d718f..c76b5ea 100644 --- a/src/main/java/com/fastchat/FastChat/server/Server.java +++ b/src/main/java/com/fastchat/FastChat/server/Server.java @@ -1,5 +1,7 @@ package com.fastchat.FastChat.server; +import com.fastchat.FastChat.util.Networking; + import java.io.IOException; import java.net.*; import java.util.ArrayList; @@ -220,16 +222,7 @@ public void run() { } private void send(final byte[] data, final InetAddress address, final int port) { - send = new Thread("Send") { - public void run() { - DatagramPacket packet = new DatagramPacket(data, data.length, address, port); - try { - socket.send(packet); - } catch (IOException e) { - e.printStackTrace(); - } - } - }; + send = Networking.send(socket, data, address, port); send.start(); } diff --git a/src/main/java/com/fastchat/FastChat/util/Networking.java b/src/main/java/com/fastchat/FastChat/util/Networking.java index e0935ef..8db0c35 100644 --- a/src/main/java/com/fastchat/FastChat/util/Networking.java +++ b/src/main/java/com/fastchat/FastChat/util/Networking.java @@ -1,4 +1,21 @@ package com.fastchat.FastChat.util; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; + public class Networking { + public static Thread send(DatagramSocket socket, final byte[] data, final InetAddress address, final int port) { + return new Thread("Send") { + public void run() { + DatagramPacket packet = new DatagramPacket(data, data.length, address, port); + try { + socket.send(packet); + } catch (IOException e) { + e.printStackTrace(); + } + } + }; + } } diff --git a/src/main/resources/norwegian.properties b/src/main/resources/norwegian.properties index 7b031fe..56aef24 100644 --- a/src/main/resources/norwegian.properties +++ b/src/main/resources/norwegian.properties @@ -3,4 +3,9 @@ window_title=FastChat (RaskChat) menu_bar_menu=Meny #menu_bar_online_users=Online Users menu_bar_settings=Instillinger - +#client_attempting_to_connect=Attempting to conect to +#client_connection_failed=Connection failed! +#login_lbl_name=Name +#login_lbl_address=Address (IP) +#login_lbl_port=Port +#login_submit=Submit \ No newline at end of file From 7be96812ab9915e76f24433661e1612929e280ad Mon Sep 17 00:00:00 2001 From: OHopiak Date: Sun, 4 Feb 2018 22:00:07 +0200 Subject: [PATCH 24/37] Fixed Online Users tab, added Window Icon --- .gitignore | 1 + .../fastchat/FastChat/client/ClientGUI.java | 75 ++++++++++-------- .../FastChat/client/ClientInterface.java | 7 +- .../fastchat/FastChat/client/LoginGUI.java | 37 +++++---- .../fastchat/FastChat/client/OnlineUsers.java | 2 +- .../FastChat/server/ServerClient.java | 6 +- src/main/resources/FastChatLogo.png | Bin 0 -> 30883 bytes src/main/resources/FastChatLogo_filled.png | Bin 0 -> 23778 bytes 8 files changed, 73 insertions(+), 55 deletions(-) create mode 100644 src/main/resources/FastChatLogo.png create mode 100644 src/main/resources/FastChatLogo_filled.png diff --git a/.gitignore b/.gitignore index 0826a07..dfefdf2 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ gradle gradlew gradlew.bat build +.gradletasknamecache diff --git a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java index 75484d1..87f5b24 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java @@ -8,6 +8,7 @@ import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.net.URL; /** * The basic GUI class, @@ -26,13 +27,6 @@ public class ClientGUI extends ClientInterface { private JPanel clientPanel; private JTextArea history; - { -// GUI initializer generated by IntelliJ IDEA GUI Designer -// >>> IMPORTANT!! <<< -// DO NOT EDIT OR ADD ANY CODE HERE! - $$$setupUI$$$(); - } - /** * */ @@ -69,6 +63,17 @@ public static ClientGUI getInstance(String name, String address, int port) { return clientGUI; } + { +// GUI initializer generated by IntelliJ IDEA GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + + public JFrame getFrame() { + return frame; + } + /** * Creates a menu bar for the client window * @@ -80,11 +85,7 @@ private JMenuBar generateMenuBar() { JMenu menu = new JMenu(Localization.get("menu_bar_menu")); menuBar.add(menu); JMenuItem onlineUsers = new JMenuItem(Localization.get("menu_bar_online_users")); - onlineUsers.addActionListener(event -> { - client.send("/u/"); - users.updateList(userArr); - users.setVisible(true); - }); + onlineUsers.addActionListener(event -> client.send("/u/")); menu.add(onlineUsers); JMenuItem settings = new JMenuItem(Localization.get("menu_bar_settings")); @@ -96,8 +97,24 @@ private JMenuBar generateMenuBar() { return menuBar; } - public JFrame getFrame() { - return frame; + @Override + void console(String message) { + history.setCaretPosition(history.getDocument().getLength()); + history.append(message + "\r\n"); + } + + @Override + protected void send(String message) { + if (message.equals("")) return; + message = client.getName() + ": " + message; + messagePrompt.setText(""); + client.send("/m/" + message); + } + + @Override + protected void onBreak() { + history.setBackground(new Color(55, 55, 55)); + messagePrompt.setEditable(false); } /** @@ -114,7 +131,7 @@ public void init() { ((DefaultCaret) history.getCaret()).setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); if (frame == null) - frame = new JFrame(TITLE); + frame = new JFrame(TITLE + " (" + getName() + ")"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setJMenuBar(generateMenuBar()); @@ -131,31 +148,19 @@ public void windowClosing(WindowEvent e) { } }); + // Set up window icon + URL iconUrl = getClass().getClassLoader().getResource("FastChatLogo_filled.png"); + if (iconUrl != null) { + ImageIcon icon; + icon = new ImageIcon(iconUrl); + frame.setIconImage(icon.getImage()); + } + super.init(); frame.setVisible(true); } - @Override - void console(String message) { - history.setCaretPosition(history.getDocument().getLength()); - history.append(message + "\r\n"); - } - - @Override - protected void send(String message) { - if (message.equals("")) return; - message = client.getName() + ": " + message; - messagePrompt.setText(""); - client.send("/m/" + message); - } - - @Override - protected void onBreak() { - history.setBackground(new Color(55, 55, 55)); - messagePrompt.setEditable(false); - } - /** * Method generated by IntelliJ IDEA GUI Designer * >>> IMPORTANT!! <<< diff --git a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java index 57584d7..03e6711 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java @@ -5,8 +5,8 @@ public abstract class ClientInterface implements Runnable { Client client; - OnlineUsers users; - String[] userArr = {"Name(id)"}; + private OnlineUsers users; + private String[] userArr = {"Name(id)"}; private Thread run, listen; private String name, address; private int port; @@ -42,6 +42,7 @@ protected void init() { this.serve(); } client.send(("/c/" + name)); + users = new OnlineUsers(); } protected abstract void send(String message); @@ -57,6 +58,7 @@ private void listen() { public void run() { while (client.isRunning()) { String message = client.receive(); +// System.out.println(message); if (message.startsWith("/c/")) { client.setID(Integer.parseInt(message.substring(3))); console("Connection is setted up successfully... ID is " + client.getID()); @@ -77,6 +79,7 @@ public void run() { message = message.substring(3); userArr = message.split("//"); users.updateList(userArr); + users.setVisible(true); } else { console("Server: " + message); } diff --git a/src/main/java/com/fastchat/FastChat/client/LoginGUI.java b/src/main/java/com/fastchat/FastChat/client/LoginGUI.java index 82f05e2..75e1942 100644 --- a/src/main/java/com/fastchat/FastChat/client/LoginGUI.java +++ b/src/main/java/com/fastchat/FastChat/client/LoginGUI.java @@ -6,6 +6,7 @@ import javax.swing.*; import java.awt.event.ActionListener; +import java.net.URL; public class LoginGUI extends LoginInterface { @@ -21,6 +22,10 @@ public class LoginGUI extends LoginInterface { private JLabel lblAddress; private JLabel lblPort; + public LoginGUI() { + init(); + } + { // GUI initializer generated by IntelliJ IDEA GUI Designer // >>> IMPORTANT!! <<< @@ -28,8 +33,17 @@ public class LoginGUI extends LoginInterface { $$$setupUI$$$(); } - public LoginGUI() { - init(); + @Override + protected void submit(String name, String address, int port) { + clientInterface = ClientGUI.getInstance(name, address, port); + clientInterface.init(); + } + + public void reset() { + frame.setVisible(true); +// txtAddress.setText("localhost"); +// txtPort.setText("1234"); + txtAddress.requestFocusInWindow(); } private void init() { @@ -62,20 +76,15 @@ private void init() { txtAddress.setText("localhost"); txtPort.setText("1234"); - frame.setVisible(true); - } - - @Override - protected void submit(String name, String address, int port) { - clientInterface = ClientGUI.getInstance(name, address, port); - clientInterface.init(); - } + // Set up window icon + URL iconUrl = getClass().getClassLoader().getResource("FastChatLogo_filled.png"); + if (iconUrl != null) { + ImageIcon icon; + icon = new ImageIcon(iconUrl); + frame.setIconImage(icon.getImage()); + } - public void reset() { frame.setVisible(true); -// txtAddress.setText("localhost"); -// txtPort.setText("1234"); - txtAddress.requestFocusInWindow(); } /** diff --git a/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java b/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java index 29dbf82..1b431f5 100644 --- a/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java +++ b/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java @@ -12,7 +12,7 @@ public class OnlineUsers extends JFrame { private JPanel contentPane; private JList list; - public OnlineUsers() { + OnlineUsers() { setType(Type.UTILITY); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setSize(278, 409); diff --git a/src/main/java/com/fastchat/FastChat/server/ServerClient.java b/src/main/java/com/fastchat/FastChat/server/ServerClient.java index e67fcf7..310be52 100644 --- a/src/main/java/com/fastchat/FastChat/server/ServerClient.java +++ b/src/main/java/com/fastchat/FastChat/server/ServerClient.java @@ -11,13 +11,13 @@ public class ServerClient { public int port; private final int ID; public int attempt = 0; - private static ArrayList identifiers = new ArrayList(); + private static ArrayList identifiers = new ArrayList<>(); public int getID() { return ID; } - - public ServerClient(String name, InetAddress ip, int port) { + + ServerClient(String name, InetAddress ip, int port) { super(); this.name = name; this.ip = ip; diff --git a/src/main/resources/FastChatLogo.png b/src/main/resources/FastChatLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..d329695ff5ee5d2cfb8b8f82689d6daf46c13227 GIT binary patch literal 30883 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 Date: Sun, 4 Feb 2018 22:13:17 +0200 Subject: [PATCH 25/37] fix version in build.gradle --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b2ed8be..35b9b6b 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'java' apply plugin: 'jacoco' group = 'com.fastchat' -version = '0.0.1' +version = '0.0.2' description = """FastChat - a tool for instant chatting that not always works as expected - WIP""" From 06b2dfbd9d9cfa982c657415bc7b68f0a9e639e6 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 3 Mar 2018 21:43:36 +0200 Subject: [PATCH 26/37] New Classes(Command and CommandRegistry) and their implementation in Server Class --- FastChat.uml | 36 +--- .../com/fastchat/FastChat/server/Server.java | 171 ++++++++++-------- .../com/fastchat/FastChat/util/Command.java | 59 ++++++ .../FastChat/util/CommandRegistry.java | 26 +++ .../FastChat/util/EnhancedCallable.java | 11 ++ src/test/java/Utils/CommandRegistry.java | 7 + src/test/java/Utils/CommandTest.java | 7 + 7 files changed, 212 insertions(+), 105 deletions(-) create mode 100644 src/main/java/com/fastchat/FastChat/util/Command.java create mode 100644 src/main/java/com/fastchat/FastChat/util/CommandRegistry.java create mode 100644 src/main/java/com/fastchat/FastChat/util/EnhancedCallable.java create mode 100644 src/test/java/Utils/CommandRegistry.java create mode 100644 src/test/java/Utils/CommandTest.java diff --git a/FastChat.uml b/FastChat.uml index ce7ca48..fe13391 100644 --- a/FastChat.uml +++ b/FastChat.uml @@ -2,39 +2,15 @@ JAVA - - com.fastchat.FastChat.Loader - com.fastchat.FastChat.client.ClientGUI - com.fastchat.FastChat.client.LoginInterface - com.fastchat.FastChat.client.LoginGUI - com.fastchat.FastChat.client.Client - com.fastchat.FastChat.server.Server.Status - com.fastchat.FastChat.server.Server - com.fastchat.FastChat.client.OnlineUsers - com.fastchat.FastChat.server.ServerClient - com.fastchat.FastChat.client.ClientInterface - + - - - - - - - - - - - - - - - - - com.fastchat.FastChat.Loader - + + + Inner Classes + Fields + Constructors All private diff --git a/src/main/java/com/fastchat/FastChat/server/Server.java b/src/main/java/com/fastchat/FastChat/server/Server.java index e6d718f..fe8ece8 100644 --- a/src/main/java/com/fastchat/FastChat/server/Server.java +++ b/src/main/java/com/fastchat/FastChat/server/Server.java @@ -1,18 +1,21 @@ package com.fastchat.FastChat.server; +import com.fastchat.FastChat.util.Command; +import com.fastchat.FastChat.util.CommandRegistry; + import java.io.IOException; import java.net.*; -import java.util.ArrayList; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Scanner; +import java.util.*; public class Server implements Runnable { private final int MAX_ATTEMPTS = 4; - private static final String[] commands = {"clients", /*"cls",*/ "help", "kick", "raw", "exit"}; private List clients = new ArrayList<>(); + private HashMap commands = new HashMap<>(); + + private CommandRegistry commandRegistry = new CommandRegistry(); + private DatagramSocket socket; private int port; @@ -36,6 +39,8 @@ public Server(int port, String address) { return; } run = new Thread(this, "Server"); + initCommands(); + run.start(); } @@ -69,77 +74,21 @@ public void run() { if (!running) in.close(); } + /** + * example: "kick 12345432 2345 3456 3456 34567 34567" + * example - split: ["kick", "12345432", "2345", "3456", "3456", "34567", "34567"] + * example - command name: "kick" + * example - argv: ["12345432", "2345", "3456", "3456", "34567", "34567"] + * + * @param text + */ private void command(String text) { - switch (text.split(" ")[0]) { - case "raw": - raw = !raw; - System.out.println((raw ? "Raw mode enabled" : "Raw mode disabled")); - break; -// case "cls": -// System.out.flush(); -// Process p; -// try { -// p = Runtime.getRuntime().exec("cls"); -// p.waitFor(); -// } catch (IOException | InterruptedException e) { -// System.err.println("Cannot run this command"); -// } -// break; - case "clients": - System.out.println("Clients:"); - System.out.println("==================================="); - for (ServerClient serverClient : clients) { - System.out.println(serverClient); - } - System.out.println("==================================="); - break; - case "kick": - String name = text.split(" ")[1]; - int id = -1; - boolean number = true; - try { - id = Integer.parseInt(name); - } catch (NumberFormatException e) { - number = false; - } - if (number) { - boolean exists = false; - for (ServerClient client : clients) { - if (client.getID() == id) { - exists = true; - break; - } - } - if (exists) - disconnectClient(id, Status.KICKED); - else - System.out.println("Client with id " + id + " does not exist"); - } else { - for (ServerClient client : clients) { - if (name.equals(client.name)) { - disconnectClient(client.getID(), Status.KICKED); - break; - } - } - } - break; - case "help": - System.out.println("Available commands: "); - for (String command : commands) { - System.out.println("/" + command); - } - break; - case "exit": - System.out.print("Do you really want to exit? (y/n) "); - Scanner input = new Scanner(System.in); - char option = input.nextLine().toLowerCase().charAt(0); - if (option == 'y') { - System.exit(0); - } - break; - default: - System.out.println("Command does not exist!"); - } + String[] args = text.split(" "); + String commandName = args[0]; + args = Arrays.copyOfRange(args, 1, args.length); + //Command c = commands.get(commandName); + //CommandRegistry commandRegistry = new CommandRegistry(); + this.commandRegistry.run(commandName, args); } @@ -275,4 +224,76 @@ private void disconnectClient(int id, Status status) { public int getPort() { return port; } + + private void initCommands() { + //CommandRegistry commandRegistry = new CommandRegistry(); + + this.commandRegistry.add(new Command("exit", (argv) -> { + System.out.print("Do you really want to exit? (y/n) "); + Scanner input = new Scanner(System.in); + char option = input.nextLine().toLowerCase().charAt(0); + if (option == 'y') { + System.exit(0); + } + return null; + })); + + this.commandRegistry.add(new Command("help", (argv) -> { + System.out.println("Available commands: "); + commandRegistry.getCommands().forEach((name, c) -> + System.out.println("/" + name + (c.getHelp().isEmpty() ? "" : " - " + c.getHelp())) + ); + return null; + })); + + this.commandRegistry.add(new Command("clients", "prints the list of clients", (argv) -> { + System.out.println("Clients:"); + System.out.println("==================================="); + for (ServerClient serverClient : clients) { + System.out.println(serverClient); + } + System.out.println("==================================="); + return null; + })); + + this.commandRegistry.add(new Command("kick", "kick the list of clients", (argv) -> { + for (String name : argv) { + + int id = -1; + boolean number = true; + try { + id = Integer.parseInt(name); + } catch (NumberFormatException e) { + number = false; + } + if (number) { + boolean exists = false; + for (ServerClient client : clients) { + if (client.getID() == id) { + exists = true; + break; + } + } + if (exists) + disconnectClient(id, Status.KICKED); + else + System.out.println("Client with id " + id + " does not exist"); + } else { + for (ServerClient client : clients) { + if (name.equals(client.name)) { + disconnectClient(client.getID(), Status.KICKED); + break; + } + } + } + } + return null; + })); + + this.commandRegistry.add(new Command("raw", "", (argv) -> { + raw = !raw; + System.out.println((raw ? "Raw mode enabled" : "Raw mode disabled")); + return null; + })); + } } diff --git a/src/main/java/com/fastchat/FastChat/util/Command.java b/src/main/java/com/fastchat/FastChat/util/Command.java new file mode 100644 index 0000000..26ad991 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/util/Command.java @@ -0,0 +1,59 @@ +package com.fastchat.FastChat.util; + + +public class Command { + private String help; + private String name; + + //it's Interface created for body + EnhancedCallable body; + + + public EnhancedCallable getBody() { + return body; + } + + + + /** + * + * @param name a name of the command + * @param body body of the command + */ + public Command(String name, EnhancedCallable body) { + this(name, "", body); + } + + public Command(String name, String help, EnhancedCallable body) { + this.name = name; + this.help = help; + this.body = body; + } + + /** + * + * @param args command arguments + */ + public void exec(String... args) { + try { + body.call(args); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public String getHelp() { + return help; + } + + public String getName() { + return name; + } + + +} + +//Java 8 lambda +//Interface Callable +//Interface Runnable + diff --git a/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java b/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java new file mode 100644 index 0000000..d045b7d --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java @@ -0,0 +1,26 @@ +package com.fastchat.FastChat.util; + +import java.util.HashMap; + +public class CommandRegistry { + private HashMap commands = new HashMap<>(); + + + public void add(Command command){ + commands.put(command.getName(),command); + } + + public void run(String name, String... args){ + Command c = commands.get(name); + + if (c != null) { + c.exec(args); + } else { + throw new IllegalArgumentException("Command does not exist!"); + } + } + + public HashMap getCommands() { + return commands; + } +} diff --git a/src/main/java/com/fastchat/FastChat/util/EnhancedCallable.java b/src/main/java/com/fastchat/FastChat/util/EnhancedCallable.java new file mode 100644 index 0000000..ed006c2 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/util/EnhancedCallable.java @@ -0,0 +1,11 @@ +package com.fastchat.FastChat.util; + +/** + * + * @param return type + * @param arguments to be put as varargs + */ +@FunctionalInterface +public interface EnhancedCallable { + R call(A... args); +} diff --git a/src/test/java/Utils/CommandRegistry.java b/src/test/java/Utils/CommandRegistry.java new file mode 100644 index 0000000..dc604f6 --- /dev/null +++ b/src/test/java/Utils/CommandRegistry.java @@ -0,0 +1,7 @@ +package Utils; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +public class CommandRegistry { +} diff --git a/src/test/java/Utils/CommandTest.java b/src/test/java/Utils/CommandTest.java new file mode 100644 index 0000000..b9ecbde --- /dev/null +++ b/src/test/java/Utils/CommandTest.java @@ -0,0 +1,7 @@ +package Utils; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +public class CommandTest { +} From 061c064accf2440164509cae9a3a383a468d4107 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 3 Mar 2018 22:22:33 +0200 Subject: [PATCH 27/37] New Classes(Command and CommandRegistry) and their implementation in Server Class, and UnitTests for Command Class --- .../com/fastchat/FastChat/util/Command.java | 4 +--- src/test/java/Utils/CommandTest.java | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/fastchat/FastChat/util/Command.java b/src/main/java/com/fastchat/FastChat/util/Command.java index 26ad991..307320b 100644 --- a/src/main/java/com/fastchat/FastChat/util/Command.java +++ b/src/main/java/com/fastchat/FastChat/util/Command.java @@ -53,7 +53,5 @@ public String getName() { } -//Java 8 lambda -//Interface Callable -//Interface Runnable + diff --git a/src/test/java/Utils/CommandTest.java b/src/test/java/Utils/CommandTest.java index b9ecbde..4901440 100644 --- a/src/test/java/Utils/CommandTest.java +++ b/src/test/java/Utils/CommandTest.java @@ -1,7 +1,30 @@ package Utils; +import com.fastchat.FastChat.util.Command; import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; + public class CommandTest { + + Command command = new Command("help", (argv) -> { + System.out.print("Do you really need help? "); + return null; + }); + + @Test + public void CommandNotNull(){ + assertNotNull(command.getName()); + assertNotNull(command.getHelp()); + assertNotNull(command.getBody()); + } + + @Test + public void CommandEquals(){ + assertEquals("help", command.getName()); + assertEquals("",command.getHelp()); + + } } From a04315c5bc9c873c0facc3b1da7d8dd6cf36e672 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 3 Mar 2018 22:42:27 +0200 Subject: [PATCH 28/37] Delete variable and useless comments --- src/main/java/com/fastchat/FastChat/server/Server.java | 5 ----- src/test/java/Utils/CommandTest.java | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/com/fastchat/FastChat/server/Server.java b/src/main/java/com/fastchat/FastChat/server/Server.java index fe8ece8..1b37e01 100644 --- a/src/main/java/com/fastchat/FastChat/server/Server.java +++ b/src/main/java/com/fastchat/FastChat/server/Server.java @@ -12,8 +12,6 @@ public class Server implements Runnable { private final int MAX_ATTEMPTS = 4; private List clients = new ArrayList<>(); - private HashMap commands = new HashMap<>(); - private CommandRegistry commandRegistry = new CommandRegistry(); private DatagramSocket socket; @@ -86,8 +84,6 @@ private void command(String text) { String[] args = text.split(" "); String commandName = args[0]; args = Arrays.copyOfRange(args, 1, args.length); - //Command c = commands.get(commandName); - //CommandRegistry commandRegistry = new CommandRegistry(); this.commandRegistry.run(commandName, args); } @@ -226,7 +222,6 @@ public int getPort() { } private void initCommands() { - //CommandRegistry commandRegistry = new CommandRegistry(); this.commandRegistry.add(new Command("exit", (argv) -> { System.out.print("Do you really want to exit? (y/n) "); diff --git a/src/test/java/Utils/CommandTest.java b/src/test/java/Utils/CommandTest.java index 4901440..fb6f63d 100644 --- a/src/test/java/Utils/CommandTest.java +++ b/src/test/java/Utils/CommandTest.java @@ -9,7 +9,7 @@ public class CommandTest { - Command command = new Command("help", (argv) -> { + Command command = new Command("help","", (argv) -> { System.out.print("Do you really need help? "); return null; }); From e8a4b4ab81753929a1f37cd1880b722b91c0143c Mon Sep 17 00:00:00 2001 From: OHopiak Date: Sun, 4 Mar 2018 02:50:03 +0200 Subject: [PATCH 29/37] slight refactoring --- scripts/deploy | 8 ++++---- .../java/com/fastchat/FastChat/Loader.java | 6 +----- .../java/com/fastchat/FastChat/beans/User.java | 2 +- .../com/fastchat/FastChat/client/Client.java | 12 ++++++------ .../fastchat/FastChat/client/ClientGUI.java | 14 +++++++------- .../FastChat/client/ClientInterface.java | 8 ++++---- .../com/fastchat/FastChat/client/LoginGUI.form | 3 +++ .../com/fastchat/FastChat/client/LoginGUI.java | 18 +++++++++++------- .../fastchat/FastChat/client/OnlineUsers.java | 4 ++-- .../com/fastchat/FastChat/server/Server.java | 15 +++++---------- .../fastchat/FastChat/server/ServerClient.java | 14 +++++++------- .../com/fastchat/FastChat/util/Command.java | 6 +++--- .../FastChat/util/CommandRegistry.java | 2 +- .../fastchat/FastChat/util/Localization.java | 2 +- .../fastchat/FastChat/util/UTF8Control.java | 2 +- src/test/java/Utils/CommandRegistry.java | 7 ------- src/test/java/util/CommandRegistry.java | 4 ++++ src/test/java/{Utils => util}/CommandTest.java | 12 ++++++------ 18 files changed, 67 insertions(+), 72 deletions(-) delete mode 100644 src/test/java/Utils/CommandRegistry.java create mode 100644 src/test/java/util/CommandRegistry.java rename src/test/java/{Utils => util}/CommandTest.java (67%) diff --git a/scripts/deploy b/scripts/deploy index ec7107e..16fa1d5 100755 --- a/scripts/deploy +++ b/scripts/deploy @@ -22,8 +22,8 @@ artifact_jar="build/libs/`gradle printJarName -q 2>/dev/null`" artifact_docs="build/docs/javadoc" artifact_tests="build/reports/tests/test" -mkdir -p $version_dir -cp -r $artifact_jar $version_dir -cp -r $artifact_docs $version_dir/docs -cp -r $artifact_tests $version_dir/tests +mkdir -p "$version_dir" +cp -r "$artifact_jar" "$version_dir" +cp -r "$artifact_docs" "$version_dir/docs" +cp -r "$artifact_tests" "$version_dir/tests" diff --git a/src/main/java/com/fastchat/FastChat/Loader.java b/src/main/java/com/fastchat/FastChat/Loader.java index 88366d5..d3188f0 100644 --- a/src/main/java/com/fastchat/FastChat/Loader.java +++ b/src/main/java/com/fastchat/FastChat/Loader.java @@ -7,7 +7,7 @@ import java.awt.*; -public class Loader { +class Loader { private static final String usageString = "Usage: java -jar FastChat.jar [ -s [PORT] [IP] ]"; private static final String description = @@ -15,10 +15,6 @@ public class Loader { // private static Class clientClass; - private static void usage() { - System.out.println(usageString); - } - private static void help() { String helpString = usageString + "\n" + description + "\n" + diff --git a/src/main/java/com/fastchat/FastChat/beans/User.java b/src/main/java/com/fastchat/FastChat/beans/User.java index d5d1085..c55bd6d 100644 --- a/src/main/java/com/fastchat/FastChat/beans/User.java +++ b/src/main/java/com/fastchat/FastChat/beans/User.java @@ -1,4 +1,4 @@ package com.fastchat.FastChat.beans; -public class User { +class User { } diff --git a/src/main/java/com/fastchat/FastChat/client/Client.java b/src/main/java/com/fastchat/FastChat/client/Client.java index e4ae272..bc7b776 100644 --- a/src/main/java/com/fastchat/FastChat/client/Client.java +++ b/src/main/java/com/fastchat/FastChat/client/Client.java @@ -8,8 +8,8 @@ public class Client { - private String name; - private int port; + private final String name; + private final int port; private int ID; private DatagramSocket socket; private InetAddress ip; @@ -22,7 +22,7 @@ public class Client { this.port = port; } - protected boolean openConnection(String address) { + boolean openConnection(String address) { try { socket = new DatagramSocket(); ip = InetAddress.getByName(address); @@ -33,7 +33,7 @@ protected boolean openConnection(String address) { return true; } - protected String receive() { + String receive() { byte[] data = new byte[1024]; DatagramPacket packet = new DatagramPacket(data, data.length); try { @@ -51,12 +51,12 @@ private void send(final byte[] data) { send.start(); } - protected void send(String message) { + void send(String message) { message += "/e/"; send(message.getBytes()); } - protected void disconnect(int id) { + void disconnect(int id) { running = false; //socket.close(); if (kicked || banned) { diff --git a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java index 87f5b24..a277e78 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java @@ -63,13 +63,6 @@ public static ClientGUI getInstance(String name, String address, int port) { return clientGUI; } - { -// GUI initializer generated by IntelliJ IDEA GUI Designer -// >>> IMPORTANT!! <<< -// DO NOT EDIT OR ADD ANY CODE HERE! - $$$setupUI$$$(); - } - public JFrame getFrame() { return frame; } @@ -161,6 +154,13 @@ public void windowClosing(WindowEvent e) { frame.setVisible(true); } + { +// GUI initializer generated by IntelliJ IDEA GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + /** * Method generated by IntelliJ IDEA GUI Designer * >>> IMPORTANT!! <<< diff --git a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java index 03e6711..995efbb 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java @@ -25,7 +25,7 @@ public abstract class ClientInterface implements Runnable { protected abstract void onBreak(); - protected void init() { + void init() { client = new Client(name, port); client.setRunning(true); @@ -114,7 +114,7 @@ public String[] getUserArr() { return userArr; } - public String getName() { + String getName() { return name; } @@ -122,7 +122,7 @@ public void setName(String name) { this.name = name; } - public String getAddress() { + String getAddress() { return address; } @@ -130,7 +130,7 @@ public void setAddress(String address) { this.address = address; } - public int getPort() { + int getPort() { return port; } diff --git a/src/main/java/com/fastchat/FastChat/client/LoginGUI.form b/src/main/java/com/fastchat/FastChat/client/LoginGUI.form index ba692a4..941a483 100644 --- a/src/main/java/com/fastchat/FastChat/client/LoginGUI.form +++ b/src/main/java/com/fastchat/FastChat/client/LoginGUI.form @@ -39,6 +39,7 @@ + @@ -60,6 +61,7 @@ + @@ -80,6 +82,7 @@ + diff --git a/src/main/java/com/fastchat/FastChat/client/LoginGUI.java b/src/main/java/com/fastchat/FastChat/client/LoginGUI.java index 75e1942..440f0bd 100644 --- a/src/main/java/com/fastchat/FastChat/client/LoginGUI.java +++ b/src/main/java/com/fastchat/FastChat/client/LoginGUI.java @@ -26,13 +26,6 @@ public LoginGUI() { init(); } - { -// GUI initializer generated by IntelliJ IDEA GUI Designer -// >>> IMPORTANT!! <<< -// DO NOT EDIT OR ADD ANY CODE HERE! - $$$setupUI$$$(); - } - @Override protected void submit(String name, String address, int port) { clientInterface = ClientGUI.getInstance(name, address, port); @@ -46,6 +39,13 @@ public void reset() { txtAddress.requestFocusInWindow(); } + { +// GUI initializer generated by IntelliJ IDEA GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + private void init() { frame = new JFrame(title); frame.setContentPane(loginPanel); @@ -71,6 +71,7 @@ private void init() { lblAddress.setText(Localization.get("login_lbl_address") + ":"); lblPort.setText(Localization.get("login_lbl_port") + ":"); submitButton.setText(Localization.get("login_submit")); + submitButton.setMnemonic(Localization.get("login_submit").charAt(0)); txtName.setText("Anon"); txtAddress.setText("localhost"); @@ -137,6 +138,9 @@ private void init() { final JSeparator separator5 = new JSeparator(); separator5.setVisible(false); loginPanel.add(separator5, cc.xy(1, 23, CellConstraints.FILL, CellConstraints.FILL)); + lblName.setLabelFor(txtName); + lblAddress.setLabelFor(txtAddress); + lblPort.setLabelFor(txtPort); } /** diff --git a/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java b/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java index 1b431f5..1dafcdd 100644 --- a/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java +++ b/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java @@ -9,8 +9,8 @@ public class OnlineUsers extends JFrame { private static final long serialVersionUID = -3779778679932068169L; - private JPanel contentPane; - private JList list; + private final JPanel contentPane; + private final JList list; OnlineUsers() { setType(Type.UTILITY); diff --git a/src/main/java/com/fastchat/FastChat/server/Server.java b/src/main/java/com/fastchat/FastChat/server/Server.java index 03b9718..7ff8acb 100644 --- a/src/main/java/com/fastchat/FastChat/server/Server.java +++ b/src/main/java/com/fastchat/FastChat/server/Server.java @@ -11,14 +11,14 @@ public class Server implements Runnable { private final int MAX_ATTEMPTS = 4; - private List clients = new ArrayList<>(); + private final List clients = new ArrayList<>(); - private CommandRegistry commandRegistry = new CommandRegistry(); + private final CommandRegistry commandRegistry = new CommandRegistry(); private DatagramSocket socket; - private int port; - private ArrayList response = new ArrayList<>(); + private final int port; + private final ArrayList response = new ArrayList<>(); private boolean running; private boolean raw; @SuppressWarnings("FieldCanBeLocal") @@ -74,12 +74,7 @@ public void run() { } /** - * example: "kick 12345432 2345 3456 3456 34567 34567" - * example - split: ["kick", "12345432", "2345", "3456", "3456", "34567", "34567"] - * example - command name: "kick" - * example - argv: ["12345432", "2345", "3456", "3456", "34567", "34567"] - * - * @param text + * @param text command name with arguments */ private void command(String text) { String[] args = text.split(" "); diff --git a/src/main/java/com/fastchat/FastChat/server/ServerClient.java b/src/main/java/com/fastchat/FastChat/server/ServerClient.java index 310be52..2cc782e 100644 --- a/src/main/java/com/fastchat/FastChat/server/ServerClient.java +++ b/src/main/java/com/fastchat/FastChat/server/ServerClient.java @@ -4,14 +4,14 @@ import java.util.ArrayList; import java.util.Random; -public class ServerClient { - - public String name; - public InetAddress ip; - public int port; +class ServerClient { + + private static final ArrayList identifiers = new ArrayList<>(); + public final String name; + public final InetAddress ip; private final int ID; public int attempt = 0; - private static ArrayList identifiers = new ArrayList<>(); + public final int port; public int getID() { return ID; @@ -39,7 +39,7 @@ public static void removeId(int id) { for (Integer integer : identifiers) { if (integer.equals(id)) { identifiers.remove(integer); - return; + break; } } } diff --git a/src/main/java/com/fastchat/FastChat/util/Command.java b/src/main/java/com/fastchat/FastChat/util/Command.java index 307320b..288e3b9 100644 --- a/src/main/java/com/fastchat/FastChat/util/Command.java +++ b/src/main/java/com/fastchat/FastChat/util/Command.java @@ -2,11 +2,11 @@ public class Command { - private String help; - private String name; + private final String help; + private final String name; //it's Interface created for body - EnhancedCallable body; + private final EnhancedCallable body; public EnhancedCallable getBody() { diff --git a/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java b/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java index d045b7d..17b7468 100644 --- a/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java +++ b/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java @@ -3,7 +3,7 @@ import java.util.HashMap; public class CommandRegistry { - private HashMap commands = new HashMap<>(); + private final HashMap commands = new HashMap<>(); public void add(Command command){ diff --git a/src/main/java/com/fastchat/FastChat/util/Localization.java b/src/main/java/com/fastchat/FastChat/util/Localization.java index 99b91a2..cb16730 100644 --- a/src/main/java/com/fastchat/FastChat/util/Localization.java +++ b/src/main/java/com/fastchat/FastChat/util/Localization.java @@ -41,7 +41,7 @@ public static void autoSetLocale() { setLocale(new Locale(language)); } - public static void setLocale(Locale locale) { + private static void setLocale(Locale locale) { switch (locale.getLanguage()) { case "uk": setBundle("ukrainian"); diff --git a/src/main/java/com/fastchat/FastChat/util/UTF8Control.java b/src/main/java/com/fastchat/FastChat/util/UTF8Control.java index 55a3f63..14fa159 100644 --- a/src/main/java/com/fastchat/FastChat/util/UTF8Control.java +++ b/src/main/java/com/fastchat/FastChat/util/UTF8Control.java @@ -9,7 +9,7 @@ import java.util.PropertyResourceBundle; import java.util.ResourceBundle; -public class UTF8Control extends ResourceBundle.Control { +class UTF8Control extends ResourceBundle.Control { public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IOException { diff --git a/src/test/java/Utils/CommandRegistry.java b/src/test/java/Utils/CommandRegistry.java deleted file mode 100644 index dc604f6..0000000 --- a/src/test/java/Utils/CommandRegistry.java +++ /dev/null @@ -1,7 +0,0 @@ -package Utils; -import org.testng.annotations.Test; - -import static org.testng.Assert.assertTrue; - -public class CommandRegistry { -} diff --git a/src/test/java/util/CommandRegistry.java b/src/test/java/util/CommandRegistry.java new file mode 100644 index 0000000..cd9f788 --- /dev/null +++ b/src/test/java/util/CommandRegistry.java @@ -0,0 +1,4 @@ +package util; + +class CommandRegistry { +} diff --git a/src/test/java/Utils/CommandTest.java b/src/test/java/util/CommandTest.java similarity index 67% rename from src/test/java/Utils/CommandTest.java rename to src/test/java/util/CommandTest.java index fb6f63d..ecbfd49 100644 --- a/src/test/java/Utils/CommandTest.java +++ b/src/test/java/util/CommandTest.java @@ -1,30 +1,30 @@ -package Utils; +package util; + import com.fastchat.FastChat.util.Command; import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; public class CommandTest { - Command command = new Command("help","", (argv) -> { + private final Command command = new Command("help", "", (argv) -> { System.out.print("Do you really need help? "); return null; }); @Test - public void CommandNotNull(){ + public void CommandNotNull() { assertNotNull(command.getName()); assertNotNull(command.getHelp()); assertNotNull(command.getBody()); } @Test - public void CommandEquals(){ + public void CommandEquals() { assertEquals("help", command.getName()); - assertEquals("",command.getHelp()); + assertEquals("", command.getHelp()); } } From 292c4de8631fcf6322b69aa2e33c13e4562ec4a2 Mon Sep 17 00:00:00 2001 From: OHopiak Date: Thu, 15 Mar 2018 15:32:02 +0200 Subject: [PATCH 30/37] Added TODOs --- .../java/com/fastchat/FastChat/Loader.java | 23 +++++++++++++++++++ .../fastchat/FastChat/client/ClientGUI.form | 4 +++- .../fastchat/FastChat/client/ClientGUI.java | 1 + .../fastchat/FastChat/client/LoginGUI.java | 14 +++++------ 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/fastchat/FastChat/Loader.java b/src/main/java/com/fastchat/FastChat/Loader.java index d3188f0..0ec18b2 100644 --- a/src/main/java/com/fastchat/FastChat/Loader.java +++ b/src/main/java/com/fastchat/FastChat/Loader.java @@ -26,6 +26,29 @@ private static void help() { System.out.println(helpString); } + /** + * Backend TODOs + * TODO: Text Box for messages instead of Text Field + * TODO: Limit width of message history Text Area + * TODO: Show messages in separate boxes (or at least in different colors per user) + * TODO: Group networking in a protocol + * TODO: Add settings frame + * TODO: Add sound to play when message comes + * TODO: Make a single class (JavaBean) for User + * TODO: Move networking from UDP to TCP + * TODO: Make tests for existing classes + * FIXME: Add cyrillic support in Windows + * FIXME: Add either message limit, or enable sending messages of any length + *

+ * Frontend TODOs: + * TODO: Pick main color scheme + * TODO: Make a dark and light theme + * TODO: (optional) use Material UI + * TODO: Reshape all components + * TODO: Add custom font (must support any languages) + * + * @param args arguments of application + */ public static void main(String[] args) { Localization.autoSetLocale(); //English diff --git a/src/main/java/com/fastchat/FastChat/client/ClientGUI.form b/src/main/java/com/fastchat/FastChat/client/ClientGUI.form index 189dc30..9e69bfc 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientGUI.form +++ b/src/main/java/com/fastchat/FastChat/client/ClientGUI.form @@ -41,7 +41,9 @@ - + + + diff --git a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java index a277e78..fa2871a 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java @@ -196,6 +196,7 @@ public void windowClosing(WindowEvent e) { gbc.fill = GridBagConstraints.HORIZONTAL; clientPanel.add(messagePrompt, gbc); sendButton = new JButton(); + sendButton.setText("Submit"); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 1; diff --git a/src/main/java/com/fastchat/FastChat/client/LoginGUI.java b/src/main/java/com/fastchat/FastChat/client/LoginGUI.java index 440f0bd..f8e0b20 100644 --- a/src/main/java/com/fastchat/FastChat/client/LoginGUI.java +++ b/src/main/java/com/fastchat/FastChat/client/LoginGUI.java @@ -39,13 +39,6 @@ public void reset() { txtAddress.requestFocusInWindow(); } - { -// GUI initializer generated by IntelliJ IDEA GUI Designer -// >>> IMPORTANT!! <<< -// DO NOT EDIT OR ADD ANY CODE HERE! - $$$setupUI$$$(); - } - private void init() { frame = new JFrame(title); frame.setContentPane(loginPanel); @@ -88,6 +81,13 @@ private void init() { frame.setVisible(true); } + { +// GUI initializer generated by IntelliJ IDEA GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + /** * Method generated by IntelliJ IDEA GUI Designer * >>> IMPORTANT!! <<< From 18b606cdf914a385b4be671a18069b03b37079a2 Mon Sep 17 00:00:00 2001 From: OHopiak Date: Thu, 15 Mar 2018 15:46:00 +0200 Subject: [PATCH 31/37] fix for TODOs --- src/main/java/com/fastchat/FastChat/Loader.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/fastchat/FastChat/Loader.java b/src/main/java/com/fastchat/FastChat/Loader.java index 0ec18b2..71cb381 100644 --- a/src/main/java/com/fastchat/FastChat/Loader.java +++ b/src/main/java/com/fastchat/FastChat/Loader.java @@ -27,6 +27,7 @@ private static void help() { } /** + *

 	 * Backend TODOs
 	 * TODO: Text Box for messages instead of Text Field
 	 * TODO: Limit width of message history Text Area
@@ -39,13 +40,14 @@ private static void help() {
 	 * TODO: Make tests for existing classes
 	 * FIXME: Add cyrillic support in Windows
 	 * FIXME: Add either message limit, or enable sending messages of any length
-	 * 

+ * * Frontend TODOs: * TODO: Pick main color scheme * TODO: Make a dark and light theme * TODO: (optional) use Material UI * TODO: Reshape all components * TODO: Add custom font (must support any languages) + *

* * @param args arguments of application */ From 2abc083dc66027c90e9952a0a22f627acc5c111b Mon Sep 17 00:00:00 2001 From: OHopiak Date: Sun, 1 Apr 2018 02:20:05 +0300 Subject: [PATCH 32/37] added a NetworkCommand class and Registry for it --- build.gradle | 1 + .../fastchat/FastChat/beans/package-info.java | 1 + .../com/fastchat/FastChat/client/Client.java | 2 +- .../FastChat/client/package-info.java | 1 + .../FastChat/networking/NetworkCommand.java | 32 +++ .../networking/NetworkCommandRegistry.java | 25 ++ .../{util => networking}/Networking.java | 2 +- .../FastChat/networking/Protocol.java | 4 + .../FastChat/networking/package-info.java | 4 + .../com/fastchat/FastChat/package-info.java | 1 + .../com/fastchat/FastChat/server/Server.java | 231 +++++++++++------- .../FastChat/server/package-info.java | 1 + .../com/fastchat/FastChat/util/Command.java | 3 - .../FastChat/util/CommandRegistry.java | 16 +- .../FastChat/util/EnhancedRunnable.java | 10 + .../fastchat/FastChat/util/package-info.java | 1 + src/test/java/util/CommandRegistry.java | 4 - src/test/java/util/CommandRegistryTest.java | 4 + src/test/java/util/CommandTest.java | 47 +++- 19 files changed, 280 insertions(+), 110 deletions(-) create mode 100644 src/main/java/com/fastchat/FastChat/beans/package-info.java create mode 100644 src/main/java/com/fastchat/FastChat/client/package-info.java create mode 100644 src/main/java/com/fastchat/FastChat/networking/NetworkCommand.java create mode 100644 src/main/java/com/fastchat/FastChat/networking/NetworkCommandRegistry.java rename src/main/java/com/fastchat/FastChat/{util => networking}/Networking.java (92%) create mode 100644 src/main/java/com/fastchat/FastChat/networking/Protocol.java create mode 100644 src/main/java/com/fastchat/FastChat/networking/package-info.java create mode 100644 src/main/java/com/fastchat/FastChat/package-info.java create mode 100644 src/main/java/com/fastchat/FastChat/server/package-info.java create mode 100644 src/main/java/com/fastchat/FastChat/util/EnhancedRunnable.java create mode 100644 src/main/java/com/fastchat/FastChat/util/package-info.java delete mode 100644 src/test/java/util/CommandRegistry.java create mode 100644 src/test/java/util/CommandRegistryTest.java diff --git a/build.gradle b/build.gradle index 35b9b6b..df3981e 100644 --- a/build.gradle +++ b/build.gradle @@ -41,6 +41,7 @@ dependencies { libraries 'com.jgoodies:jgoodies-forms:1.6.0' // libraries 'com.intellij:forms_rt:7.0.3' testCompile 'org.testng:testng:6.9.12' + testCompile 'com.google.guava:guava:19.0' configurations.compile.extendsFrom(configurations.libraries) } diff --git a/src/main/java/com/fastchat/FastChat/beans/package-info.java b/src/main/java/com/fastchat/FastChat/beans/package-info.java new file mode 100644 index 0000000..6c986fa --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/beans/package-info.java @@ -0,0 +1 @@ +package com.fastchat.FastChat.beans; \ No newline at end of file diff --git a/src/main/java/com/fastchat/FastChat/client/Client.java b/src/main/java/com/fastchat/FastChat/client/Client.java index bc7b776..7ddf236 100644 --- a/src/main/java/com/fastchat/FastChat/client/Client.java +++ b/src/main/java/com/fastchat/FastChat/client/Client.java @@ -1,6 +1,6 @@ package com.fastchat.FastChat.client; -import com.fastchat.FastChat.util.Networking; +import com.fastchat.FastChat.networking.Networking; import javax.swing.*; import java.io.IOException; diff --git a/src/main/java/com/fastchat/FastChat/client/package-info.java b/src/main/java/com/fastchat/FastChat/client/package-info.java new file mode 100644 index 0000000..7aa7cbb --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/client/package-info.java @@ -0,0 +1 @@ +package com.fastchat.FastChat.client; \ No newline at end of file diff --git a/src/main/java/com/fastchat/FastChat/networking/NetworkCommand.java b/src/main/java/com/fastchat/FastChat/networking/NetworkCommand.java new file mode 100644 index 0000000..9463afa --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/networking/NetworkCommand.java @@ -0,0 +1,32 @@ +package com.fastchat.FastChat.networking; + +import com.fastchat.FastChat.util.EnhancedRunnable; + +public class NetworkCommand { + + private String name; + private String shortcut; + private EnhancedRunnable process; + + public NetworkCommand(String name, String shortcut, EnhancedRunnable process) { + this.name = name; + this.shortcut = shortcut; + this.process = process; + } + + public void exec(String... args) throws RuntimeException { + try { + process.run(args); + } catch (Exception e) { + throw new RuntimeException("Command '" + name + "' exited with error: " + e.getMessage()); + } + } + + public String getShortcut() { + return shortcut; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/com/fastchat/FastChat/networking/NetworkCommandRegistry.java b/src/main/java/com/fastchat/FastChat/networking/NetworkCommandRegistry.java new file mode 100644 index 0000000..7e2eaf6 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/networking/NetworkCommandRegistry.java @@ -0,0 +1,25 @@ +package com.fastchat.FastChat.networking; + +import java.util.HashMap; + +public class NetworkCommandRegistry { + + private HashMap registry = new HashMap<>(); + + /** + * @param commands + */ + public void add(NetworkCommand... commands) { + for (NetworkCommand command : commands) { + registry.put(command.getName(), command); + } + } + + /** + * @param shortcut + * @return + */ + public NetworkCommand get(String shortcut) { + return registry.getOrDefault(shortcut, null); + } +} diff --git a/src/main/java/com/fastchat/FastChat/util/Networking.java b/src/main/java/com/fastchat/FastChat/networking/Networking.java similarity index 92% rename from src/main/java/com/fastchat/FastChat/util/Networking.java rename to src/main/java/com/fastchat/FastChat/networking/Networking.java index 8db0c35..ef9abbc 100644 --- a/src/main/java/com/fastchat/FastChat/util/Networking.java +++ b/src/main/java/com/fastchat/FastChat/networking/Networking.java @@ -1,4 +1,4 @@ -package com.fastchat.FastChat.util; +package com.fastchat.FastChat.networking; import java.io.IOException; import java.net.DatagramPacket; diff --git a/src/main/java/com/fastchat/FastChat/networking/Protocol.java b/src/main/java/com/fastchat/FastChat/networking/Protocol.java new file mode 100644 index 0000000..e62eb5b --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/networking/Protocol.java @@ -0,0 +1,4 @@ +package com.fastchat.FastChat.networking; + +public class Protocol { +} diff --git a/src/main/java/com/fastchat/FastChat/networking/package-info.java b/src/main/java/com/fastchat/FastChat/networking/package-info.java new file mode 100644 index 0000000..3e87568 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/networking/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains all functions that are used for network communication + */ +package com.fastchat.FastChat.networking; \ No newline at end of file diff --git a/src/main/java/com/fastchat/FastChat/package-info.java b/src/main/java/com/fastchat/FastChat/package-info.java new file mode 100644 index 0000000..3547d79 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/package-info.java @@ -0,0 +1 @@ +package com.fastchat.FastChat; \ No newline at end of file diff --git a/src/main/java/com/fastchat/FastChat/server/Server.java b/src/main/java/com/fastchat/FastChat/server/Server.java index 7ff8acb..409b3cd 100644 --- a/src/main/java/com/fastchat/FastChat/server/Server.java +++ b/src/main/java/com/fastchat/FastChat/server/Server.java @@ -1,8 +1,10 @@ package com.fastchat.FastChat.server; +import com.fastchat.FastChat.networking.NetworkCommand; +import com.fastchat.FastChat.networking.NetworkCommandRegistry; +import com.fastchat.FastChat.networking.Networking; import com.fastchat.FastChat.util.Command; import com.fastchat.FastChat.util.CommandRegistry; -import com.fastchat.FastChat.util.Networking; import java.io.IOException; import java.net.*; @@ -11,9 +13,10 @@ public class Server implements Runnable { private final int MAX_ATTEMPTS = 4; - private final List clients = new ArrayList<>(); + private final List clients = Collections.synchronizedList(new ArrayList<>()); private final CommandRegistry commandRegistry = new CommandRegistry(); + private final NetworkCommandRegistry networkCommandRegistry = new NetworkCommandRegistry(); private DatagramSocket socket; @@ -28,6 +31,10 @@ private enum Status { DISCONNECTED, TIMED_OUT, KICKED, BANNED } + /** + * @param port + * @param address + */ public Server(int port, String address) { System.out.printf("Server started at %s:%d\n", address, port); this.port = port; @@ -43,6 +50,9 @@ public Server(int port, String address) { run.start(); } + /** + * + */ public void run() { running = true; manageClients(); @@ -84,6 +94,9 @@ private void command(String text) { } + /** + * + */ private void manageClients() { manage = new Thread("Manage") { public void run() { @@ -112,34 +125,22 @@ public void run() { manage.start(); } + /** + * @param packet + */ private void process(DatagramPacket packet) { String string = new String(packet.getData()); string = string.split("/e/")[0]; + if (raw) System.out.println(string); - if (string.startsWith("/c/")) { - String name = string.substring(3); - ServerClient client = new ServerClient(name, packet.getAddress(), packet.getPort()); - clients.add(client); - System.out.println(client + " has connected"); - sendToAll(client.name + "(" + client.getID() + ") has connected"); - send("/c/" + client.getID(), client.ip, client.port); - } else if (string.startsWith("/m/")) { - System.out.println(string); - sendToAll(string); - } else if (string.startsWith("/d/")) { - int id = Integer.parseInt(string.substring(3)); - disconnectClient(id, Status.DISCONNECTED); - } else if (string.startsWith("/i/")) { - response.add(Integer.parseInt(string.substring(3))); - } else if (string.startsWith("/u/")) { - StringBuilder message = new StringBuilder("/u/"); - for (ServerClient client : clients) { - message.append(client.name).append("(").append(client.getID()).append(")//"); - } - send(message.toString(), packet.getAddress(), packet.getPort()); - } else { + + NetworkCommand cmd = networkCommandRegistry.get(string.charAt(1) + ""); + + //FIXME hardcoded crappy if statement should be replaced + if (cmd == null || string.length() < 3 || string.charAt(0) != '/' || string.charAt(2) != '/') System.out.println(string); - } + else + cmd.exec(string, packet.getAddress().getHostAddress(), Integer.toString(packet.getPort())); } private void receive() { @@ -210,72 +211,128 @@ public int getPort() { private void initCommands() { - this.commandRegistry.add(new Command("exit", (argv) -> { - System.out.print("Do you really want to exit? (y/n) "); - Scanner input = new Scanner(System.in); - char option = input.nextLine().toLowerCase().charAt(0); - if (option == 'y') { - System.exit(0); - } - return null; - })); - - this.commandRegistry.add(new Command("help", (argv) -> { - System.out.println("Available commands: "); - commandRegistry.getCommands().forEach((name, c) -> - System.out.println("/" + name + (c.getHelp().isEmpty() ? "" : " - " + c.getHelp())) - ); - return null; - })); - - this.commandRegistry.add(new Command("clients", "prints the list of clients", (argv) -> { - System.out.println("Clients:"); - System.out.println("==================================="); - for (ServerClient serverClient : clients) { - System.out.println(serverClient); - } - System.out.println("==================================="); - return null; - })); - - this.commandRegistry.add(new Command("kick", "kick the list of clients", (argv) -> { - for (String name : argv) { + commandRegistry.add( + new Command("exit", (argv) -> { + System.out.print("Do you really want to exit? (y/n) "); + Scanner input = new Scanner(System.in); + char option = input.nextLine().toLowerCase().charAt(0); + if (option == 'y') { + System.exit(0); + } + return null; + }), + new Command("help", (argv) -> { + System.out.println("Available commands: "); + commandRegistry.getRegistry().forEach((name, c) -> + System.out.println("/" + name + (c.getHelp().isEmpty() ? "" : " - " + c.getHelp())) + ); + return null; + }), + new Command("clients", "prints the list of clients", (argv) -> { + System.out.println("Clients:"); + System.out.println("==================================="); + for (ServerClient serverClient : clients) { + System.out.println(serverClient); + } + System.out.println("==================================="); + return null; + }), + new Command("kick", "kick the list of clients", (argv) -> { + for (String name : argv) { - int id = -1; - boolean number = true; - try { - id = Integer.parseInt(name); - } catch (NumberFormatException e) { - number = false; - } - if (number) { - boolean exists = false; - for (ServerClient client : clients) { - if (client.getID() == id) { - exists = true; - break; + int id = -1; + boolean number = true; + try { + id = Integer.parseInt(name); + } catch (NumberFormatException e) { + number = false; } - } - if (exists) - disconnectClient(id, Status.KICKED); - else - System.out.println("Client with id " + id + " does not exist"); - } else { - for (ServerClient client : clients) { - if (name.equals(client.name)) { - disconnectClient(client.getID(), Status.KICKED); - break; + if (number) { + boolean exists = false; + for (ServerClient client : clients) { + if (client.getID() == id) { + exists = true; + break; + } + } + if (exists) + disconnectClient(id, Status.KICKED); + else + System.out.println("Client with id " + id + " does not exist"); + } else { + for (ServerClient client : clients) { + if (name.equals(client.name)) { + disconnectClient(client.getID(), Status.KICKED); + break; + } + } } } - } - } - return null; - })); + return null; + }), + new Command("raw", "", (argv) -> { + raw = !raw; + System.out.println((raw ? "Raw mode enabled" : "Raw mode disabled")); + return null; + }) + ); - this.commandRegistry.add(new Command("raw", "", (argv) -> { - raw = !raw; - System.out.println((raw ? "Raw mode enabled" : "Raw mode disabled")); - return null; - })); + networkCommandRegistry.add( + new NetworkCommand("ping", "i", (args) -> { + if (args.length <= 0) return; + String data = args[0]; + response.add(Integer.parseInt(data.substring(3))); + }), + new NetworkCommand("connect", "c", (args) -> { + if (args.length < 3) return; + String data = args[0]; + InetAddress address; + try { + address = InetAddress.getByAddress(args[1].getBytes()); + } catch (UnknownHostException e) { + System.err.println("Couldn't send connection confirm to " + args[1]); + return; + } + int port = Integer.parseInt(args[2]); + String name = data.substring(3); + ServerClient client = new ServerClient(name, address, port); + synchronized (clients) { + clients.add(client); + } + System.out.println(client + " has connected"); + sendToAll(client.name + "(" + client.getID() + ") has connected"); + send("/c/" + client.getID(), client.ip, client.port); + }), + new NetworkCommand("message", "m", (args) -> { + if (args.length <= 0) return; + String data = args[0]; + System.out.println(data); + sendToAll(data); + }), + new NetworkCommand("disconnect", "d", (args) -> { + if (args.length <= 0) return; + String data = args[0]; + int id = Integer.parseInt(data.substring(3)); + synchronized (clients) { + disconnectClient(id, Status.DISCONNECTED); + } + }), + new NetworkCommand("users", "u", (args) -> { + if (args.length < 3) return; + InetAddress address; + try { + address = InetAddress.getByAddress(args[1].getBytes()); + } catch (UnknownHostException e) { + System.err.println("Couldn't send user list to " + args[1]); + return; + } + int port = Integer.parseInt(args[2]); + StringBuilder message = new StringBuilder("/u/"); + for (ServerClient client : clients) { + message.append(client.name).append("(").append(client.getID()).append(")//"); + } + send(message.toString(), address, port); + }) + ); } } diff --git a/src/main/java/com/fastchat/FastChat/server/package-info.java b/src/main/java/com/fastchat/FastChat/server/package-info.java new file mode 100644 index 0000000..29fb349 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/server/package-info.java @@ -0,0 +1 @@ +package com.fastchat.FastChat.server; \ No newline at end of file diff --git a/src/main/java/com/fastchat/FastChat/util/Command.java b/src/main/java/com/fastchat/FastChat/util/Command.java index 288e3b9..58535da 100644 --- a/src/main/java/com/fastchat/FastChat/util/Command.java +++ b/src/main/java/com/fastchat/FastChat/util/Command.java @@ -14,9 +14,7 @@ public EnhancedCallable getBody() { } - /** - * * @param name a name of the command * @param body body of the command */ @@ -31,7 +29,6 @@ public Command(String name, String help, EnhancedCallable body) } /** - * * @param args command arguments */ public void exec(String... args) { diff --git a/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java b/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java index 17b7468..5e3a221 100644 --- a/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java +++ b/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java @@ -3,15 +3,15 @@ import java.util.HashMap; public class CommandRegistry { - private final HashMap commands = new HashMap<>(); + private final HashMap registry = new HashMap<>(); - - public void add(Command command){ - commands.put(command.getName(),command); + public void add(Command... commands) { + for (Command command : commands) + registry.put(command.getName(), command); } - public void run(String name, String... args){ - Command c = commands.get(name); + public void run(String name, String... args) { + Command c = registry.get(name); if (c != null) { c.exec(args); @@ -20,7 +20,7 @@ public void run(String name, String... args){ } } - public HashMap getCommands() { - return commands; + public HashMap getRegistry() { + return registry; } } diff --git a/src/main/java/com/fastchat/FastChat/util/EnhancedRunnable.java b/src/main/java/com/fastchat/FastChat/util/EnhancedRunnable.java new file mode 100644 index 0000000..a486337 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/util/EnhancedRunnable.java @@ -0,0 +1,10 @@ +package com.fastchat.FastChat.util; + +/** + * @param
arguments to be put as varargs + */ +@FunctionalInterface +public interface EnhancedRunnable { + void run(A... args); +} + diff --git a/src/main/java/com/fastchat/FastChat/util/package-info.java b/src/main/java/com/fastchat/FastChat/util/package-info.java new file mode 100644 index 0000000..7117c08 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/util/package-info.java @@ -0,0 +1 @@ +package com.fastchat.FastChat.util; \ No newline at end of file diff --git a/src/test/java/util/CommandRegistry.java b/src/test/java/util/CommandRegistry.java deleted file mode 100644 index cd9f788..0000000 --- a/src/test/java/util/CommandRegistry.java +++ /dev/null @@ -1,4 +0,0 @@ -package util; - -class CommandRegistry { -} diff --git a/src/test/java/util/CommandRegistryTest.java b/src/test/java/util/CommandRegistryTest.java new file mode 100644 index 0000000..f18e2c1 --- /dev/null +++ b/src/test/java/util/CommandRegistryTest.java @@ -0,0 +1,4 @@ +package util; + +class CommandRegistryTest { +} diff --git a/src/test/java/util/CommandTest.java b/src/test/java/util/CommandTest.java index ecbfd49..2b50dc2 100644 --- a/src/test/java/util/CommandTest.java +++ b/src/test/java/util/CommandTest.java @@ -1,17 +1,19 @@ package util; import com.fastchat.FastChat.util.Command; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.testng.Assert.*; public class CommandTest { private final Command command = new Command("help", "", (argv) -> { System.out.print("Do you really need help? "); - return null; + return "Do you really need help? "; }); @Test @@ -21,10 +23,43 @@ public void CommandNotNull() { assertNotNull(command.getBody()); } + @Test(dataProvider = "names", dataProviderClass = CommandTestData.class) + public void TestCommandName(String name, Command command) { + assertEquals(name, command.getName()); + } + @Test - public void CommandEquals() { - assertEquals("help", command.getName()); - assertEquals("", command.getHelp()); + public void CommandExecutedCorrectly() { + AtomicBoolean variable = new AtomicBoolean(false); + Command changesTheVariable = new Command("change_var", args -> { + variable.set(true); + return null; + }); + + assertFalse(variable.get()); + changesTheVariable.exec(); + assertTrue(variable.get()); + } + +/* + //Runs all tests in this class + public static void main (String [] arg) { + ITestNGListener tla = new TestListenerAdapter(); + TestNG testng = new TestNG(); + testng.setTestClasses(new Class[] { CommandTest.class }); + testng.addListener(tla); + testng.setVerbose(2); + testng.run(); + } +*/ +} +class CommandTestData { + @DataProvider(name = "names") + public static Object[][] testCmdName() { + return new Object[][]{ + {"help", new Command("help", "", null)}, + {"", new Command("", "", null)} + }; } } From 3d53c3e3d4675c60a2588c9e735534afac1254fc Mon Sep 17 00:00:00 2001 From: OHopiak Date: Sun, 1 Apr 2018 17:06:28 +0300 Subject: [PATCH 33/37] Refactored hardcoded network connections --- .gitignore | 4 + .travis.yml | 5 + FastChat.uml | 111 ++++-------------- .../com/fastchat/FastChat/client/Client.java | 37 ++---- .../fastchat/FastChat/client/ClientGUI.java | 5 +- .../FastChat/client/ClientInterface.java | 105 ++++++++--------- .../FastChat/networking/NetworkCommand.java | 15 ++- .../networking/NetworkCommandRegistry.java | 14 ++- .../FastChat/networking/Networking.java | 21 ---- .../FastChat/networking/Protocol.java | 98 ++++++++++++++++ .../FastChat/networking/ProtocolCommands.java | 59 ++++++++++ .../FastChat/networking/package-info.java | 1 + .../com/fastchat/FastChat/server/Server.java | 111 +++++++----------- 13 files changed, 325 insertions(+), 261 deletions(-) delete mode 100644 src/main/java/com/fastchat/FastChat/networking/Networking.java create mode 100644 src/main/java/com/fastchat/FastChat/networking/ProtocolCommands.java diff --git a/.gitignore b/.gitignore index dfefdf2..9899293 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ gradlew gradlew.bat build .gradletasknamecache + +#TestNG +test-output + diff --git a/.travis.yml b/.travis.yml index 444b90a..5275048 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,5 +5,10 @@ cache: gradle jdk: - oraclejdk8 +script: + - gradle assemble + - gradle check jacocoTestReport + - gradle build javadoc + after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/FastChat.uml b/FastChat.uml index 50a1500..52cd288 100644 --- a/FastChat.uml +++ b/FastChat.uml @@ -3,100 +3,39 @@ JAVA - com.fastchat.FastChat.Loader - com.fastchat.FastChat.client.ClientGUI - com.fastchat.FastChat.client.LoginInterface - com.fastchat.FastChat.client.LoginGUI - com.fastchat.FastChat.client.Client - com.fastchat.FastChat.server.Server.Status - com.fastchat.FastChat.server.Server - com.fastchat.FastChat.client.OnlineUsers - com.fastchat.FastChat.server.ServerClient - com.fastchat.FastChat.client.ClientInterface + com.fastchat.FastChat.Loader + com.fastchat.FastChat.client + com.fastchat.FastChat.util + com.fastchat.FastChat.networking + com.fastchat.FastChat.server + com.fastchat.FastChat.beans - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + - com.fastchat.FastChat.Loader + com.fastchat.FastChat.server - Inner Classes Methods All diff --git a/src/main/java/com/fastchat/FastChat/client/Client.java b/src/main/java/com/fastchat/FastChat/client/Client.java index 7ddf236..2fa6a36 100644 --- a/src/main/java/com/fastchat/FastChat/client/Client.java +++ b/src/main/java/com/fastchat/FastChat/client/Client.java @@ -1,10 +1,13 @@ package com.fastchat.FastChat.client; -import com.fastchat.FastChat.networking.Networking; +import com.fastchat.FastChat.networking.Protocol; +import com.fastchat.FastChat.networking.ProtocolCommands; import javax.swing.*; -import java.io.IOException; -import java.net.*; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; public class Client { @@ -13,7 +16,6 @@ public class Client { private int ID; private DatagramSocket socket; private InetAddress ip; - private Thread send; private boolean running = false, kicked = false, banned = false; Client(String name, int port) { @@ -33,27 +35,8 @@ boolean openConnection(String address) { return true; } - String receive() { - byte[] data = new byte[1024]; - DatagramPacket packet = new DatagramPacket(data, data.length); - try { - socket.receive(packet); - } catch (IOException e) { - e.printStackTrace(); - } - String message = new String(packet.getData()); - message = message.split("/e/")[0]; - return message; - } - - private void send(final byte[] data) { - send = Networking.send(socket, data, ip, port); - send.start(); - } - void send(String message) { - message += "/e/"; - send(message.getBytes()); + Protocol.send(socket, message, ip, port).start(); } void disconnect(int id) { @@ -69,7 +52,7 @@ void disconnect(int id) { kicked = false; banned = false; } else { - send("/d/" + id); + send(ProtocolCommands.Disconnect.PREFIX + id); System.exit(0); } } @@ -117,4 +100,8 @@ public boolean isBanned() { public void setBanned(boolean banned) { this.banned = banned; } + + public DatagramSocket getSocket() { + return socket; + } } diff --git a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java index fa2871a..11e5145 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java @@ -1,5 +1,6 @@ package com.fastchat.FastChat.client; +import com.fastchat.FastChat.networking.ProtocolCommands; import com.fastchat.FastChat.util.Localization; import javax.swing.*; @@ -78,7 +79,7 @@ private JMenuBar generateMenuBar() { JMenu menu = new JMenu(Localization.get("menu_bar_menu")); menuBar.add(menu); JMenuItem onlineUsers = new JMenuItem(Localization.get("menu_bar_online_users")); - onlineUsers.addActionListener(event -> client.send("/u/")); + onlineUsers.addActionListener(event -> client.send(ProtocolCommands.Users.PREFIX)); menu.add(onlineUsers); JMenuItem settings = new JMenuItem(Localization.get("menu_bar_settings")); @@ -101,7 +102,7 @@ protected void send(String message) { if (message.equals("")) return; message = client.getName() + ": " + message; messagePrompt.setText(""); - client.send("/m/" + message); + client.send(ProtocolCommands.Message.PREFIX + message); } @Override diff --git a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java index 995efbb..be1c617 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java @@ -1,5 +1,8 @@ package com.fastchat.FastChat.client; +import com.fastchat.FastChat.networking.NetworkCommandRegistry; +import com.fastchat.FastChat.networking.Protocol; +import com.fastchat.FastChat.networking.ProtocolCommands; import com.fastchat.FastChat.util.Localization; public abstract class ClientInterface implements Runnable { @@ -7,9 +10,11 @@ public abstract class ClientInterface implements Runnable { Client client; private OnlineUsers users; private String[] userArr = {"Name(id)"}; - private Thread run, listen; + private final NetworkCommandRegistry networkCommandRegistry = new NetworkCommandRegistry(); private String name, address; private int port; + @SuppressWarnings("FieldCanBeLocal") + private Thread run, listen; ClientInterface() { this("Anon", "localhost", 1234); @@ -20,6 +25,7 @@ public abstract class ClientInterface implements Runnable { this.address = address; this.port = port; + initCommands(); // init(); } @@ -41,7 +47,7 @@ void init() { } else { this.serve(); } - client.send(("/c/" + name)); + client.send((ProtocolCommands.Connect.PREFIX + name)); users = new OnlineUsers(); } @@ -54,38 +60,14 @@ public void run() { } private void listen() { - listen = new Thread("Listen") { - public void run() { - while (client.isRunning()) { - String message = client.receive(); + listen = Protocol.listen(client::isRunning, () -> { + String message = Protocol.parsePacket(Protocol.receive(client.getSocket())); // System.out.println(message); - if (message.startsWith("/c/")) { - client.setID(Integer.parseInt(message.substring(3))); - console("Connection is setted up successfully... ID is " + client.getID()); - } else if (message.startsWith("/m/")) { - console(message.substring(3)); - } else if (message.startsWith("/i/")) { - client.send("/i/" + client.getID()); - } else if (message.startsWith("/d/")) { - message = message.substring(3); - if (message.startsWith("0")) { - client.setKicked(true); - client.disconnect(client.getID()); - } else { - client.setBanned(true); - client.disconnect(client.getID()); - } - } else if (message.startsWith("/u/")) { - message = message.substring(3); - userArr = message.split("//"); - users.updateList(userArr); - users.setVisible(true); - } else { - console("Server: " + message); - } - } - } - }; + Protocol.process(message, networkCommandRegistry, + Protocol.IGNORE, + () -> console("Server: " + message) + ); + }); listen.start(); } @@ -94,24 +76,45 @@ private void serve() { run.start(); } - public Client getClient() { - return client; - } - - public Thread getRun() { - return run; - } - - public Thread getListen() { - return listen; - } - - public OnlineUsers getUsers() { - return users; + private void initCommands() { + networkCommandRegistry.add( + new ProtocolCommands.Ping((args) -> client.send(ProtocolCommands.Ping.PREFIX + client.getID())), + new ProtocolCommands.Connect((args) -> { + if (args.length < 3) return; + String data = args[0]; + client.setID(Integer.parseInt(data.substring(3))); + console("Connection is set up successfully... ID is " + client.getID()); + }), + new ProtocolCommands.Message((args) -> { + if (args.length <= 0) return; + String data = args[0]; + console(data.substring(3)); + }), + new ProtocolCommands.Disconnect((args) -> { + if (args.length <= 0) return; + String data = args[0]; + data = data.substring(3); + if (data.startsWith("0")) { + client.setKicked(true); + client.disconnect(client.getID()); + } else { + client.setBanned(true); + client.disconnect(client.getID()); + } + }), + new ProtocolCommands.Users((args) -> { + if (args.length <= 0) return; + String data = args[0]; + data = data.substring(3); + userArr = data.split("//"); + users.updateList(userArr); + users.setVisible(true); + }) + ); } - public String[] getUserArr() { - return userArr; + public Client getClient() { + return client; } String getName() { @@ -126,10 +129,6 @@ String getAddress() { return address; } - public void setAddress(String address) { - this.address = address; - } - int getPort() { return port; } diff --git a/src/main/java/com/fastchat/FastChat/networking/NetworkCommand.java b/src/main/java/com/fastchat/FastChat/networking/NetworkCommand.java index 9463afa..0da2023 100644 --- a/src/main/java/com/fastchat/FastChat/networking/NetworkCommand.java +++ b/src/main/java/com/fastchat/FastChat/networking/NetworkCommand.java @@ -2,18 +2,31 @@ import com.fastchat.FastChat.util.EnhancedRunnable; +/** + * A command that sends data through network + */ public class NetworkCommand { private String name; private String shortcut; private EnhancedRunnable process; - public NetworkCommand(String name, String shortcut, EnhancedRunnable process) { + /** + * @param name to describe the command + * @param shortcut that is used in networking and as a key in registry + * @param process that is executed when command is executed + */ + NetworkCommand(String name, String shortcut, EnhancedRunnable process) { this.name = name; this.shortcut = shortcut; this.process = process; } + /** + * Executes current network command with params + * @param args Arguments to pass to the command + * @throws RuntimeException if the command returned any errors + */ public void exec(String... args) throws RuntimeException { try { process.run(args); diff --git a/src/main/java/com/fastchat/FastChat/networking/NetworkCommandRegistry.java b/src/main/java/com/fastchat/FastChat/networking/NetworkCommandRegistry.java index 7e2eaf6..1e7df06 100644 --- a/src/main/java/com/fastchat/FastChat/networking/NetworkCommandRegistry.java +++ b/src/main/java/com/fastchat/FastChat/networking/NetworkCommandRegistry.java @@ -2,22 +2,26 @@ import java.util.HashMap; +/** + * A registry to hold network commands + */ public class NetworkCommandRegistry { - private HashMap registry = new HashMap<>(); /** - * @param commands + * adds commands to the registry, using command shortcut as a key + * @param commands Commands to be added to registry */ public void add(NetworkCommand... commands) { for (NetworkCommand command : commands) { - registry.put(command.getName(), command); + registry.put(command.getShortcut(), command); } } /** - * @param shortcut - * @return + * Gets a network command based on the shortcut + * @param shortcut of a command in registry + * @return command with this shortcut or null if not found */ public NetworkCommand get(String shortcut) { return registry.getOrDefault(shortcut, null); diff --git a/src/main/java/com/fastchat/FastChat/networking/Networking.java b/src/main/java/com/fastchat/FastChat/networking/Networking.java deleted file mode 100644 index ef9abbc..0000000 --- a/src/main/java/com/fastchat/FastChat/networking/Networking.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.fastchat.FastChat.networking; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; - -public class Networking { - public static Thread send(DatagramSocket socket, final byte[] data, final InetAddress address, final int port) { - return new Thread("Send") { - public void run() { - DatagramPacket packet = new DatagramPacket(data, data.length, address, port); - try { - socket.send(packet); - } catch (IOException e) { - e.printStackTrace(); - } - } - }; - } -} diff --git a/src/main/java/com/fastchat/FastChat/networking/Protocol.java b/src/main/java/com/fastchat/FastChat/networking/Protocol.java index e62eb5b..2e50c37 100644 --- a/src/main/java/com/fastchat/FastChat/networking/Protocol.java +++ b/src/main/java/com/fastchat/FastChat/networking/Protocol.java @@ -1,4 +1,102 @@ package com.fastchat.FastChat.networking; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.util.function.Supplier; + public class Protocol { + public static final String EOD = "/e/"; + + public static final Runnable IGNORE = () -> { + }; + + public static void process(DatagramPacket packet, NetworkCommandRegistry networkCommandRegistry) { + process(packet, networkCommandRegistry, IGNORE, IGNORE); + } + + public static void process(DatagramPacket packet, NetworkCommandRegistry networkCommandRegistry, Runnable onSuccess) { + process(packet, networkCommandRegistry, onSuccess, IGNORE); + } + + public static void process(String string, NetworkCommandRegistry networkCommandRegistry) { + process(string, networkCommandRegistry, IGNORE, IGNORE); + } + + public static void process(String string, NetworkCommandRegistry networkCommandRegistry, Runnable onSuccess) { + process(string, networkCommandRegistry, onSuccess, IGNORE); + } + + public static void process(DatagramPacket packet, NetworkCommandRegistry networkCommandRegistry, + Runnable onSuccess, Runnable onError) { + String string = new String(packet.getData()); + string = string.split(EOD)[0]; + onSuccess.run(); + NetworkCommand cmd = networkCommandRegistry.get(string.charAt(1) + ""); + + //FIXME hardcoded crappy if statement should be replaced + if (cmd == null || string.length() < 3 || string.charAt(0) != '/' || string.charAt(2) != '/') + onError.run(); + else + cmd.exec(string, packet.getAddress().getHostAddress(), Integer.toString(packet.getPort())); + } + + public static void process(String string, NetworkCommandRegistry networkCommandRegistry, + Runnable onSuccess, Runnable onError) { + string = string.split(EOD)[0]; + onSuccess.run(); + NetworkCommand cmd = networkCommandRegistry.get(string.charAt(1) + ""); + + //FIXME hardcoded crappy if statement should be replaced + if (cmd == null || string.length() < 3 || string.charAt(0) != '/' || string.charAt(2) != '/') + onError.run(); + else + cmd.exec(string); + } + + public static String parsePacket(DatagramPacket packet) { + return new String(packet.getData()).split(EOD)[0]; + } + + public static DatagramPacket receive(DatagramSocket socket) { + byte[] data = new byte[1024]; + DatagramPacket packet = new DatagramPacket(data, data.length); + try { + socket.receive(packet); + } catch (IOException e) { + e.printStackTrace(); + } + return packet; + } + + private static Thread send(DatagramSocket socket, final byte[] data, final InetAddress address, final int port) { + return new Thread("Send") { + public void run() { + DatagramPacket packet = new DatagramPacket(data, data.length, address, port); + try { + socket.send(packet); + } catch (IOException e) { + e.printStackTrace(); + } + } + }; + } + + public static Thread send(DatagramSocket socket, String message, final InetAddress address, final int port) { + message += EOD; + return send(socket, message.getBytes(), address, port); + } + + public static Thread listen(Supplier isRunning, Runnable tick) { + return new Thread("Listen") { + public void run() { + while (isRunning.get()) { + tick.run(); + } + } + }; + } + + } diff --git a/src/main/java/com/fastchat/FastChat/networking/ProtocolCommands.java b/src/main/java/com/fastchat/FastChat/networking/ProtocolCommands.java new file mode 100644 index 0000000..877abfb --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/networking/ProtocolCommands.java @@ -0,0 +1,59 @@ +package com.fastchat.FastChat.networking; + +import com.fastchat.FastChat.util.EnhancedRunnable; + +public class ProtocolCommands { + static String prefix(String shortcut) { + return "/" + shortcut + "/"; + } + + public static class Ping extends NetworkCommand { + static final String NAME = "ping"; + static final String SHORTCUT = "i"; + public static final String PREFIX = prefix(SHORTCUT); + + public Ping(EnhancedRunnable receive) { + super(NAME, SHORTCUT, receive); + } + } + + public static class Connect extends NetworkCommand { + static String NAME = "connect"; + static String SHORTCUT = "c"; + public static final String PREFIX = prefix(SHORTCUT); + + public Connect(EnhancedRunnable receive) { + super(NAME, SHORTCUT, receive); + } + } + + public static class Message extends NetworkCommand { + static String NAME = "message"; + static String SHORTCUT = "m"; + public static final String PREFIX = prefix(SHORTCUT); + + public Message(EnhancedRunnable receive) { + super(NAME, SHORTCUT, receive); + } + } + + public static class Disconnect extends NetworkCommand { + static String NAME = "disconnect"; + static String SHORTCUT = "d"; + public static final String PREFIX = prefix(SHORTCUT); + + public Disconnect(EnhancedRunnable receive) { + super(NAME, SHORTCUT, receive); + } + } + + public static class Users extends NetworkCommand { + static String NAME = "disconnect"; + static String SHORTCUT = "d"; + public static final String PREFIX = prefix(SHORTCUT); + + public Users(EnhancedRunnable receive) { + super(NAME, SHORTCUT, receive); + } + } +} diff --git a/src/main/java/com/fastchat/FastChat/networking/package-info.java b/src/main/java/com/fastchat/FastChat/networking/package-info.java index 3e87568..1a00977 100644 --- a/src/main/java/com/fastchat/FastChat/networking/package-info.java +++ b/src/main/java/com/fastchat/FastChat/networking/package-info.java @@ -1,4 +1,5 @@ /** * This package contains all functions that are used for network communication + * and a custom network protocol */ package com.fastchat.FastChat.networking; \ No newline at end of file diff --git a/src/main/java/com/fastchat/FastChat/server/Server.java b/src/main/java/com/fastchat/FastChat/server/Server.java index 409b3cd..c3bbacf 100644 --- a/src/main/java/com/fastchat/FastChat/server/Server.java +++ b/src/main/java/com/fastchat/FastChat/server/Server.java @@ -1,12 +1,11 @@ package com.fastchat.FastChat.server; -import com.fastchat.FastChat.networking.NetworkCommand; import com.fastchat.FastChat.networking.NetworkCommandRegistry; -import com.fastchat.FastChat.networking.Networking; +import com.fastchat.FastChat.networking.Protocol; +import com.fastchat.FastChat.networking.ProtocolCommands; import com.fastchat.FastChat.util.Command; import com.fastchat.FastChat.util.CommandRegistry; -import java.io.IOException; import java.net.*; import java.util.*; @@ -25,7 +24,7 @@ public class Server implements Runnable { private boolean running; private boolean raw; @SuppressWarnings("FieldCanBeLocal") - private Thread run, manage, send, receive; + private Thread run, manage, receive; private enum Status { DISCONNECTED, TIMED_OUT, KICKED, BANNED @@ -72,8 +71,8 @@ public void run() { } if (!text.startsWith("/")) { - sendToAll("/m/Server: " + text + "/e/"); - System.out.println("/m/Server: " + text + "/e/"); + sendToAll(ProtocolCommands.Message.PREFIX + "Server: " + text + Protocol.EOD); + System.out.println(ProtocolCommands.Message.PREFIX + "Server: " + text + Protocol.EOD); continue; } @@ -91,7 +90,6 @@ private void command(String text) { String commandName = args[0]; args = Arrays.copyOfRange(args, 1, args.length); this.commandRegistry.run(commandName, args); - } /** @@ -101,22 +99,24 @@ private void manageClients() { manage = new Thread("Manage") { public void run() { while (running) { - sendToAll("/i/"); - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - for (ServerClient c : clients) { - if (!response.contains(c.getID())) { - if (c.attempt >= MAX_ATTEMPTS) { - disconnectClient(c.getID(), Status.TIMED_OUT); + synchronized (clients) { + sendToAll(ProtocolCommands.Ping.PREFIX); + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + for (ServerClient c : clients) { + if (!response.contains(c.getID())) { + if (c.attempt >= MAX_ATTEMPTS) { + disconnectClient(c.getID(), Status.TIMED_OUT); + } else { + ++c.attempt; + } } else { - ++c.attempt; + response.remove(new Integer(c.getID())); + c.attempt = 0; } - } else { - response.remove(new Integer(c.getID())); - c.attempt = 0; } } } @@ -125,50 +125,25 @@ public void run() { manage.start(); } - /** - * @param packet - */ - private void process(DatagramPacket packet) { - String string = new String(packet.getData()); - string = string.split("/e/")[0]; - - if (raw) System.out.println(string); - - NetworkCommand cmd = networkCommandRegistry.get(string.charAt(1) + ""); - - //FIXME hardcoded crappy if statement should be replaced - if (cmd == null || string.length() < 3 || string.charAt(0) != '/' || string.charAt(2) != '/') - System.out.println(string); - else - cmd.exec(string, packet.getAddress().getHostAddress(), Integer.toString(packet.getPort())); - } - private void receive() { - receive = new Thread("Receive") { - public void run() { - while (running) { - byte[] data = new byte[1024]; - DatagramPacket packet = new DatagramPacket(data, data.length); - try { - socket.receive(packet); - } catch (IOException e) { - e.printStackTrace(); - } - process(packet); - } - } - }; + receive = Protocol.listen(() -> running, this::tick); receive.start(); } - private void send(final byte[] data, final InetAddress address, final int port) { - send = Networking.send(socket, data, address, port); - send.start(); + private void tick() { + DatagramPacket packet = Protocol.receive(socket); + String string = Protocol.parsePacket(packet); + + Protocol.process(packet, networkCommandRegistry, + () -> { + if (raw) System.out.println(string); + }, + () -> System.out.println("[Err]: " + string) + ); } - private void send(String message, InetAddress ip, int port) { - message += "/e/"; - send(message.getBytes(), ip, port); + private void send(String message, InetAddress address, int port) { + Protocol.send(socket, message, address, port).start(); } private void sendToAll(String string) { @@ -189,7 +164,7 @@ private void disconnectClient(int id, Status status) { disc += "was timed out."; break; case KICKED: - send("/d/0", client.ip, client.port); + send(ProtocolCommands.Disconnect.PREFIX + "0", client.ip, client.port); disc += "was kicked out of server."; break; case BANNED: @@ -278,17 +253,17 @@ private void initCommands() { ); networkCommandRegistry.add( - new NetworkCommand("ping", "i", (args) -> { + new ProtocolCommands.Ping((args) -> { if (args.length <= 0) return; String data = args[0]; response.add(Integer.parseInt(data.substring(3))); }), - new NetworkCommand("connect", "c", (args) -> { + new ProtocolCommands.Connect((args) -> { if (args.length < 3) return; String data = args[0]; InetAddress address; try { - address = InetAddress.getByAddress(args[1].getBytes()); + address = InetAddress.getByName(args[1]); } catch (UnknownHostException e) { System.err.println("Couldn't send connection confirm to " + args[1]); return; @@ -301,15 +276,15 @@ private void initCommands() { } System.out.println(client + " has connected"); sendToAll(client.name + "(" + client.getID() + ") has connected"); - send("/c/" + client.getID(), client.ip, client.port); + send(ProtocolCommands.Connect.PREFIX + client.getID(), client.ip, client.port); }), - new NetworkCommand("message", "m", (args) -> { + new ProtocolCommands.Message((args) -> { if (args.length <= 0) return; String data = args[0]; System.out.println(data); sendToAll(data); }), - new NetworkCommand("disconnect", "d", (args) -> { + new ProtocolCommands.Disconnect((args) -> { if (args.length <= 0) return; String data = args[0]; int id = Integer.parseInt(data.substring(3)); @@ -317,7 +292,7 @@ private void initCommands() { disconnectClient(id, Status.DISCONNECTED); } }), - new NetworkCommand("users", "u", (args) -> { + new ProtocolCommands.Users((args) -> { if (args.length < 3) return; InetAddress address; try { @@ -327,7 +302,7 @@ private void initCommands() { return; } int port = Integer.parseInt(args[2]); - StringBuilder message = new StringBuilder("/u/"); + StringBuilder message = new StringBuilder(ProtocolCommands.Users.PREFIX); for (ServerClient client : clients) { message.append(client.name).append("(").append(client.getID()).append(")//"); } From 93d0f78022cb14fbd7261ba7e8e86a2672c2c609 Mon Sep 17 00:00:00 2001 From: OHopiak Date: Mon, 2 Apr 2018 00:05:52 +0300 Subject: [PATCH 34/37] Moved connection to TCP --- build.gradle | 2 +- .../com/fastchat/FastChat/client/Client.java | 44 ++- .../fastchat/FastChat/client/ClientGUI.java | 2 +- .../FastChat/client/ClientInterface.java | 35 +- .../FastChat/networking/Protocol.java | 87 +--- .../FastChat/networking/ProtocolCommands.java | 4 +- .../com/fastchat/FastChat/server/Server.java | 374 ++++++++++-------- .../FastChat/server/ServerClient.java | 39 +- .../FastChat/util/CommandRegistry.java | 2 +- 9 files changed, 307 insertions(+), 282 deletions(-) diff --git a/build.gradle b/build.gradle index df3981e..0551c5c 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'java' apply plugin: 'jacoco' group = 'com.fastchat' -version = '0.0.2' +version = '0.0.3' description = """FastChat - a tool for instant chatting that not always works as expected - WIP""" diff --git a/src/main/java/com/fastchat/FastChat/client/Client.java b/src/main/java/com/fastchat/FastChat/client/Client.java index 2fa6a36..45fdb68 100644 --- a/src/main/java/com/fastchat/FastChat/client/Client.java +++ b/src/main/java/com/fastchat/FastChat/client/Client.java @@ -1,42 +1,46 @@ package com.fastchat.FastChat.client; -import com.fastchat.FastChat.networking.Protocol; import com.fastchat.FastChat.networking.ProtocolCommands; import javax.swing.*; -import java.net.DatagramSocket; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; import java.net.InetAddress; -import java.net.SocketException; -import java.net.UnknownHostException; +import java.net.Socket; public class Client { private final String name; private final int port; private int ID; - private DatagramSocket socket; + private Socket socket; private InetAddress ip; private boolean running = false, kicked = false, banned = false; + private BufferedReader reader; + private PrintWriter out; + + Client(String name, int port) { super(); this.name = name; this.port = port; } - boolean openConnection(String address) { - try { - socket = new DatagramSocket(); - ip = InetAddress.getByName(address); - } catch (UnknownHostException | SocketException e) { - e.printStackTrace(); - return false; - } - return true; + void openConnection(String address) throws IOException { + socket = new Socket(address, port); + ip = InetAddress.getByName(address); + reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + out = new PrintWriter(socket.getOutputStream()); + send(name); } void send(String message) { - Protocol.send(socket, message, ip, port).start(); + out.println(message); + out.flush(); +// Protocol.send(socket, message, ip, port).start(); } void disconnect(int id) { @@ -101,7 +105,15 @@ public void setBanned(boolean banned) { this.banned = banned; } - public DatagramSocket getSocket() { + public Socket getSocket() { return socket; } + + public BufferedReader getReader() { + return reader; + } + + public PrintWriter getOut() { + return out; + } } diff --git a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java index 11e5145..c55b887 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientGUI.java @@ -100,7 +100,6 @@ void console(String message) { @Override protected void send(String message) { if (message.equals("")) return; - message = client.getName() + ": " + message; messagePrompt.setText(""); client.send(ProtocolCommands.Message.PREFIX + message); } @@ -109,6 +108,7 @@ protected void send(String message) { protected void onBreak() { history.setBackground(new Color(55, 55, 55)); messagePrompt.setEditable(false); + System.exit(1); //FIXME } /** diff --git a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java index be1c617..0f9c40b 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java @@ -5,6 +5,8 @@ import com.fastchat.FastChat.networking.ProtocolCommands; import com.fastchat.FastChat.util.Localization; +import java.io.IOException; + public abstract class ClientInterface implements Runnable { Client client; @@ -37,7 +39,13 @@ void init() { console(String.format("%s %s:%d...", Localization.get("client_attempting_to_connect"), address, port)); - boolean connected = client.openConnection(address); + boolean connected = true; + try { + client.openConnection(address); + } catch (IOException e) { + e.printStackTrace(); + connected = false; + } if (!connected) { String client_connection_failed = Localization.get("client_connection_failed"); @@ -47,7 +55,7 @@ void init() { } else { this.serve(); } - client.send((ProtocolCommands.Connect.PREFIX + name)); +// client.send((ProtocolCommands.Connect.PREFIX + name)); users = new OnlineUsers(); } @@ -61,12 +69,19 @@ public void run() { private void listen() { listen = Protocol.listen(client::isRunning, () -> { - String message = Protocol.parsePacket(Protocol.receive(client.getSocket())); -// System.out.println(message); - Protocol.process(message, networkCommandRegistry, - Protocol.IGNORE, - () -> console("Server: " + message) - ); + try { + String message = client.getReader().readLine(); + if (message == null) { + System.err.println("Server ended session"); + System.exit(0); + } + if (!message.isEmpty()) + Protocol.process(message, networkCommandRegistry, + Protocol.IGNORE, + () -> console("Error: " + message) + ); + } catch (IOException ignored) { + } }); listen.start(); } @@ -78,11 +93,11 @@ private void serve() { private void initCommands() { networkCommandRegistry.add( - new ProtocolCommands.Ping((args) -> client.send(ProtocolCommands.Ping.PREFIX + client.getID())), +// new ProtocolCommands.Ping((args) -> client.send(ProtocolCommands.Ping.PREFIX + client.getID())), new ProtocolCommands.Connect((args) -> { if (args.length < 3) return; String data = args[0]; - client.setID(Integer.parseInt(data.substring(3))); + client.setID(Integer.parseInt(data)); console("Connection is set up successfully... ID is " + client.getID()); }), new ProtocolCommands.Message((args) -> { diff --git a/src/main/java/com/fastchat/FastChat/networking/Protocol.java b/src/main/java/com/fastchat/FastChat/networking/Protocol.java index 2e50c37..2530df4 100644 --- a/src/main/java/com/fastchat/FastChat/networking/Protocol.java +++ b/src/main/java/com/fastchat/FastChat/networking/Protocol.java @@ -1,9 +1,7 @@ package com.fastchat.FastChat.networking; -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; +import com.fastchat.FastChat.server.ServerClient; + import java.util.function.Supplier; public class Protocol { @@ -12,80 +10,25 @@ public class Protocol { public static final Runnable IGNORE = () -> { }; - public static void process(DatagramPacket packet, NetworkCommandRegistry networkCommandRegistry) { - process(packet, networkCommandRegistry, IGNORE, IGNORE); - } - - public static void process(DatagramPacket packet, NetworkCommandRegistry networkCommandRegistry, Runnable onSuccess) { - process(packet, networkCommandRegistry, onSuccess, IGNORE); - } - - public static void process(String string, NetworkCommandRegistry networkCommandRegistry) { - process(string, networkCommandRegistry, IGNORE, IGNORE); - } - - public static void process(String string, NetworkCommandRegistry networkCommandRegistry, Runnable onSuccess) { - process(string, networkCommandRegistry, onSuccess, IGNORE); - } - - public static void process(DatagramPacket packet, NetworkCommandRegistry networkCommandRegistry, - Runnable onSuccess, Runnable onError) { - String string = new String(packet.getData()); - string = string.split(EOD)[0]; - onSuccess.run(); - NetworkCommand cmd = networkCommandRegistry.get(string.charAt(1) + ""); - - //FIXME hardcoded crappy if statement should be replaced - if (cmd == null || string.length() < 3 || string.charAt(0) != '/' || string.charAt(2) != '/') - onError.run(); - else - cmd.exec(string, packet.getAddress().getHostAddress(), Integer.toString(packet.getPort())); - } - public static void process(String string, NetworkCommandRegistry networkCommandRegistry, + public static void process(ServerClient client, String message, NetworkCommandRegistry networkCommandRegistry, Runnable onSuccess, Runnable onError) { - string = string.split(EOD)[0]; - onSuccess.run(); - NetworkCommand cmd = networkCommandRegistry.get(string.charAt(1) + ""); - + NetworkCommand cmd = message.length() > 3 ? networkCommandRegistry.get(message.charAt(1) + "") : null; //FIXME hardcoded crappy if statement should be replaced - if (cmd == null || string.length() < 3 || string.charAt(0) != '/' || string.charAt(2) != '/') + if (cmd == null || message.charAt(0) != '/' || message.charAt(2) != '/') onError.run(); - else - cmd.exec(string); - } - - public static String parsePacket(DatagramPacket packet) { - return new String(packet.getData()).split(EOD)[0]; - } - - public static DatagramPacket receive(DatagramSocket socket) { - byte[] data = new byte[1024]; - DatagramPacket packet = new DatagramPacket(data, data.length); - try { - socket.receive(packet); - } catch (IOException e) { - e.printStackTrace(); + else { + onSuccess.run(); + if (client == null) + cmd.exec(message.substring(3)); + else + cmd.exec(message.substring(3), Integer.toString(client.getID())); } - return packet; - } - - private static Thread send(DatagramSocket socket, final byte[] data, final InetAddress address, final int port) { - return new Thread("Send") { - public void run() { - DatagramPacket packet = new DatagramPacket(data, data.length, address, port); - try { - socket.send(packet); - } catch (IOException e) { - e.printStackTrace(); - } - } - }; } - public static Thread send(DatagramSocket socket, String message, final InetAddress address, final int port) { - message += EOD; - return send(socket, message.getBytes(), address, port); + public static void process(String message, NetworkCommandRegistry networkCommandRegistry, + Runnable onSuccess, Runnable onError) { + process(null, message, networkCommandRegistry, onSuccess, onError); } public static Thread listen(Supplier isRunning, Runnable tick) { @@ -97,6 +40,4 @@ public void run() { } }; } - - } diff --git a/src/main/java/com/fastchat/FastChat/networking/ProtocolCommands.java b/src/main/java/com/fastchat/FastChat/networking/ProtocolCommands.java index 877abfb..fe51dee 100644 --- a/src/main/java/com/fastchat/FastChat/networking/ProtocolCommands.java +++ b/src/main/java/com/fastchat/FastChat/networking/ProtocolCommands.java @@ -48,8 +48,8 @@ public Disconnect(EnhancedRunnable receive) { } public static class Users extends NetworkCommand { - static String NAME = "disconnect"; - static String SHORTCUT = "d"; + static String NAME = "users"; + static String SHORTCUT = "u"; public static final String PREFIX = prefix(SHORTCUT); public Users(EnhancedRunnable receive) { diff --git a/src/main/java/com/fastchat/FastChat/server/Server.java b/src/main/java/com/fastchat/FastChat/server/Server.java index c3bbacf..d200065 100644 --- a/src/main/java/com/fastchat/FastChat/server/Server.java +++ b/src/main/java/com/fastchat/FastChat/server/Server.java @@ -6,25 +6,29 @@ import com.fastchat.FastChat.util.Command; import com.fastchat.FastChat.util.CommandRegistry; -import java.net.*; -import java.util.*; +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Arrays; +import java.util.HashMap; +import java.util.NoSuchElementException; +import java.util.Scanner; public class Server implements Runnable { - private final int MAX_ATTEMPTS = 4; - private final List clients = Collections.synchronizedList(new ArrayList<>()); + private static final String CARET = "> "; private final CommandRegistry commandRegistry = new CommandRegistry(); private final NetworkCommandRegistry networkCommandRegistry = new NetworkCommandRegistry(); - - private DatagramSocket socket; + private final HashMap clients = new HashMap<>(); private final int port; - private final ArrayList response = new ArrayList<>(); private boolean running; private boolean raw; + private ServerSocket serverSocket; @SuppressWarnings("FieldCanBeLocal") - private Thread run, manage, receive; + private Thread run, manage, receive, console; private enum Status { DISCONNECTED, TIMED_OUT, KICKED, BANNED @@ -38,14 +42,13 @@ public Server(int port, String address) { System.out.printf("Server started at %s:%d\n", address, port); this.port = port; try { - socket = new DatagramSocket(this.port, InetAddress.getByName(address)); - } catch (SocketException | UnknownHostException e) { + serverSocket = new ServerSocket(port, 100, InetAddress.getByName(address)); + } catch (IOException e) { e.printStackTrace(); return; } run = new Thread(this, "Server"); initCommands(); - run.start(); } @@ -56,30 +59,8 @@ public void run() { running = true; manageClients(); receive(); - Scanner in = new Scanner(System.in); - System.out.println("Type /help to get more information"); - while (running) { - System.out.print("> "); - String text; - - try { - text = in.nextLine(); - } catch (NoSuchElementException e) { - // Ctrl-D used - System.exit(0); - continue; - } - - if (!text.startsWith("/")) { - sendToAll(ProtocolCommands.Message.PREFIX + "Server: " + text + Protocol.EOD); - System.out.println(ProtocolCommands.Message.PREFIX + "Server: " + text + Protocol.EOD); - continue; - } - - text = text.substring(1); - command(text); - } - if (!running) in.close(); + startConsole(); + console("Type /help to get more information"); } /** @@ -89,7 +70,11 @@ private void command(String text) { String[] args = text.split(" "); String commandName = args[0]; args = Arrays.copyOfRange(args, 1, args.length); - this.commandRegistry.run(commandName, args); + try { + commandRegistry.run(commandName, args); + } catch (IllegalArgumentException e) { + System.out.println("Command \"" + commandName + "\" doesn't exist"); + } } /** @@ -99,25 +84,16 @@ private void manageClients() { manage = new Thread("Manage") { public void run() { while (running) { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } synchronized (clients) { - sendToAll(ProtocolCommands.Ping.PREFIX); - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - for (ServerClient c : clients) { - if (!response.contains(c.getID())) { - if (c.attempt >= MAX_ATTEMPTS) { - disconnectClient(c.getID(), Status.TIMED_OUT); - } else { - ++c.attempt; - } - } else { - response.remove(new Integer(c.getID())); - c.attempt = 0; - } - } + clients.forEach((id, c) -> { + if (c.socket.isClosed()) + disconnectClient(c.getID(), Status.TIMED_OUT); + }); } } } @@ -131,53 +107,99 @@ private void receive() { } private void tick() { - DatagramPacket packet = Protocol.receive(socket); - String string = Protocol.parsePacket(packet); + try { + Socket socket = serverSocket.accept(); + ServerClient client = new ServerClient(socket); + clients.put(client.getID(), client); + getClientThread(client).start(); + } catch (IOException e) { + System.err.println("I/O error: " + e); + } +// DatagramPacket packet = Protocol.receive(clientSocket); +// String string = Protocol.parsePacket(packet); +// +// Protocol.process(packet, networkCommandRegistry, +// () -> { +// if (raw) System.out.println(string); +// }, +// () -> System.out.println("[Err]: " + string) +// ); + } - Protocol.process(packet, networkCommandRegistry, - () -> { - if (raw) System.out.println(string); - }, - () -> System.out.println("[Err]: " + string) - ); + private void startConsole() { + console = new Thread(() -> { + Scanner in = new Scanner(System.in); + while (running) { + String text; + + try { + text = in.nextLine(); + } catch (NoSuchElementException e) { + // Ctrl-D used + System.exit(0); + continue; + } + + if (!text.startsWith("/")) { + sendToAll("Server: " + text); + console("Server: " + text); + continue; + } + + text = text.substring(1); + command(text); + } + if (!running) in.close(); + }, "Console"); + console.start(); + } + + private void send(String message, ServerClient client) { + client.out.println(message); + client.out.flush(); } - private void send(String message, InetAddress address, int port) { - Protocol.send(socket, message, address, port).start(); + private void console(Object message) { + System.out.print("\r" + message.toString() + "\n" + CARET); } private void sendToAll(String string) { - for (ServerClient client : clients) { - send(string, client.ip, client.port); - } + clients.forEach((id, client) -> send(ProtocolCommands.Message.PREFIX + string, client)); } private void disconnectClient(int id, Status status) { - for (ServerClient client : clients) { - if (client.getID() == id) { - String disc = client.name + "(" + client.getID() + ") "; - switch (status) { - case DISCONNECTED: - disc += "disconnected."; - break; - case TIMED_OUT: - disc += "was timed out."; - break; - case KICKED: - send(ProtocolCommands.Disconnect.PREFIX + "0", client.ip, client.port); - disc += "was kicked out of server."; - break; - case BANNED: - disc += "was banned!"; - break; - } - clients.remove(client); - ServerClient.removeId(id); - System.out.println(disc); - sendToAll(disc); + ServerClient client = clients.getOrDefault(id, null); + if (client == null) { + return; + } + String disc = client.name + "(" + client.getID() + ") "; + switch (status) { + case DISCONNECTED: + disc += "disconnected."; + break; + case TIMED_OUT: + disc += "was timed out."; + break; + case KICKED: + send(ProtocolCommands.Disconnect.PREFIX + "0", client); + disc += "was kicked out of server."; + break; + case BANNED: + disc += "was banned!"; break; - } } + try { + client.socket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + synchronized (clients) { + clients.remove(id); + } + ServerClient.removeId(id); + console(disc); + sendToAll(disc); + } public int getPort() { @@ -197,117 +219,131 @@ private void initCommands() { return null; }), new Command("help", (argv) -> { - System.out.println("Available commands: "); + console("Available commands: "); commandRegistry.getRegistry().forEach((name, c) -> - System.out.println("/" + name + (c.getHelp().isEmpty() ? "" : " - " + c.getHelp())) + console("/" + name + (c.getHelp().isEmpty() ? "" : " - " + c.getHelp())) ); return null; }), new Command("clients", "prints the list of clients", (argv) -> { - System.out.println("Clients:"); - System.out.println("==================================="); - for (ServerClient serverClient : clients) { - System.out.println(serverClient); - } - System.out.println("==================================="); + console("Clients:"); + console("==================================="); + clients.forEach((id, client) -> console(client)); + console("==================================="); return null; }), new Command("kick", "kick the list of clients", (argv) -> { for (String name : argv) { int id = -1; - boolean number = true; try { id = Integer.parseInt(name); - } catch (NumberFormatException e) { - number = false; - } - if (number) { - boolean exists = false; - for (ServerClient client : clients) { - if (client.getID() == id) { - exists = true; - break; - } - } - if (exists) - disconnectClient(id, Status.KICKED); - else - System.out.println("Client with id " + id + " does not exist"); - } else { - for (ServerClient client : clients) { - if (name.equals(client.name)) { - disconnectClient(client.getID(), Status.KICKED); - break; - } - } + } catch (NumberFormatException ignored) { } + if (clients.containsKey(id)) + disconnectClient(id, Status.KICKED); + else + console("Client with id " + id + " does not exist"); } return null; }), new Command("raw", "", (argv) -> { raw = !raw; - System.out.println((raw ? "Raw mode enabled" : "Raw mode disabled")); + console((raw ? "Raw mode enabled" : "Raw mode disabled")); return null; }) ); networkCommandRegistry.add( - new ProtocolCommands.Ping((args) -> { - if (args.length <= 0) return; - String data = args[0]; - response.add(Integer.parseInt(data.substring(3))); - }), - new ProtocolCommands.Connect((args) -> { - if (args.length < 3) return; - String data = args[0]; - InetAddress address; - try { - address = InetAddress.getByName(args[1]); - } catch (UnknownHostException e) { - System.err.println("Couldn't send connection confirm to " + args[1]); - return; - } - int port = Integer.parseInt(args[2]); - String name = data.substring(3); - ServerClient client = new ServerClient(name, address, port); - synchronized (clients) { - clients.add(client); - } - System.out.println(client + " has connected"); - sendToAll(client.name + "(" + client.getID() + ") has connected"); - send(ProtocolCommands.Connect.PREFIX + client.getID(), client.ip, client.port); - }), +// new ProtocolCommands.Connect((args) -> { +// if (args.length < 3) return; +// String data = args[0]; +// InetAddress address; +// try { +// address = InetAddress.getByName(args[1]); +// } catch (UnknownHostException e) { +// System.err.println("Couldn't send connection confirm to " + args[1]); +// return; +// } +// int port = Integer.parseInt(args[2]); +// String name = data.substring(3); +// ServerClient client = null; +// try { +// client = new ServerClient(name, address, port, null); +// } catch (IOException e) { +// e.printStackTrace(); +// return; +// } +// synchronized (clients) { +// clients.add(client); +// } +// }), new ProtocolCommands.Message((args) -> { if (args.length <= 0) return; String data = args[0]; - System.out.println(data); - sendToAll(data); + int id = Integer.parseInt(args[1]); + if (clients.containsKey(id)) { + data = clients.get(id) + ":" + data; + console(data); + sendToAll(data); + } }), new ProtocolCommands.Disconnect((args) -> { - if (args.length <= 0) return; - String data = args[0]; - int id = Integer.parseInt(data.substring(3)); - synchronized (clients) { - disconnectClient(id, Status.DISCONNECTED); - } + if (args.length < 2) return; + int id = Integer.parseInt(args[1]); + disconnectClient(id, Status.DISCONNECTED); }), new ProtocolCommands.Users((args) -> { - if (args.length < 3) return; - InetAddress address; - try { - address = InetAddress.getByAddress(args[1].getBytes()); - } catch (UnknownHostException e) { - System.err.println("Couldn't send user list to " + args[1]); - return; - } - int port = Integer.parseInt(args[2]); + if (args.length < 2) return; + int id = Integer.parseInt(args[1]); + ServerClient current = clients.getOrDefault(id, null); + if (current == null) return; StringBuilder message = new StringBuilder(ProtocolCommands.Users.PREFIX); - for (ServerClient client : clients) { - message.append(client.name).append("(").append(client.getID()).append(")//"); - } - send(message.toString(), address, port); + clients.forEach((key, client) -> + message.append(client.name) + .append("(") + .append(client.getID()) + .append(")//")); + send(message.toString(), current); }) ); + + } + + /** + * Creates a thread for connection with client + * + * @param client that has connected + * @return thread that keeps the connection with client + */ + private Thread getClientThread(ServerClient client) { + return new Thread(() -> { + try { + client.name = client.reader.readLine(); + } catch (IOException e) { + e.printStackTrace(); + return; + } + + console(client + " has connected"); + sendToAll(client.name + "(" + client.getID() + ") has connected"); + send(ProtocolCommands.Connect.PREFIX + client.getID(), client); + + while (!client.socket.isClosed()) { + try { + String line = client.reader.readLine(); + if (line == null) return; + Protocol.process(client, line, networkCommandRegistry, + () -> { + if (raw) console(line); + }, + () -> console("[Err]: " + line) + ); + } catch (IOException e) { + e.printStackTrace(); + return; + } + } + }); } -} +} \ No newline at end of file diff --git a/src/main/java/com/fastchat/FastChat/server/ServerClient.java b/src/main/java/com/fastchat/FastChat/server/ServerClient.java index 2cc782e..f9fbed5 100644 --- a/src/main/java/com/fastchat/FastChat/server/ServerClient.java +++ b/src/main/java/com/fastchat/FastChat/server/ServerClient.java @@ -1,30 +1,50 @@ package com.fastchat.FastChat.server; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; import java.net.InetAddress; +import java.net.Socket; import java.util.ArrayList; import java.util.Random; -class ServerClient { +public class ServerClient { private static final ArrayList identifiers = new ArrayList<>(); - public final String name; - public final InetAddress ip; + public final Socket socket; + private final InetAddress ip; private final int ID; public int attempt = 0; public final int port; - + public String name; + public BufferedReader reader; + public PrintWriter out; + + public int getID() { return ID; } - ServerClient(String name, InetAddress ip, int port) { + private ServerClient(String name, InetAddress ip, int port, Socket socket) throws IOException { super(); this.name = name; this.ip = ip; this.port = port; this.ID = createId(); + this.socket = socket; + this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + this.out = new PrintWriter(socket.getOutputStream()); } - + + ServerClient(Socket socket) throws IOException { + this(socket.getInetAddress().getHostAddress() + ":" + socket.getPort(), socket); + } + + private ServerClient(String name, Socket socket) throws IOException { + this(name, socket.getInetAddress(), socket.getPort(), socket); + } + private static int createId() { int id = new Random().nextInt(10000); if (!(identifiers.contains(id) || id <= 0)) { @@ -34,7 +54,7 @@ private static int createId() { return createId(); } } - + public static void removeId(int id) { for (Integer integer : identifiers) { if (integer.equals(id)) { @@ -43,9 +63,10 @@ public static void removeId(int id) { } } } - + @Override public String toString() { - return (name != null ? name : "NONAME") + "(" + ID + ")" + (ip != null ? ip + ":" : "NOADDRESS") + port; + return (name != null ? name : "NONAME") + "(" + ID + ")"; +// + (ip != null ? ip + ":" : "NOADDRESS") + port; } } diff --git a/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java b/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java index 5e3a221..ad10b9a 100644 --- a/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java +++ b/src/main/java/com/fastchat/FastChat/util/CommandRegistry.java @@ -10,7 +10,7 @@ public void add(Command... commands) { registry.put(command.getName(), command); } - public void run(String name, String... args) { + public void run(String name, String... args) throws IllegalArgumentException { Command c = registry.get(name); if (c != null) { From 0fdc4b6ee3644718476e594364daef320bcf4e0c Mon Sep 17 00:00:00 2001 From: OHopiak Date: Mon, 2 Apr 2018 00:22:06 +0300 Subject: [PATCH 35/37] added CodeCov badge --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 5771fb2..e6de617 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,8 @@ FastChat ======== -Travis: [![Build Status](https://travis-ci.org/OHopiak/FastChat.svg?branch=master)](https://travis-ci.org/OHopiak/FastChat) - -Jenkins: -[![Build Status](http://18.194.57.194:8080/job/FastChat/job/master/4/badge/icon)](http://18.194.57.194:8080/job/FastChat/job/master/4/) +[![codecov](https://codecov.io/gh/OHopiak/FastChat/branch/dev/graph/badge.svg)](https://codecov.io/gh/OHopiak/FastChat) This is the simple chatting tool Feel free to contribute to this project From b13c2885d5434139d8c2eb94a6af6d6c2791c644 Mon Sep 17 00:00:00 2001 From: OHopiak Date: Tue, 3 Apr 2018 17:13:56 +0300 Subject: [PATCH 36/37] adding deploy to travis config --- .travis.yml | 7 +++++ scripts/deploy-travis | 70 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100755 scripts/deploy-travis diff --git a/.travis.yml b/.travis.yml index 5275048..302b508 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,11 @@ cache: gradle jdk: - oraclejdk8 +env: + global: + - GH_REPO_NAME: FastChat + - GH_REPO_REF: github.com/OHopiak/FastChat.git + script: - gradle assemble - gradle check jacocoTestReport @@ -12,3 +17,5 @@ script: after_success: - bash <(curl -s https://codecov.io/bash) + - cd $TRAVIS_BUILD_DIR + - ./scripts/deploy-travis diff --git a/scripts/deploy-travis b/scripts/deploy-travis new file mode 100755 index 0000000..56e2a1f --- /dev/null +++ b/scripts/deploy-travis @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +PROJECT_DIR=$(cd $(dirname "$0")/.. && echo "$PWD") +BUILD_DIR="$PROJECT_DIR/build" +GH_PAGES_DIR="$BUILD_DIR/gh-pages" +BRANCH=$(git branch | grep \* | cut -d ' ' -f2-) + +if [ "$BRANCH" == "master" ]; then + DEPLOY_DIR="$GH_PAGES_DIR" +else + DEPLOY_DIR="$GH_PAGES_DIR/$BRANCH" +fi + +set -ex + +function copy_markdown(){ + cp "$PROJECT_DIR"/*.md "$DEPLOY_DIR" +} + +function deploy_dir(){ + local dir=$1 + if [ -d "$BUILD_DIR/$dir" ]; then + if [ -d "$DEPLOY_DIR/$dir" ]; then + rm -fr "$DEPLOY_DIR/$dir" + fi + cp -r "$BUILD_DIR/$dir" "$DEPLOY_DIR/$dir" + else + echo "Directory $dir doesn't exist" + fi +} + +function set_up_git(){ + # delete directory with pages if it exists + if [ -d "$GH_PAGES_DIR" ]; then + rm -fr "$GH_PAGES_DIR" + fi + + git clone -b gh-pages "https://git@$GH_REPO_REF.git" "$GH_PAGES_DIR" + + cd "$GH_PAGES_DIR" + git config --global push.default simple + git config user.name "Travis CI" + git config user.email "travis@travis-ci.org" +} + +function push_changes(){ + if ! git diff-index --quiet HEAD --; then + git add --all + git commit -m "Deploy code docs to GitHub Pages Travis build: ${TRAVIS_BUILD_NUMBER}" -m "Commit: ${TRAVIS_COMMIT}" + git push --force "https://${GH_REPO_TOKEN}@${GH_REPO_REF}" > /dev/null 2>&1 + fi +} + +function main(){ + if [ ! -d "$PROJECT_DIR/build" ]; then + echo "You must build the project before deploying" + exit 1 + fi + + set_up_git + mkdir -p "$DEPLOY_DIR" + copy_markdown + deploy_dir libs + deploy_dir docs + deploy_dir reports + push_changes + echo "Deploy is finished successfully" +} + +main \ No newline at end of file From af60a24baef001510ff508c2fac58f5ba1e6ae6f Mon Sep 17 00:00:00 2001 From: OHopiak Date: Fri, 6 Apr 2018 15:59:56 +0300 Subject: [PATCH 37/37] separated OnlineUsers to the interface, fixed bug with cropping first three characters in client, moved User to separate class, now it is used in both backend and frontend --- build.gradle | 1 + .../java/com/fastchat/FastChat/Loader.java | 2 +- .../com/fastchat/FastChat/beans/User.java | 99 ++++++++++++++++++- .../com/fastchat/FastChat/beans/UserBean.java | 30 ++++++ .../com/fastchat/FastChat/client/Client.java | 87 +++------------- .../FastChat/client/ClientInterface.java | 27 +++-- .../FastChat/client/LoginInterface.java | 2 +- .../fastchat/FastChat/client/OnlineUsers.java | 42 -------- .../FastChat/client/OnlineUsersInterface.java | 11 +++ .../FastChat/client/cli/OnlineUsersCLI.java | 33 +++++++ .../FastChat/client/{ => gui}/ClientGUI.form | 2 +- .../FastChat/client/{ => gui}/ClientGUI.java | 20 ++-- .../FastChat/client/{ => gui}/LoginGUI.form | 2 +- .../FastChat/client/{ => gui}/LoginGUI.java | 17 ++-- .../FastChat/client/gui/OnlineUsersGUI.form | 26 +++++ .../FastChat/client/gui/OnlineUsersGUI.java | 77 +++++++++++++++ .../FastChat/networking/Protocol.java | 5 +- .../com/fastchat/FastChat/server/Server.java | 20 ++-- .../FastChat/server/ServerClient.java | 35 ++----- 19 files changed, 348 insertions(+), 190 deletions(-) create mode 100644 src/main/java/com/fastchat/FastChat/beans/UserBean.java delete mode 100644 src/main/java/com/fastchat/FastChat/client/OnlineUsers.java create mode 100644 src/main/java/com/fastchat/FastChat/client/OnlineUsersInterface.java create mode 100644 src/main/java/com/fastchat/FastChat/client/cli/OnlineUsersCLI.java rename src/main/java/com/fastchat/FastChat/client/{ => gui}/ClientGUI.form (97%) rename src/main/java/com/fastchat/FastChat/client/{ => gui}/ClientGUI.java (96%) rename src/main/java/com/fastchat/FastChat/client/{ => gui}/LoginGUI.form (99%) rename src/main/java/com/fastchat/FastChat/client/{ => gui}/LoginGUI.java (98%) create mode 100644 src/main/java/com/fastchat/FastChat/client/gui/OnlineUsersGUI.form create mode 100644 src/main/java/com/fastchat/FastChat/client/gui/OnlineUsersGUI.java diff --git a/build.gradle b/build.gradle index 0551c5c..4fad909 100644 --- a/build.gradle +++ b/build.gradle @@ -39,6 +39,7 @@ repositories { dependencies { libraries 'com.jgoodies:jgoodies-forms:1.6.0' + libraries 'com.google.code.gson:gson:2.8.2' // libraries 'com.intellij:forms_rt:7.0.3' testCompile 'org.testng:testng:6.9.12' testCompile 'com.google.guava:guava:19.0' diff --git a/src/main/java/com/fastchat/FastChat/Loader.java b/src/main/java/com/fastchat/FastChat/Loader.java index 71cb381..b05ca6a 100644 --- a/src/main/java/com/fastchat/FastChat/Loader.java +++ b/src/main/java/com/fastchat/FastChat/Loader.java @@ -1,7 +1,7 @@ package com.fastchat.FastChat; -import com.fastchat.FastChat.client.LoginGUI; import com.fastchat.FastChat.client.LoginInterface; +import com.fastchat.FastChat.client.gui.LoginGUI; import com.fastchat.FastChat.server.Server; import com.fastchat.FastChat.util.Localization; diff --git a/src/main/java/com/fastchat/FastChat/beans/User.java b/src/main/java/com/fastchat/FastChat/beans/User.java index c55bd6d..85e8165 100644 --- a/src/main/java/com/fastchat/FastChat/beans/User.java +++ b/src/main/java/com/fastchat/FastChat/beans/User.java @@ -1,4 +1,101 @@ package com.fastchat.FastChat.beans; -class User { +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.InetAddress; +import java.net.Socket; + +public class User { + private String name; + private InetAddress ip; + private int port; + private int ID; + private Socket socket; + private BufferedReader reader; + private PrintWriter out; + + protected User() { + this.name = ""; + this.ip = null; + this.port = 0; + this.ID = 0; + this.socket = null; + this.reader = null; + this.out = null; + } + +// public User(String name, InetAddress ip, int port, int ID, Socket socket) throws IOException { +// this.name = name; +// this.ip = ip; +// this.port = port; +// this.ID = ID; +// this.socket = socket; +// this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); +// this.out = new PrintWriter(socket.getOutputStream()); +// } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public InetAddress getIp() { + return ip; + } + + protected void setIp(InetAddress ip) { + this.ip = ip; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public int getID() { + return ID; + } + + public void setID(int ID) { + this.ID = ID; + } + + public Socket getSocket() { + return socket; + } + + protected void setSocket(Socket socket) throws IOException { + this.socket = socket; + this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + this.out = new PrintWriter(socket.getOutputStream()); + + } + + public BufferedReader getReader() { + return reader; + } + + public void setReader(BufferedReader reader) { + this.reader = reader; + } + + public PrintWriter getOut() { + return out; + } + + public void setOut(PrintWriter out) { + this.out = out; + } + + public UserBean toBean() { + return new UserBean(name, ID); + } } diff --git a/src/main/java/com/fastchat/FastChat/beans/UserBean.java b/src/main/java/com/fastchat/FastChat/beans/UserBean.java new file mode 100644 index 0000000..6b937ca --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/beans/UserBean.java @@ -0,0 +1,30 @@ +package com.fastchat.FastChat.beans; + +public class UserBean { + private String name; + private int ID; + + public UserBean() { + } + + UserBean(String name, int ID) { + this.name = name; + this.ID = ID; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getID() { + return ID; + } + + public void setID(int ID) { + this.ID = ID; + } +} diff --git a/src/main/java/com/fastchat/FastChat/client/Client.java b/src/main/java/com/fastchat/FastChat/client/Client.java index 45fdb68..25be7c7 100644 --- a/src/main/java/com/fastchat/FastChat/client/Client.java +++ b/src/main/java/com/fastchat/FastChat/client/Client.java @@ -1,58 +1,43 @@ package com.fastchat.FastChat.client; +import com.fastchat.FastChat.beans.User; import com.fastchat.FastChat.networking.ProtocolCommands; -import javax.swing.*; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; -public class Client { - - private final String name; - private final int port; - private int ID; - private Socket socket; - private InetAddress ip; +public class Client extends User { + private boolean running = false, kicked = false, banned = false; - private BufferedReader reader; - private PrintWriter out; - - Client(String name, int port) { - super(); - this.name = name; - this.port = port; + setName(name); + setPort(port); } void openConnection(String address) throws IOException { - socket = new Socket(address, port); - ip = InetAddress.getByName(address); - reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); - out = new PrintWriter(socket.getOutputStream()); - send(name); + setIp(InetAddress.getByName(address)); + setSocket(new Socket(address, getPort())); + send(getName()); } - void send(String message) { - out.println(message); - out.flush(); + public void send(String message) { + getOut().println(message); + getOut().flush(); // Protocol.send(socket, message, ip, port).start(); } - void disconnect(int id) { + public void disconnect(int id) { running = false; //socket.close(); if (kicked || banned) { // ((LoginGUI)LoginGUI.getCurrentLogin()).reset(); // ((ClientGUI)LoginGUI.getClientInterface()).getFrame().setVisible(false); - JOptionPane.showMessageDialog(null, - (kicked ? "You were kicked out of server\nTry to reconnect again" - : "You were banned on this server!\nYou can't reconnect unless the server restarts!"), - "Warning!", JOptionPane.WARNING_MESSAGE); +// JOptionPane.showMessageDialog(null, +// (kicked ? "You were kicked out of server\nTry to reconnect again" +// : "You were banned on this server!\nYou can't reconnect unless the server restarts!"), +// "Warning!", JOptionPane.WARNING_MESSAGE); kicked = false; banned = false; } else { @@ -61,26 +46,6 @@ void disconnect(int id) { } } - public String getName() { - return name; - } - - public int getID() { - return ID; - } - - public void setID(int iD) { - ID = iD; - } - - public int getPort() { - return port; - } - - public InetAddress getIp() { - return ip; - } - public boolean isRunning() { return running; } @@ -89,31 +54,11 @@ public void setRunning(boolean running) { this.running = running; } - public boolean isKicked() { - return kicked; - } - public void setKicked(boolean kicked) { this.kicked = kicked; } - public boolean isBanned() { - return banned; - } - public void setBanned(boolean banned) { this.banned = banned; } - - public Socket getSocket() { - return socket; - } - - public BufferedReader getReader() { - return reader; - } - - public PrintWriter getOut() { - return out; - } } diff --git a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java index 0f9c40b..1038d5e 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientInterface.java +++ b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java @@ -9,8 +9,8 @@ public abstract class ClientInterface implements Runnable { - Client client; - private OnlineUsers users; + protected Client client; + protected OnlineUsersInterface users; private String[] userArr = {"Name(id)"}; private final NetworkCommandRegistry networkCommandRegistry = new NetworkCommandRegistry(); private String name, address; @@ -18,11 +18,11 @@ public abstract class ClientInterface implements Runnable { @SuppressWarnings("FieldCanBeLocal") private Thread run, listen; - ClientInterface() { + protected ClientInterface() { this("Anon", "localhost", 1234); } - ClientInterface(String name, String address, int port) { + protected ClientInterface(String name, String address, int port) { this.name = name; this.address = address; this.port = port; @@ -33,7 +33,7 @@ public abstract class ClientInterface implements Runnable { protected abstract void onBreak(); - void init() { + public void init() { client = new Client(name, port); client.setRunning(true); @@ -56,12 +56,11 @@ void init() { this.serve(); } // client.send((ProtocolCommands.Connect.PREFIX + name)); - users = new OnlineUsers(); } protected abstract void send(String message); - abstract void console(String message); + protected abstract void console(String message); public void run() { listen(); @@ -103,12 +102,11 @@ private void initCommands() { new ProtocolCommands.Message((args) -> { if (args.length <= 0) return; String data = args[0]; - console(data.substring(3)); + console(data); }), new ProtocolCommands.Disconnect((args) -> { if (args.length <= 0) return; String data = args[0]; - data = data.substring(3); if (data.startsWith("0")) { client.setKicked(true); client.disconnect(client.getID()); @@ -120,10 +118,9 @@ private void initCommands() { new ProtocolCommands.Users((args) -> { if (args.length <= 0) return; String data = args[0]; - data = data.substring(3); userArr = data.split("//"); - users.updateList(userArr); - users.setVisible(true); + users.update(userArr); + users.show(); }) ); } @@ -132,7 +129,7 @@ public Client getClient() { return client; } - String getName() { + protected String getName() { return name; } @@ -140,11 +137,11 @@ public void setName(String name) { this.name = name; } - String getAddress() { + protected String getAddress() { return address; } - int getPort() { + protected int getPort() { return port; } diff --git a/src/main/java/com/fastchat/FastChat/client/LoginInterface.java b/src/main/java/com/fastchat/FastChat/client/LoginInterface.java index 85dbf66..c66e7f1 100644 --- a/src/main/java/com/fastchat/FastChat/client/LoginInterface.java +++ b/src/main/java/com/fastchat/FastChat/client/LoginInterface.java @@ -1,7 +1,7 @@ package com.fastchat.FastChat.client; public abstract class LoginInterface { - ClientInterface clientInterface; + protected ClientInterface clientInterface; protected abstract void submit(String name, String address, int port); diff --git a/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java b/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java deleted file mode 100644 index 1dafcdd..0000000 --- a/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.fastchat.FastChat.client; - -import com.fastchat.FastChat.util.Localization; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import java.awt.*; - -public class OnlineUsers extends JFrame { - - private static final long serialVersionUID = -3779778679932068169L; - private final JPanel contentPane; - private final JList list; - - OnlineUsers() { - setType(Type.UTILITY); - setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - setSize(278, 409); - setLocationRelativeTo(null); - setTitle(Localization.get("menu_bar_online_users")); - contentPane = new JPanel(); - contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); - setContentPane(contentPane); - GridBagLayout gbl_contentPane = new GridBagLayout(); - gbl_contentPane.columnWidths = new int[]{0, 0}; - gbl_contentPane.rowHeights = new int[]{0, 0}; - gbl_contentPane.columnWeights = new double[]{1.0, Double.MIN_VALUE}; - gbl_contentPane.rowWeights = new double[]{1.0, Double.MIN_VALUE}; - contentPane.setLayout(gbl_contentPane); - - list = new JList<>(); - GridBagConstraints gbc_list = new GridBagConstraints(); - gbc_list.fill = GridBagConstraints.BOTH; - gbc_list.gridx = 0; - gbc_list.gridy = 0; - contentPane.add(list, gbc_list); - } - - public void updateList(String[] users) { - list.setListData(users); - } -} diff --git a/src/main/java/com/fastchat/FastChat/client/OnlineUsersInterface.java b/src/main/java/com/fastchat/FastChat/client/OnlineUsersInterface.java new file mode 100644 index 0000000..cbaba1c --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/client/OnlineUsersInterface.java @@ -0,0 +1,11 @@ +package com.fastchat.FastChat.client; + +public abstract class OnlineUsersInterface { +// List users; + + protected abstract void init(); + + protected abstract void update(String[] users); + + protected abstract void show(); +} diff --git a/src/main/java/com/fastchat/FastChat/client/cli/OnlineUsersCLI.java b/src/main/java/com/fastchat/FastChat/client/cli/OnlineUsersCLI.java new file mode 100644 index 0000000..49ab297 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/client/cli/OnlineUsersCLI.java @@ -0,0 +1,33 @@ +package com.fastchat.FastChat.client.cli; + +import com.fastchat.FastChat.client.OnlineUsersInterface; + +import java.util.ArrayList; +import java.util.Arrays; + +public class OnlineUsersCLI extends OnlineUsersInterface { + + private ArrayList list; + + public OnlineUsersCLI() { + init(); + } + + @Override + public void init() { + list = new ArrayList<>(); + } + + @Override + public void update(String[] users) { + list.clear(); + list.addAll(Arrays.asList(users)); + } + + @Override + public void show() { + System.out.println("====================================="); + list.forEach(System.out::println); + System.out.println("====================================="); + } +} diff --git a/src/main/java/com/fastchat/FastChat/client/ClientGUI.form b/src/main/java/com/fastchat/FastChat/client/gui/ClientGUI.form similarity index 97% rename from src/main/java/com/fastchat/FastChat/client/ClientGUI.form rename to src/main/java/com/fastchat/FastChat/client/gui/ClientGUI.form index 9e69bfc..2702dab 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientGUI.form +++ b/src/main/java/com/fastchat/FastChat/client/gui/ClientGUI.form @@ -1,5 +1,5 @@ -
+ diff --git a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java b/src/main/java/com/fastchat/FastChat/client/gui/ClientGUI.java similarity index 96% rename from src/main/java/com/fastchat/FastChat/client/ClientGUI.java rename to src/main/java/com/fastchat/FastChat/client/gui/ClientGUI.java index c55b887..2a4ff6e 100644 --- a/src/main/java/com/fastchat/FastChat/client/ClientGUI.java +++ b/src/main/java/com/fastchat/FastChat/client/gui/ClientGUI.java @@ -1,5 +1,6 @@ -package com.fastchat.FastChat.client; +package com.fastchat.FastChat.client.gui; +import com.fastchat.FastChat.client.ClientInterface; import com.fastchat.FastChat.networking.ProtocolCommands; import com.fastchat.FastChat.util.Localization; @@ -28,6 +29,13 @@ public class ClientGUI extends ClientInterface { private JPanel clientPanel; private JTextArea history; + { +// GUI initializer generated by IntelliJ IDEA GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + /** * */ @@ -92,7 +100,7 @@ private JMenuBar generateMenuBar() { } @Override - void console(String message) { + protected void console(String message) { history.setCaretPosition(history.getDocument().getLength()); history.append(message + "\r\n"); } @@ -153,13 +161,7 @@ public void windowClosing(WindowEvent e) { super.init(); frame.setVisible(true); - } - - { -// GUI initializer generated by IntelliJ IDEA GUI Designer -// >>> IMPORTANT!! <<< -// DO NOT EDIT OR ADD ANY CODE HERE! - $$$setupUI$$$(); + users = new OnlineUsersGUI(); } /** diff --git a/src/main/java/com/fastchat/FastChat/client/LoginGUI.form b/src/main/java/com/fastchat/FastChat/client/gui/LoginGUI.form similarity index 99% rename from src/main/java/com/fastchat/FastChat/client/LoginGUI.form rename to src/main/java/com/fastchat/FastChat/client/gui/LoginGUI.form index 941a483..fbe138c 100644 --- a/src/main/java/com/fastchat/FastChat/client/LoginGUI.form +++ b/src/main/java/com/fastchat/FastChat/client/gui/LoginGUI.form @@ -1,5 +1,5 @@ - + diff --git a/src/main/java/com/fastchat/FastChat/client/LoginGUI.java b/src/main/java/com/fastchat/FastChat/client/gui/LoginGUI.java similarity index 98% rename from src/main/java/com/fastchat/FastChat/client/LoginGUI.java rename to src/main/java/com/fastchat/FastChat/client/gui/LoginGUI.java index f8e0b20..b4e1bf8 100644 --- a/src/main/java/com/fastchat/FastChat/client/LoginGUI.java +++ b/src/main/java/com/fastchat/FastChat/client/gui/LoginGUI.java @@ -1,5 +1,6 @@ -package com.fastchat.FastChat.client; +package com.fastchat.FastChat.client.gui; +import com.fastchat.FastChat.client.LoginInterface; import com.fastchat.FastChat.util.Localization; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; @@ -22,6 +23,13 @@ public class LoginGUI extends LoginInterface { private JLabel lblAddress; private JLabel lblPort; + { +// GUI initializer generated by IntelliJ IDEA GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + public LoginGUI() { init(); } @@ -81,13 +89,6 @@ private void init() { frame.setVisible(true); } - { -// GUI initializer generated by IntelliJ IDEA GUI Designer -// >>> IMPORTANT!! <<< -// DO NOT EDIT OR ADD ANY CODE HERE! - $$$setupUI$$$(); - } - /** * Method generated by IntelliJ IDEA GUI Designer * >>> IMPORTANT!! <<< diff --git a/src/main/java/com/fastchat/FastChat/client/gui/OnlineUsersGUI.form b/src/main/java/com/fastchat/FastChat/client/gui/OnlineUsersGUI.form new file mode 100644 index 0000000..5b463b6 --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/client/gui/OnlineUsersGUI.form @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/fastchat/FastChat/client/gui/OnlineUsersGUI.java b/src/main/java/com/fastchat/FastChat/client/gui/OnlineUsersGUI.java new file mode 100644 index 0000000..a55d3fb --- /dev/null +++ b/src/main/java/com/fastchat/FastChat/client/gui/OnlineUsersGUI.java @@ -0,0 +1,77 @@ +package com.fastchat.FastChat.client.gui; + +import com.fastchat.FastChat.client.OnlineUsersInterface; +import com.fastchat.FastChat.util.Localization; + +import javax.swing.*; +import java.awt.*; + +public class OnlineUsersGUI extends OnlineUsersInterface { + private JFrame frame; + private JPanel panel; + private JList userList; + + { +// GUI initializer generated by IntelliJ IDEA GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + + OnlineUsersGUI() { + init(); + } + + @Override + protected void init() { + frame = new JFrame(); + frame.setType(JFrame.Type.UTILITY); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.setSize(278, 409); + frame.setLocationRelativeTo(null); + frame.setTitle(Localization.get("menu_bar_online_users")); + frame.setContentPane(panel); + } + + @Override + protected void update(String[] users) { + userList.setListData(users); + } + + @Override + protected void show() { + frame.setVisible(true); + } + + /** + * Method generated by IntelliJ IDEA GUI Designer + * >>> IMPORTANT!! <<< + * DO NOT edit this method OR call it in your code! + * + * @noinspection ALL + */ + private void $$$setupUI$$$() { + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + final JScrollPane scrollPane1 = new JScrollPane(); + GridBagConstraints gbc; + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.fill = GridBagConstraints.BOTH; + gbc.ipadx = 5; + gbc.ipady = 5; + panel.add(scrollPane1, gbc); + userList = new JList(); + scrollPane1.setViewportView(userList); + } + + /** + * @noinspection ALL + */ + public JComponent $$$getRootComponent$$$() { + return panel; + } +} diff --git a/src/main/java/com/fastchat/FastChat/networking/Protocol.java b/src/main/java/com/fastchat/FastChat/networking/Protocol.java index 2530df4..1e52679 100644 --- a/src/main/java/com/fastchat/FastChat/networking/Protocol.java +++ b/src/main/java/com/fastchat/FastChat/networking/Protocol.java @@ -5,15 +5,12 @@ import java.util.function.Supplier; public class Protocol { - public static final String EOD = "/e/"; - public static final Runnable IGNORE = () -> { }; - public static void process(ServerClient client, String message, NetworkCommandRegistry networkCommandRegistry, Runnable onSuccess, Runnable onError) { - NetworkCommand cmd = message.length() > 3 ? networkCommandRegistry.get(message.charAt(1) + "") : null; + NetworkCommand cmd = message.length() >= 3 ? networkCommandRegistry.get(message.charAt(1) + "") : null; //FIXME hardcoded crappy if statement should be replaced if (cmd == null || message.charAt(0) != '/' || message.charAt(2) != '/') onError.run(); diff --git a/src/main/java/com/fastchat/FastChat/server/Server.java b/src/main/java/com/fastchat/FastChat/server/Server.java index d200065..5ebf9f9 100644 --- a/src/main/java/com/fastchat/FastChat/server/Server.java +++ b/src/main/java/com/fastchat/FastChat/server/Server.java @@ -91,7 +91,7 @@ public void run() { } synchronized (clients) { clients.forEach((id, c) -> { - if (c.socket.isClosed()) + if (c.getSocket().isClosed()) disconnectClient(c.getID(), Status.TIMED_OUT); }); } @@ -155,8 +155,8 @@ private void startConsole() { } private void send(String message, ServerClient client) { - client.out.println(message); - client.out.flush(); + client.getOut().println(message); + client.getOut().flush(); } private void console(Object message) { @@ -172,7 +172,7 @@ private void disconnectClient(int id, Status status) { if (client == null) { return; } - String disc = client.name + "(" + client.getID() + ") "; + String disc = client.getName() + "(" + client.getID() + ") "; switch (status) { case DISCONNECTED: disc += "disconnected."; @@ -189,7 +189,7 @@ private void disconnectClient(int id, Status status) { break; } try { - client.socket.close(); + client.getSocket().close(); } catch (IOException e) { e.printStackTrace(); } @@ -300,7 +300,7 @@ private void initCommands() { if (current == null) return; StringBuilder message = new StringBuilder(ProtocolCommands.Users.PREFIX); clients.forEach((key, client) -> - message.append(client.name) + message.append(client.getName()) .append("(") .append(client.getID()) .append(")//")); @@ -319,19 +319,19 @@ private void initCommands() { private Thread getClientThread(ServerClient client) { return new Thread(() -> { try { - client.name = client.reader.readLine(); + client.setName(client.getReader().readLine()); } catch (IOException e) { e.printStackTrace(); return; } console(client + " has connected"); - sendToAll(client.name + "(" + client.getID() + ") has connected"); + sendToAll(client.getName() + "(" + client.getID() + ") has connected"); send(ProtocolCommands.Connect.PREFIX + client.getID(), client); - while (!client.socket.isClosed()) { + while (!client.getSocket().isClosed()) { try { - String line = client.reader.readLine(); + String line = client.getReader().readLine(); if (line == null) return; Protocol.process(client, line, networkCommandRegistry, () -> { diff --git a/src/main/java/com/fastchat/FastChat/server/ServerClient.java b/src/main/java/com/fastchat/FastChat/server/ServerClient.java index f9fbed5..99b9c0b 100644 --- a/src/main/java/com/fastchat/FastChat/server/ServerClient.java +++ b/src/main/java/com/fastchat/FastChat/server/ServerClient.java @@ -1,40 +1,23 @@ package com.fastchat.FastChat.server; -import java.io.BufferedReader; +import com.fastchat.FastChat.beans.User; + import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; import java.util.ArrayList; import java.util.Random; -public class ServerClient { +public class ServerClient extends User { private static final ArrayList identifiers = new ArrayList<>(); - public final Socket socket; - private final InetAddress ip; - private final int ID; - public int attempt = 0; - public final int port; - public String name; - public BufferedReader reader; - public PrintWriter out; - - - public int getID() { - return ID; - } private ServerClient(String name, InetAddress ip, int port, Socket socket) throws IOException { - super(); - this.name = name; - this.ip = ip; - this.port = port; - this.ID = createId(); - this.socket = socket; - this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); - this.out = new PrintWriter(socket.getOutputStream()); + setName(name); + setPort(port); + setIp(ip); + setSocket(socket); + setID(createId()); } ServerClient(Socket socket) throws IOException { @@ -66,7 +49,7 @@ public static void removeId(int id) { @Override public String toString() { - return (name != null ? name : "NONAME") + "(" + ID + ")"; + return (getName() != null ? getName() : "NONAME") + "(" + getID() + ")"; // + (ip != null ? ip + ":" : "NOADDRESS") + port; } }