diff --git a/.gitignore b/.gitignore
index 0826a07..9899293 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,8 @@ gradle
gradlew
gradlew.bat
build
+.gradletasknamecache
+
+#TestNG
+test-output
+
diff --git a/.travis.yml b/.travis.yml
index 45e9d6b..302b508 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,21 @@
language: java
+cache: gradle
+
jdk:
- oraclejdk8
+
+env:
+ global:
+ - GH_REPO_NAME: FastChat
+ - GH_REPO_REF: github.com/OHopiak/FastChat.git
+
+script:
+ - gradle assemble
+ - gradle check jacocoTestReport
+ - gradle build javadoc
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
+ - cd $TRAVIS_BUILD_DIR
+ - ./scripts/deploy-travis
diff --git a/FastChat.uml b/FastChat.uml
index b793435..52cd288 100644
--- a/FastChat.uml
+++ b/FastChat.uml
@@ -3,79 +3,40 @@
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
+ com.fastchat.FastChat.util
+ com.fastchat.FastChat.networking
+ com.fastchat.FastChat.server
+ com.fastchat.FastChat.beans
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+ com.fastchat.FastChat.server
+
Methods
- Inner Classes
All
private
diff --git a/Jenkinsfile b/Jenkinsfile
index ae87b51..3414a69 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -13,8 +13,9 @@ pipeline {
}
stage('test') {
steps {
- sh 'gradle test'
+ sh 'gradle check jacocoTestReport'
junit(testResults: '**/build/test-results/**/*.xml', allowEmptyResults: true)
+ jacoco(execPattern: '**/build/**.exec')
}
}
stage('deploy') {
diff --git a/README.md b/README.md
index 6785f21..e6de617 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,11 @@
FastChat
========
-Travis:
[](https://travis-ci.org/OHopiak/FastChat)
-
-Jenkins:
-[](http://18.194.57.194:8080/job/FastChat/job/master/4/)
+[](https://codecov.io/gh/OHopiak/FastChat)
This is the simple chatting tool
+ Feel free to contribute to this project
Requirements
------------
diff --git a/build.gradle b/build.gradle
index c9c985a..4fad909 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,14 +1,19 @@
apply plugin: 'java'
-apply plugin: 'maven'
+apply plugin: 'jacoco'
group = 'com.fastchat'
-version = '0.0.1'
+version = '0.0.3'
description = """FastChat - a tool for instant chatting that not always works as expected - WIP"""
sourceCompatibility = 1.8
targetCompatibility = 1.8
+
+configurations {
+ libraries
+}
+
jar {
manifest {
attributes(
@@ -18,6 +23,9 @@ jar {
)
}
+ from {
+ configurations.libraries.collect { it.isDirectory() ? it : zipTree(it) }
+ }
}
test {
@@ -26,15 +34,34 @@ 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'
+ 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'
+ configurations.compile.extendsFrom(configurations.libraries)
+}
+
+jacocoTestReport {
+ reports {
+ //noinspection GroovyAssignabilityCheck
+ html.enabled true
+ //noinspection GroovyAssignabilityCheck
+ xml.enabled true
+ //noinspection GroovyAssignabilityCheck
+ csv.enabled true
+ }
}
-task printJarName{
+javadoc {
+ failOnError = false
+}
+
+task printJarName {
dependsOn 'jar'
doLast {
println "${project.name}-${project.version}.jar"
@@ -46,4 +73,4 @@ task printArtifactsDirectory {
doLast {
println "${project.name}/${project.version}"
}
-}
\ No newline at end of file
+}
diff --git a/deploy/Dockerfile b/deploy/Dockerfile
new file mode 100644
index 0000000..08f2bbf
--- /dev/null
+++ b/deploy/Dockerfile
@@ -0,0 +1,6 @@
+FROM gradle:latest
+
+USER root
+RUN mkdir /opt/artifacts && chown -R gradle:gradle /opt/artifacts
+RUN apt update && apt install -y ssh
+#USER gradle
diff --git a/scripts/deploy b/scripts/deploy
index 1647f4c..ef6e103 100755
--- a/scripts/deploy
+++ b/scripts/deploy
@@ -22,7 +22,7 @@ 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
\ No newline at end of file
+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/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
diff --git a/src/main/java/com/fastchat/FastChat/Loader.java b/src/main/java/com/fastchat/FastChat/Loader.java
index 057852b..b05ca6a 100644
--- a/src/main/java/com/fastchat/FastChat/Loader.java
+++ b/src/main/java/com/fastchat/FastChat/Loader.java
@@ -1,17 +1,19 @@
package com.fastchat.FastChat;
-import com.fastchat.FastChat.client.Login;
+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;
-public class Loader {
+import java.awt.*;
+
+class Loader {
private static final String usageString = "Usage: java -jar FastChat.jar [ -s [PORT] [IP] ]";
private static final String description =
"This is really cool tool for chatting\nrun without arguments to launch the client";
- private static void usage() {
- System.out.println(usageString);
- }
+// private static Class clientClass;
private static void help() {
String helpString = usageString + "\n" +
@@ -24,9 +26,44 @@ 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
+// Localization.setLocale(new Locale("uk")); //Ukrainian
+// Localization.setLocale(new Locale("nb")); //Norwegian
+
if (args.length == 0) {
- Login.main(args);
+// 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/beans/User.java b/src/main/java/com/fastchat/FastChat/beans/User.java
new file mode 100644
index 0000000..85e8165
--- /dev/null
+++ b/src/main/java/com/fastchat/FastChat/beans/User.java
@@ -0,0 +1,101 @@
+package com.fastchat.FastChat.beans;
+
+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/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 89ac57f..25be7c7 100644
--- a/src/main/java/com/fastchat/FastChat/client/Client.java
+++ b/src/main/java/com/fastchat/FastChat/client/Client.java
@@ -1,134 +1,63 @@
package com.fastchat.FastChat.client;
+import com.fastchat.FastChat.beans.User;
+import com.fastchat.FastChat.networking.ProtocolCommands;
+
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.Socket;
-public class Client {
-
- private String name;
- private int port;
- private int ID;
- private DatagramSocket socket;
- 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) {
- e.printStackTrace();
- return false;
- }
- return true;
- }
+public class Client extends User {
- 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) {
- e.printStackTrace();
- }
- String message = new String(packet.getData());
- message = message.split("/e/")[0];
- return message;
+ private boolean running = false, kicked = false, banned = false;
+
+ Client(String name, int port) {
+ setName(name);
+ setPort(port);
}
-
- protected 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) {
- e.printStackTrace();
- }
- }
- };
- send.start();
+
+ void openConnection(String address) throws IOException {
+ setIp(InetAddress.getByName(address));
+ setSocket(new Socket(address, getPort()));
+ send(getName());
}
-
- protected void send(String message) {
- message += "/e/";
- send(message.getBytes());
+
+ public void send(String message) {
+ getOut().println(message);
+ getOut().flush();
+// Protocol.send(socket, message, ip, port).start();
}
-
- protected void disconnect(int id) {
+
+ public void disconnect(int id) {
running = false;
//socket.close();
if (kicked || banned) {
- Login.getCurrentLogin().setVisible(true);
- Login.getClientWindow().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);
+// ((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);
kicked = false;
banned = false;
} else {
- send("/d/" + id);
+ send(ProtocolCommands.Disconnect.PREFIX + 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/ClientInterface.java b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java
new file mode 100644
index 0000000..1038d5e
--- /dev/null
+++ b/src/main/java/com/fastchat/FastChat/client/ClientInterface.java
@@ -0,0 +1,151 @@
+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;
+
+import java.io.IOException;
+
+public abstract class ClientInterface implements Runnable {
+
+ protected Client client;
+ protected OnlineUsersInterface users;
+ private String[] userArr = {"Name(id)"};
+ private final NetworkCommandRegistry networkCommandRegistry = new NetworkCommandRegistry();
+ private String name, address;
+ private int port;
+ @SuppressWarnings("FieldCanBeLocal")
+ private Thread run, listen;
+
+ protected ClientInterface() {
+ this("Anon", "localhost", 1234);
+ }
+
+ protected ClientInterface(String name, String address, int port) {
+ this.name = name;
+ this.address = address;
+ this.port = port;
+
+ initCommands();
+// init();
+ }
+
+ protected abstract void onBreak();
+
+ public 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 = true;
+ try {
+ client.openConnection(address);
+ } catch (IOException e) {
+ e.printStackTrace();
+ connected = false;
+ }
+
+ 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((ProtocolCommands.Connect.PREFIX + name));
+ }
+
+ protected abstract void send(String message);
+
+ protected abstract void console(String message);
+
+ public void run() {
+ listen();
+ }
+
+ private void listen() {
+ listen = Protocol.listen(client::isRunning, () -> {
+ 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();
+ }
+
+ private void serve() {
+ run = new Thread(this, "Run");
+ run.start();
+ }
+
+ 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));
+ 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);
+ }),
+ new ProtocolCommands.Disconnect((args) -> {
+ if (args.length <= 0) return;
+ String data = args[0];
+ 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];
+ userArr = data.split("//");
+ users.update(userArr);
+ users.show();
+ })
+ );
+ }
+
+ public Client getClient() {
+ return client;
+ }
+
+ protected String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ protected String getAddress() {
+ return address;
+ }
+
+ protected 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 9e2196e..0000000
--- a/src/main/java/com/fastchat/FastChat/client/ClientWindow.java
+++ /dev/null
@@ -1,192 +0,0 @@
-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.border.EmptyBorder;
-import javax.swing.text.DefaultCaret;
-
-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) {
- 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(new ActionListener() {
- public void actionPerformed(ActionEvent 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(new ActionListener() {
- public void actionPerformed(ActionEvent 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(new ActionListener() {
- public void actionPerformed(ActionEvent 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 1b8f410..0000000
--- a/src/main/java/com/fastchat/FastChat/client/Login.java
+++ /dev/null
@@ -1,137 +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;
-
- public 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(() -> {
- try {
- currentLogin = new Login();
- } catch (Exception e) {
- e.printStackTrace();
- }
- });
- }
-
- public static Login getCurrentLogin() {
- 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/LoginInterface.java b/src/main/java/com/fastchat/FastChat/client/LoginInterface.java
new file mode 100644
index 0000000..c66e7f1
--- /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 {
+ protected 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
deleted file mode 100644
index bdc8090..0000000
--- a/src/main/java/com/fastchat/FastChat/client/OnlineUsers.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.fastchat.FastChat.client;
-
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-
-import javax.swing.JFrame;
-import javax.swing.JList;
-import javax.swing.JPanel;
-import javax.swing.border.EmptyBorder;
-
-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);
- setSize(278, 409);
- setLocationRelativeTo(null);
- setTitle("Users Online");
- 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/gui/ClientGUI.form b/src/main/java/com/fastchat/FastChat/client/gui/ClientGUI.form
new file mode 100644
index 0000000..2702dab
--- /dev/null
+++ b/src/main/java/com/fastchat/FastChat/client/gui/ClientGUI.form
@@ -0,0 +1,50 @@
+
+
diff --git a/src/main/java/com/fastchat/FastChat/client/gui/ClientGUI.java b/src/main/java/com/fastchat/FastChat/client/gui/ClientGUI.java
new file mode 100644
index 0000000..2a4ff6e
--- /dev/null
+++ b/src/main/java/com/fastchat/FastChat/client/gui/ClientGUI.java
@@ -0,0 +1,217 @@
+package com.fastchat.FastChat.client.gui;
+
+import com.fastchat.FastChat.client.ClientInterface;
+import com.fastchat.FastChat.networking.ProtocolCommands;
+import com.fastchat.FastChat.util.Localization;
+
+import javax.swing.*;
+import javax.swing.text.DefaultCaret;
+import java.awt.*;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.net.URL;
+
+/**
+ * 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() {
+ super();
+ }
+
+ private ClientGUI(String name, String address, int port) {
+ super(name, address, port);
+ }
+
+ /**
+ * @return The singleton instance
+ */
+ public static ClientGUI getInstance() {
+ if (clientGUI == null)
+ clientGUI = new ClientGUI();
+ 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;
+ }
+
+ public JFrame getFrame() {
+ return frame;
+ }
+
+ /**
+ * Creates a menu bar for the client window
+ *
+ * @return generated MenuBar
+ */
+ 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(ProtocolCommands.Users.PREFIX));
+ menu.add(onlineUsers);
+
+ JMenuItem settings = new JMenuItem(Localization.get("menu_bar_settings"));
+ settings.addActionListener(event -> {
+
+ });
+ menu.add(settings);
+
+ return menuBar;
+ }
+
+ @Override
+ protected void console(String message) {
+ history.setCaretPosition(history.getDocument().getLength());
+ history.append(message + "\r\n");
+ }
+
+ @Override
+ protected void send(String message) {
+ if (message.equals("")) return;
+ messagePrompt.setText("");
+ client.send(ProtocolCommands.Message.PREFIX + message);
+ }
+
+ @Override
+ protected void onBreak() {
+ history.setBackground(new Color(55, 55, 55));
+ messagePrompt.setEditable(false);
+ System.exit(1); //FIXME
+ }
+
+ /**
+ *
+ */
+ @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);
+
+ if (frame == null)
+ frame = new JFrame(TITLE + " (" + getName() + ")");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ frame.setJMenuBar(generateMenuBar());
+
+ frame.setContentPane(this.clientPanel);
+ frame.pack();
+
+ frame.setSize(WIDTH, HEIGHT);
+ frame.setLocationRelativeTo(null);
+
+ frame.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ client.disconnect(client.getID());
+ }
+ });
+
+ // 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);
+ users = new OnlineUsersGUI();
+ }
+
+ /**
+ * 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();
+ sendButton.setText("Submit");
+ 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/gui/LoginGUI.form b/src/main/java/com/fastchat/FastChat/client/gui/LoginGUI.form
new file mode 100644
index 0000000..fbe138c
--- /dev/null
+++ b/src/main/java/com/fastchat/FastChat/client/gui/LoginGUI.form
@@ -0,0 +1,157 @@
+
+
diff --git a/src/main/java/com/fastchat/FastChat/client/gui/LoginGUI.java b/src/main/java/com/fastchat/FastChat/client/gui/LoginGUI.java
new file mode 100644
index 0000000..b4e1bf8
--- /dev/null
+++ b/src/main/java/com/fastchat/FastChat/client/gui/LoginGUI.java
@@ -0,0 +1,153 @@
+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;
+
+import javax.swing.*;
+import java.awt.event.ActionListener;
+import java.net.URL;
+
+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();
+ }
+
+ @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() {
+ 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"));
+ submitButton.setMnemonic(Localization.get("login_submit").charAt(0));
+
+ txtName.setText("Anon");
+ txtAddress.setText("localhost");
+ txtPort.setText("1234");
+
+ // 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());
+ }
+
+ 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$$$() {
+ 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));
+ lblName.setLabelFor(txtName);
+ lblAddress.setLabelFor(txtAddress);
+ lblPort.setLabelFor(txtPort);
+ }
+
+ /**
+ * @noinspection ALL
+ */
+ public JComponent $$$getRootComponent$$$() {
+ return loginPanel;
+ }
+}
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/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..0da2023
--- /dev/null
+++ b/src/main/java/com/fastchat/FastChat/networking/NetworkCommand.java
@@ -0,0 +1,45 @@
+package com.fastchat.FastChat.networking;
+
+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;
+
+ /**
+ * @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);
+ } 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..1e7df06
--- /dev/null
+++ b/src/main/java/com/fastchat/FastChat/networking/NetworkCommandRegistry.java
@@ -0,0 +1,29 @@
+package com.fastchat.FastChat.networking;
+
+import java.util.HashMap;
+
+/**
+ * A registry to hold network commands
+ */
+public class NetworkCommandRegistry {
+ private HashMap registry = new HashMap<>();
+
+ /**
+ * 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.getShortcut(), command);
+ }
+ }
+
+ /**
+ * 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/Protocol.java b/src/main/java/com/fastchat/FastChat/networking/Protocol.java
new file mode 100644
index 0000000..1e52679
--- /dev/null
+++ b/src/main/java/com/fastchat/FastChat/networking/Protocol.java
@@ -0,0 +1,40 @@
+package com.fastchat.FastChat.networking;
+
+import com.fastchat.FastChat.server.ServerClient;
+
+import java.util.function.Supplier;
+
+public class Protocol {
+ 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;
+ //FIXME hardcoded crappy if statement should be replaced
+ if (cmd == null || message.charAt(0) != '/' || message.charAt(2) != '/')
+ onError.run();
+ else {
+ onSuccess.run();
+ if (client == null)
+ cmd.exec(message.substring(3));
+ else
+ cmd.exec(message.substring(3), Integer.toString(client.getID()));
+ }
+ }
+
+ 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) {
+ 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..fe51dee
--- /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 = "users";
+ static String SHORTCUT = "u";
+ 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
new file mode 100644
index 0000000..1a00977
--- /dev/null
+++ b/src/main/java/com/fastchat/FastChat/networking/package-info.java
@@ -0,0 +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/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 345c004..5ebf9f9 100644
--- a/src/main/java/com/fastchat/FastChat/server/Server.java
+++ b/src/main/java/com/fastchat/FastChat/server/Server.java
@@ -1,165 +1,99 @@
package com.fastchat.FastChat.server;
+import com.fastchat.FastChat.networking.NetworkCommandRegistry;
+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.ArrayList;
-import java.util.List;
+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 static final String[] commands = {"clients", /*"cls",*/ "help", "kick", "raw", "exit"};
- private List clients = new ArrayList<>();
+ private static final String CARET = "> ";
- private DatagramSocket socket;
+ private final CommandRegistry commandRegistry = new CommandRegistry();
+ private final NetworkCommandRegistry networkCommandRegistry = new NetworkCommandRegistry();
+ private final HashMap clients = new HashMap<>();
- private int port;
- private ArrayList response = new ArrayList<>();
+ private final int port;
private boolean running;
private boolean raw;
+ private ServerSocket serverSocket;
@SuppressWarnings("FieldCanBeLocal")
- private Thread run, manage, send, receive;
+ private Thread run, manage, receive, console;
+
+ 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;
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();
}
+ /**
+ *
+ */
public void run() {
running = true;
manageClients();
receive();
- Scanner in = new Scanner(System.in);
- System.out.println("Type /help to get some 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("/m/Server: " + text + "/e/");
- System.out.println("/m/Server: " + text + "/e/");
- continue;
- }
-
- text = text.substring(1);
- command(text);
- }
- if (!running) in.close();
+ startConsole();
+ console("Type /help to get more information");
}
+ /**
+ * @param text command name with arguments
+ */
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);
+ try {
+ commandRegistry.run(commandName, args);
+ } catch (IllegalArgumentException e) {
+ System.out.println("Command \"" + commandName + "\" doesn't exist");
}
-
}
+ /**
+ *
+ */
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) {
+ synchronized (clients) {
+ clients.forEach((id, c) -> {
+ if (c.getSocket().isClosed())
disconnectClient(c.getID(), Status.TIMED_OUT);
- } else {
- ++c.attempt;
- }
- } else {
- response.remove(new Integer(c.getID()));
- c.attempt = 0;
- }
+ });
}
}
}
@@ -167,112 +101,249 @@ public void run() {
manage.start();
}
- 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 {
- System.out.println(string);
- }
- }
-
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 = new Thread("Send") {
- public void run() {
- DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
+ private void tick() {
+ 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)
+// );
+ }
+
+ private void startConsole() {
+ console = new Thread(() -> {
+ Scanner in = new Scanner(System.in);
+ while (running) {
+ String text;
+
try {
- socket.send(packet);
- } catch (IOException e) {
- e.printStackTrace();
+ 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);
}
- };
- send.start();
+ if (!running) in.close();
+ }, "Console");
+ console.start();
+ }
+
+ private void send(String message, ServerClient client) {
+ client.getOut().println(message);
+ client.getOut().flush();
}
- private void send(String message, InetAddress ip, int port) {
- message += "/e/";
- send(message.getBytes(), ip, port);
+ 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("/d/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.getName() + "(" + 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.getSocket().close();
+ } catch (IOException e) {
+ e.printStackTrace();
}
+ synchronized (clients) {
+ clients.remove(id);
+ }
+ ServerClient.removeId(id);
+ console(disc);
+ sendToAll(disc);
+
}
public int getPort() {
return port;
}
- private enum Status {
- DISCONNECTED, TIMED_OUT, KICKED, BANNED
+ private void initCommands() {
+
+ 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) -> {
+ console("Available commands: ");
+ commandRegistry.getRegistry().forEach((name, c) ->
+ console("/" + name + (c.getHelp().isEmpty() ? "" : " - " + c.getHelp()))
+ );
+ return null;
+ }),
+ new Command("clients", "prints the list of clients", (argv) -> {
+ 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;
+ try {
+ id = Integer.parseInt(name);
+ } 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;
+ console((raw ? "Raw mode enabled" : "Raw mode disabled"));
+ return null;
+ })
+ );
+
+ networkCommandRegistry.add(
+// 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];
+ 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 < 2) return;
+ int id = Integer.parseInt(args[1]);
+ disconnectClient(id, Status.DISCONNECTED);
+ }),
+ new ProtocolCommands.Users((args) -> {
+ 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);
+ clients.forEach((key, client) ->
+ message.append(client.getName())
+ .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.setName(client.getReader().readLine());
+ } catch (IOException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ console(client + " has connected");
+ sendToAll(client.getName() + "(" + client.getID() + ") has connected");
+ send(ProtocolCommands.Connect.PREFIX + client.getID(), client);
+
+ while (!client.getSocket().isClosed()) {
+ try {
+ String line = client.getReader().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 e67fcf7..99b9c0b 100644
--- a/src/main/java/com/fastchat/FastChat/server/ServerClient.java
+++ b/src/main/java/com/fastchat/FastChat/server/ServerClient.java
@@ -1,30 +1,33 @@
package com.fastchat.FastChat.server;
+import com.fastchat.FastChat.beans.User;
+
+import java.io.IOException;
import java.net.InetAddress;
+import java.net.Socket;
import java.util.ArrayList;
import java.util.Random;
-public class ServerClient {
-
- public String name;
- public InetAddress ip;
- public int port;
- private final int ID;
- public int attempt = 0;
- private static ArrayList identifiers = new ArrayList();
-
- public int getID() {
- return ID;
+public class ServerClient extends User {
+
+ private static final ArrayList identifiers = new ArrayList<>();
+
+ private ServerClient(String name, InetAddress ip, int port, Socket socket) throws IOException {
+ setName(name);
+ setPort(port);
+ setIp(ip);
+ setSocket(socket);
+ setID(createId());
}
-
- public ServerClient(String name, InetAddress ip, int port) {
- super();
- this.name = name;
- this.ip = ip;
- this.port = port;
- this.ID = createId();
+
+ 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,18 +37,19 @@ private static int createId() {
return createId();
}
}
-
+
public static void removeId(int id) {
for (Integer integer : identifiers) {
if (integer.equals(id)) {
identifiers.remove(integer);
- return;
+ break;
}
}
}
-
+
@Override
public String toString() {
- return (name != null ? name : "NONAME") + "(" + ID + ")" + (ip != null ? ip + ":" : "NOADDRESS") + port;
+ return (getName() != null ? getName() : "NONAME") + "(" + getID() + ")";
+// + (ip != null ? ip + ":" : "NOADDRESS") + 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
new file mode 100644
index 0000000..58535da
--- /dev/null
+++ b/src/main/java/com/fastchat/FastChat/util/Command.java
@@ -0,0 +1,54 @@
+package com.fastchat.FastChat.util;
+
+
+public class Command {
+ private final String help;
+ private final String name;
+
+ //it's Interface created for body
+ private final 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;
+ }
+
+
+}
+
+
+
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..ad10b9a
--- /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 final HashMap registry = new HashMap<>();
+
+ public void add(Command... commands) {
+ for (Command command : commands)
+ registry.put(command.getName(), command);
+ }
+
+ public void run(String name, String... args) throws IllegalArgumentException {
+ Command c = registry.get(name);
+
+ if (c != null) {
+ c.exec(args);
+ } else {
+ throw new IllegalArgumentException("Command does not exist!");
+ }
+ }
+
+ public HashMap getRegistry() {
+ return registry;
+ }
+}
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/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/Localization.java b/src/main/java/com/fastchat/FastChat/util/Localization.java
new file mode 100644
index 0000000..cb16730
--- /dev/null
+++ b/src/main/java/com/fastchat/FastChat/util/Localization.java
@@ -0,0 +1,56 @@
+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));
+ }
+
+ private static void setLocale(Locale locale) {
+ switch (locale.getLanguage()) {
+ case "uk":
+ setBundle("ukrainian");
+ break;
+ case "nb":
+ setBundle("norwegian");
+ 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..14fa159
--- /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;
+
+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/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/main/resources/FastChatLogo.png b/src/main/resources/FastChatLogo.png
new file mode 100644
index 0000000..d329695
Binary files /dev/null and b/src/main/resources/FastChatLogo.png differ
diff --git a/src/main/resources/FastChatLogo_filled.png b/src/main/resources/FastChatLogo_filled.png
new file mode 100644
index 0000000..65243c7
Binary files /dev/null and b/src/main/resources/FastChatLogo_filled.png differ
diff --git a/src/main/resources/default.properties b/src/main/resources/default.properties
new file mode 100644
index 0000000..335c963
--- /dev/null
+++ b/src/main/resources/default.properties
@@ -0,0 +1,11 @@
+send_btn_text=Send
+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..56aef24
--- /dev/null
+++ b/src/main/resources/norwegian.properties
@@ -0,0 +1,11 @@
+#send_btn_text=Send
+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
diff --git a/src/main/resources/ukrainian.properties b/src/main/resources/ukrainian.properties
new file mode 100644
index 0000000..2a8d8aa
--- /dev/null
+++ b/src/main/resources/ukrainian.properties
@@ -0,0 +1,11 @@
+send_btn_text=Надіслати
+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=Відправити
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
new file mode 100644
index 0000000..2b50dc2
--- /dev/null
+++ b/src/test/java/util/CommandTest.java
@@ -0,0 +1,65 @@
+package util;
+
+import com.fastchat.FastChat.util.Command;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+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 "Do you really need help? ";
+ });
+
+ @Test
+ public void CommandNotNull() {
+ assertNotNull(command.getName());
+ assertNotNull(command.getHelp());
+ assertNotNull(command.getBody());
+ }
+
+ @Test(dataProvider = "names", dataProviderClass = CommandTestData.class)
+ public void TestCommandName(String name, Command command) {
+ assertEquals(name, command.getName());
+ }
+
+ @Test
+ 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)}
+ };
+ }
+}