Compare commits
21 commits
93cf550a9b
...
e8807f28d3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8807f28d3 | ||
|
|
ada6167c25 | ||
|
|
b281b4b530 | ||
|
|
547c94110e | ||
|
|
4d21742261 | ||
|
|
2fc3a00264 | ||
|
|
65872c33aa | ||
| a4d2c48eb6 | |||
| 599a4a1121 | |||
|
|
298e01d09c | ||
|
|
60fa5adfe4 | ||
|
|
d51cbc6d75 | ||
| 172f9d48a3 | |||
| f41c7baa8b | |||
|
|
ebd292e6eb | ||
|
|
33f355b90b | ||
| 32364461be | |||
|
|
7bbaa4f8da | ||
|
|
034fb9203d | ||
|
|
04e7cfb5e3 | ||
|
|
b6d02eaaec |
3
.gitignore
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
.gradle
|
.gradle
|
||||||
**/build/
|
**/build/
|
||||||
!src/**/build/
|
!client/src/**/build/
|
||||||
|
|
||||||
# Ignore sqlite db files
|
# Ignore sqlite db files
|
||||||
/*.db
|
/*.db
|
||||||
|
|
@ -17,3 +17,4 @@ gradle-app.setting
|
||||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||||
# gradle/wrapper/gradle-wrapper.properties
|
# gradle/wrapper/gradle-wrapper.properties
|
||||||
/out/
|
/out/
|
||||||
|
/clavardator_stored_files/
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="14" />
|
<bytecodeTargetLevel target="11" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
24
.idea/dataSources.xml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
|
<data-source source="LOCAL" name="clavardator_test" uuid="ef1353b8-2399-4195-b9fc-3ae9b9c61351">
|
||||||
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/clavardator_test.db</jdbc-url>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
<data-source source="LOCAL" name="clavardator" uuid="9e12684a-a723-44a7-82c6-f07ab8912b44">
|
||||||
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/clavardator.db</jdbc-url>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
<libraries>
|
||||||
|
<library>
|
||||||
|
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.31.1/sqlite-jdbc-3.31.1.jar</url>
|
||||||
|
</library>
|
||||||
|
</libraries>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -9,6 +9,9 @@
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/client" />
|
||||||
|
<option value="$PROJECT_DIR$/lib" />
|
||||||
|
<option value="$PROJECT_DIR$/server" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
|
|
|
||||||
29
.idea/modules/clavardator.main.iml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module external.system.module.type="sourceSet" type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<output url="file://$MODULE_DIR$/../../build/classes/java/main" />
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$/../../client/src/main">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/../../client/src/main/java" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/../../client/src/main/resources" type="java-resource" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.jfoenix:jfoenix:9.0.10" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-fxml:linux:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-controls:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-controls:linux:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-graphics:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-graphics:linux:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-base:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-base:linux:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.jetbrains:annotations:20.1.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.xerial:sqlite-jdbc:3.32.3" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.kordamp.ikonli:ikonli-javafx:12.0.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.kordamp.ikonli:ikonli-fontawesome5-pack:12.0.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.kordamp.ikonli:ikonli-core:12.0.0" level="project" />
|
||||||
|
<orderEntry type="library" scope="RUNTIME" name="Gradle: org.openjfx:javafx-graphics:mac:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="RUNTIME" name="Gradle: org.openjfx:javafx-graphics:win:11.0.2" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
36
.idea/modules/clavardator.test.iml
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module external.system.module.type="sourceSet" type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<output-test url="file://$MODULE_DIR$/../../build/classes/java/test" />
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$/../../client/src/test">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/../../client/src/test/java" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/../../client/src/test/resources" type="java-test-resource" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="module" module-name="clavardator.main" />
|
||||||
|
<orderEntry type="library" name="Gradle: com.jfoenix:jfoenix:9.0.10" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-fxml:linux:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-controls:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-controls:linux:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-graphics:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-graphics:linux:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-base:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.openjfx:javafx-base:linux:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.jetbrains:annotations:20.1.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.xerial:sqlite-jdbc:3.32.3" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.kordamp.ikonli:ikonli-javafx:12.0.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.kordamp.ikonli:ikonli-fontawesome5-pack:12.0.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.json:json:20201115" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.junit.jupiter:junit-jupiter-api:5.7.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.kordamp.ikonli:ikonli-core:12.0.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.junit.platform:junit-platform-commons:1.7.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.apiguardian:apiguardian-api:1.1.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Gradle: org.opentest4j:opentest4j:1.2.0" level="project" />
|
||||||
|
<orderEntry type="library" scope="RUNTIME" name="Gradle: org.openjfx:javafx-graphics:mac:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="RUNTIME" name="Gradle: org.openjfx:javafx-graphics:win:11.0.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="RUNTIME" name="Gradle: org.junit.jupiter:junit-jupiter-engine:5.7.0" level="project" />
|
||||||
|
<orderEntry type="library" scope="RUNTIME" name="Gradle: org.junit.platform:junit-platform-engine:1.7.0" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="SqlDialectMappings">
|
<component name="SqlDialectMappings">
|
||||||
<file url="file://$PROJECT_DIR$/src/main/java/fr/insa/clavardator/db/DatabaseController.java" dialect="GenericSQL" />
|
<file url="file://$PROJECT_DIR$/client/src/main/java/fr/insa/clavardator/client/db/DatabaseController.java" dialect="GenericSQL" />
|
||||||
<file url="PROJECT" dialect="SQLite" />
|
<file url="PROJECT" dialect="SQLite" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
12
README.md
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
This project contains the source for the 4IR Java project.
|
This project contains the source for the 4IR Java project.
|
||||||
|
|
||||||
|
Full report available [here](report/report.pdf).
|
||||||
|
|
||||||
## Group
|
## Group
|
||||||
|
|
||||||
* SIMARD Yohan
|
* SIMARD Yohan
|
||||||
|
|
@ -31,10 +33,10 @@ Follow [this link](https://gluonhq.com/products/scene-builder/) to download and
|
||||||
Run this command
|
Run this command
|
||||||
|
|
||||||
```shell script
|
```shell script
|
||||||
./gradlew run
|
./gradlew runShadow
|
||||||
```
|
```
|
||||||
|
|
||||||
Or in Intellij, open the gradle window and click on `clavardator -> Tasks -> application -> run`.
|
Or in Intellij, open the gradle window and click on `clavardator -> Tasks -> application -> runShadow`.
|
||||||
|
|
||||||
#### Generate cross-platform jar
|
#### Generate cross-platform jar
|
||||||
|
|
||||||
|
|
@ -44,11 +46,13 @@ Run this command
|
||||||
./gradlew build
|
./gradlew build
|
||||||
```
|
```
|
||||||
|
|
||||||
This will generate a jar file under `build/libs`.
|
This will generate a jar file under `client/build/libs/client-{VERSION}-all.jar`.
|
||||||
|
|
||||||
You can then copy this file and place it on any Linux/Windows/Mac environment with at least Java 11 installed.
|
You can then copy this file and place it on any Linux/Windows/Mac environment with at least Java 11 installed.
|
||||||
|
|
||||||
You can then run this jar file with this command:
|
#### Running a JAR file
|
||||||
|
|
||||||
|
You can run a jar file with this command:
|
||||||
|
|
||||||
```shell script
|
```shell script
|
||||||
java -jar <JAR-NAME>.jar
|
java -jar <JAR-NAME>.jar
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ plugins {
|
||||||
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
group 'fr.insa.clavardator'
|
group 'fr.insa.clavardator.client'
|
||||||
version '0.0.1'
|
version '0.0.1'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
|
@ -17,6 +17,7 @@ javafx {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation project(':lib')
|
||||||
implementation 'org.jetbrains:annotations:20.1.0'
|
implementation 'org.jetbrains:annotations:20.1.0'
|
||||||
runtimeOnly "org.openjfx:javafx-graphics:$javafx.version:win"
|
runtimeOnly "org.openjfx:javafx-graphics:$javafx.version:win"
|
||||||
runtimeOnly "org.openjfx:javafx-graphics:$javafx.version:linux"
|
runtimeOnly "org.openjfx:javafx-graphics:$javafx.version:linux"
|
||||||
|
|
@ -35,16 +36,7 @@ test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
mainClassName = 'fr.insa.clavardator.Launcher'
|
mainClassName = 'fr.insa.clavardator.client.Launcher'
|
||||||
|
|
||||||
jar {
|
|
||||||
manifest {
|
|
||||||
attributes 'Main-Class': 'fr.insa.clavardator.Launcher'
|
|
||||||
}
|
|
||||||
from {
|
|
||||||
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
mergeServiceFiles()
|
mergeServiceFiles()
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
{
|
{
|
||||||
"serveur": {
|
"serveur": {
|
||||||
"actif": 1,
|
"actif": 0,
|
||||||
"uri": "test",
|
"uri": "localhost",
|
||||||
"type": "INSA",
|
"type": "INSA",
|
||||||
"ports": {
|
"ports": {
|
||||||
"presence": 35650,
|
"presence": 35650,
|
||||||
"proxy": 35750
|
"proxy": 35750
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"local": {
|
||||||
|
"actif": 1,
|
||||||
|
"port": 31590
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package fr.insa.clavardator;
|
package fr.insa.clavardator.client;
|
||||||
|
|
||||||
public class Launcher {
|
public class Launcher {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
package fr.insa.clavardator;
|
package fr.insa.clavardator.client;
|
||||||
|
|
||||||
import fr.insa.clavardator.ui.MainController;
|
import fr.insa.clavardator.client.ui.MainController;
|
||||||
import fr.insa.clavardator.users.UserList;
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
import fr.insa.clavardator.util.Log;
|
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
package fr.insa.clavardator.chat;
|
package fr.insa.clavardator.client.chat;
|
||||||
|
|
||||||
import fr.insa.clavardator.db.DatabaseController;
|
import fr.insa.clavardator.client.db.DatabaseController;
|
||||||
import fr.insa.clavardator.users.CurrentUser;
|
import fr.insa.clavardator.client.users.CurrentUser;
|
||||||
import fr.insa.clavardator.users.PeerUser;
|
import fr.insa.clavardator.client.users.PeerUser;
|
||||||
import fr.insa.clavardator.users.UserInformation;
|
import fr.insa.clavardator.lib.message.Message;
|
||||||
import fr.insa.clavardator.util.ErrorCallback;
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
import fr.insa.clavardator.util.Log;
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
package fr.insa.clavardator.config;
|
package fr.insa.clavardator.client.config;
|
||||||
|
|
||||||
import fr.insa.clavardator.server.PresenceType;
|
import fr.insa.clavardator.client.server.ServerType;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
@SuppressWarnings("FieldCanBeLocal")
|
@SuppressWarnings("FieldCanBeLocal")
|
||||||
public class Config {
|
public class Config {
|
||||||
|
|
||||||
private ServerConfig serverConfig;
|
|
||||||
private final String SERVER_KEY = "serveur";
|
private final String SERVER_KEY = "serveur";
|
||||||
|
private final String LOCAL_KEY = "local";
|
||||||
|
private ServerConfig serverConfig;
|
||||||
|
private LocalConfig localConfig;
|
||||||
|
|
||||||
public Config() {
|
public Config() {
|
||||||
serverConfig = new ServerConfig();
|
serverConfig = new ServerConfig();
|
||||||
|
localConfig = new LocalConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Config(JSONObject obj) {
|
public Config(JSONObject obj) {
|
||||||
|
|
@ -19,29 +22,35 @@ public class Config {
|
||||||
if (obj.has(SERVER_KEY)) {
|
if (obj.has(SERVER_KEY)) {
|
||||||
this.serverConfig = new ServerConfig(obj.getJSONObject(SERVER_KEY));
|
this.serverConfig = new ServerConfig(obj.getJSONObject(SERVER_KEY));
|
||||||
}
|
}
|
||||||
|
if (obj.has(LOCAL_KEY)) {
|
||||||
|
this.localConfig = new LocalConfig(obj.getJSONObject(LOCAL_KEY));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServerConfig getServerConfig() {
|
public ServerConfig getServerConfig() {
|
||||||
return serverConfig;
|
return serverConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LocalConfig getLocalConfig() {
|
||||||
|
return localConfig;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("FieldCanBeLocal")
|
@SuppressWarnings("FieldCanBeLocal")
|
||||||
public static class ServerConfig {
|
public static class ServerConfig {
|
||||||
|
public static final int DEFAULT_PRESENCE_PORT = 35650;
|
||||||
private JSONObject obj;
|
public static final int DEFAULT_PROXY_PORT = 35750;
|
||||||
|
|
||||||
private boolean enabled;
|
|
||||||
private String uri;
|
|
||||||
private PresenceType type;
|
|
||||||
private int presencePort;
|
|
||||||
private int proxyPort;
|
|
||||||
|
|
||||||
private final String ENABLED_KEY = "actif";
|
private final String ENABLED_KEY = "actif";
|
||||||
private final String URI_KEY = "uri";
|
private final String URI_KEY = "uri";
|
||||||
private final String TYPE_KEY = "type";
|
private final String TYPE_KEY = "type";
|
||||||
private final String PORTS_KEY = "ports";
|
private final String PORTS_KEY = "ports";
|
||||||
private final String PORT_PRESENCE_KEY = "presence";
|
private final String PORT_PRESENCE_KEY = "presence";
|
||||||
private final String PORT_PROXY_KEY = "proxy";
|
private final String PORT_PROXY_KEY = "proxy";
|
||||||
|
private JSONObject obj;
|
||||||
|
private boolean enabled;
|
||||||
|
private String uri;
|
||||||
|
private ServerType type;
|
||||||
|
private int presencePort;
|
||||||
|
private int proxyPort;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic constructor setting the default server configuration
|
* Basic constructor setting the default server configuration
|
||||||
|
|
@ -49,7 +58,7 @@ public class Config {
|
||||||
public ServerConfig() {
|
public ServerConfig() {
|
||||||
enabled = false;
|
enabled = false;
|
||||||
uri = "";
|
uri = "";
|
||||||
type = PresenceType.INSA;
|
type = ServerType.INSA;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -102,9 +111,9 @@ public class Config {
|
||||||
*/
|
*/
|
||||||
private void readServerType() {
|
private void readServerType() {
|
||||||
try {
|
try {
|
||||||
type = obj.getEnum(PresenceType.class, TYPE_KEY);
|
type = obj.getEnum(ServerType.class, TYPE_KEY);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
type = PresenceType.INSA;
|
type = ServerType.INSA;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,8 +123,8 @@ public class Config {
|
||||||
presencePort = portsObj.getInt(PORT_PRESENCE_KEY);
|
presencePort = portsObj.getInt(PORT_PRESENCE_KEY);
|
||||||
proxyPort = portsObj.getInt(PORT_PROXY_KEY);
|
proxyPort = portsObj.getInt(PORT_PROXY_KEY);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
presencePort = 30000; // TODO set default ports
|
presencePort = DEFAULT_PRESENCE_PORT;
|
||||||
proxyPort = 300001;
|
proxyPort = DEFAULT_PROXY_PORT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,7 +142,7 @@ public class Config {
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PresenceType getType() {
|
public ServerType getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,4 +154,70 @@ public class Config {
|
||||||
return proxyPort;
|
return proxyPort;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("FieldCanBeLocal")
|
||||||
|
public static class LocalConfig {
|
||||||
|
public static final int DEFAULT_TCP_PORT = 31598;
|
||||||
|
private final String ENABLED_KEY = "actif";
|
||||||
|
private final String PORT_KEY = "port";
|
||||||
|
private JSONObject obj;
|
||||||
|
private boolean enabled;
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic constructor setting the default local configuration
|
||||||
|
*/
|
||||||
|
public LocalConfig() {
|
||||||
|
enabled = true;
|
||||||
|
port = DEFAULT_TCP_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to read the given JSON object to extract custom user configuration
|
||||||
|
*
|
||||||
|
* @param obj THe JSON object to read config from
|
||||||
|
*/
|
||||||
|
public LocalConfig(JSONObject obj) {
|
||||||
|
this();
|
||||||
|
this.obj = obj;
|
||||||
|
readEnabled();
|
||||||
|
if (enabled) {
|
||||||
|
readServerPort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads if local mode is enabled.
|
||||||
|
* Uses true by default.
|
||||||
|
*/
|
||||||
|
private void readEnabled() {
|
||||||
|
try {
|
||||||
|
enabled = obj.getInt(ENABLED_KEY) == 1;
|
||||||
|
} catch (JSONException e) {
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readServerPort() {
|
||||||
|
try {
|
||||||
|
port = obj.getInt(PORT_KEY);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
port = DEFAULT_TCP_PORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the local mode is enabled.
|
||||||
|
* If this returns false, all other data returned by this object can be ignored.
|
||||||
|
*
|
||||||
|
* @return True if enabled, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package fr.insa.clavardator.config;
|
package fr.insa.clavardator.client.config;
|
||||||
|
|
||||||
import fr.insa.clavardator.util.Log;
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.json.JSONTokener;
|
import org.json.JSONTokener;
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
package fr.insa.clavardator.db;
|
package fr.insa.clavardator.client.db;
|
||||||
|
|
||||||
import fr.insa.clavardator.chat.FileMessage;
|
import fr.insa.clavardator.client.users.CurrentUser;
|
||||||
import fr.insa.clavardator.chat.Message;
|
import fr.insa.clavardator.lib.message.FileMessage;
|
||||||
import fr.insa.clavardator.users.User;
|
import fr.insa.clavardator.lib.message.Message;
|
||||||
import fr.insa.clavardator.users.UserInformation;
|
import fr.insa.clavardator.lib.users.User;
|
||||||
import fr.insa.clavardator.util.ErrorCallback;
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
import fr.insa.clavardator.util.Log;
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
import org.intellij.lang.annotations.Language;
|
import org.intellij.lang.annotations.Language;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
|
@ -274,8 +275,6 @@ public class DatabaseController {
|
||||||
chatHistory.add(new FileMessage(sender, recipient, date, text, filePath));
|
chatHistory.add(new FileMessage(sender, recipient, date, text, filePath));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(getClass().getSimpleName(), "Error while opening the file", e);
|
Log.e(getClass().getSimpleName(), "Error while opening the file", e);
|
||||||
errorCallback.onError(e);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -314,11 +313,17 @@ public class DatabaseController {
|
||||||
public void addMessage(Message message, @Nullable DatabaseController.UpdateCallback callback, ErrorCallback errorCallback) {
|
public void addMessage(Message message, @Nullable DatabaseController.UpdateCallback callback, ErrorCallback errorCallback) {
|
||||||
// Insert the correspondent if not already in the database
|
// Insert the correspondent if not already in the database
|
||||||
Log.v(getClass().getSimpleName(), "Inserting correspondent into db... ");
|
Log.v(getClass().getSimpleName(), "Inserting correspondent into db... ");
|
||||||
addUser(message.getCorrespondent(), () -> {
|
UserInformation correspondent;
|
||||||
|
if (CurrentUser.getInstance().getId() != null && CurrentUser.getInstance().getId().equals(message.getSender().id)) {
|
||||||
|
correspondent = message.getRecipient();
|
||||||
|
} else {
|
||||||
|
correspondent = message.getSender();
|
||||||
|
}
|
||||||
|
addUser(correspondent, () -> {
|
||||||
// Handle messages containing a file
|
// Handle messages containing a file
|
||||||
String filePath = null;
|
String filePath = null;
|
||||||
if (message instanceof FileMessage) {
|
if (message instanceof FileMessage) {
|
||||||
filePath = "'" + ((FileMessage) message).getPath() + "'";
|
filePath = ((FileMessage) message).getPath();
|
||||||
}
|
}
|
||||||
@Language("SQL") String sql = "INSERT INTO message " +
|
@Language("SQL") String sql = "INSERT INTO message " +
|
||||||
"(timestamp, sender, recipient, text, file_path) VALUES (?, ?, ?, ?, ?)";
|
"(timestamp, sender, recipient, text, file_path) VALUES (?, ?, ?, ?, ?)";
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
package fr.insa.clavardator.network;
|
package fr.insa.clavardator.client.network;
|
||||||
|
|
||||||
import fr.insa.clavardator.util.ErrorCallback;
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
import fr.insa.clavardator.util.Log;
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.*;
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.SocketException;
|
||||||
|
|
||||||
import static fr.insa.clavardator.network.NetUtil.isLocalAddress;
|
import static fr.insa.clavardator.client.network.NetUtil.isLocalAddress;
|
||||||
|
|
||||||
public class NetDiscoverer {
|
public class NetDiscoverer {
|
||||||
private static final short DISCOVERY_PORT = 31593;
|
private static final short DISCOVERY_PORT = 31593;
|
||||||
|
|
@ -86,6 +89,14 @@ public class NetDiscoverer {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private interface BroadcastReceivedCallback {
|
||||||
|
void onBroadcastReceived(InetAddress ipAddr, String data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ResponseReceivedCallback {
|
||||||
|
void onResponseReceived(InetAddress ipAddr, String data);
|
||||||
|
}
|
||||||
|
|
||||||
private static class BroadcastSender extends Thread {
|
private static class BroadcastSender extends Thread {
|
||||||
private final String broadcastMessage;
|
private final String broadcastMessage;
|
||||||
private final ErrorCallback errorCallback;
|
private final ErrorCallback errorCallback;
|
||||||
|
|
@ -104,16 +115,22 @@ public class NetDiscoverer {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
byte[] buf = broadcastMessage.getBytes();
|
byte[] buf = broadcastMessage.getBytes();
|
||||||
|
DatagramSocket broadcastSocket = null;
|
||||||
try {
|
try {
|
||||||
for (InetAddress broadcastAddr : NetUtil.listAllBroadcastAddresses()) {
|
for (InetAddress broadcastAddr : NetUtil.listAllBroadcastAddresses()) {
|
||||||
DatagramSocket broadcastSocket = new DatagramSocket();
|
broadcastSocket = new DatagramSocket();
|
||||||
broadcastSocket.setBroadcast(true);
|
broadcastSocket.setBroadcast(true);
|
||||||
broadcastSocket.send(new DatagramPacket(buf, buf.length, broadcastAddr, DISCOVERY_PORT));
|
broadcastSocket.send(new DatagramPacket(buf, buf.length, broadcastAddr, DISCOVERY_PORT));
|
||||||
Log.v(this.getClass().getSimpleName(), "Broadcast sent on address " + broadcastAddr.toString());
|
Log.v(this.getClass().getSimpleName(), "Broadcast sent on address " + broadcastAddr.toString());
|
||||||
|
broadcastSocket.close();
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(this.getClass().getSimpleName(), "Error sending broadcast", e);
|
Log.e(this.getClass().getSimpleName(), "Error sending broadcast", e);
|
||||||
errorCallback.onError(e);
|
errorCallback.onError(e);
|
||||||
|
} finally {
|
||||||
|
if (broadcastSocket != null) {
|
||||||
|
broadcastSocket.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -138,15 +155,11 @@ public class NetDiscoverer {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
socket = new DatagramSocket(DISCOVERY_PORT);
|
||||||
|
byte[] buffer = new byte[BROADCAST_BUFFER_SIZE];
|
||||||
|
|
||||||
while (!shouldStop) {
|
while (!shouldStop) {
|
||||||
socket = new DatagramSocket(null);
|
|
||||||
socket.setOption(StandardSocketOptions.SO_REUSEPORT, true);
|
|
||||||
socket.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
|
||||||
socket.bind(new InetSocketAddress((InetAddress) null, DISCOVERY_PORT));
|
|
||||||
|
|
||||||
byte[] buffer = new byte[BROADCAST_BUFFER_SIZE];
|
|
||||||
DatagramPacket receivedPacket = new DatagramPacket(buffer, BROADCAST_BUFFER_SIZE);
|
DatagramPacket receivedPacket = new DatagramPacket(buffer, BROADCAST_BUFFER_SIZE);
|
||||||
|
|
||||||
socket.receive(receivedPacket);
|
socket.receive(receivedPacket);
|
||||||
Log.v(this.getClass().getSimpleName(), "Broadcast received from ip " + receivedPacket.getAddress().toString());
|
Log.v(this.getClass().getSimpleName(), "Broadcast received from ip " + receivedPacket.getAddress().toString());
|
||||||
callback.onBroadcastReceived(receivedPacket.getAddress(), new String(receivedPacket.getData()));
|
callback.onBroadcastReceived(receivedPacket.getAddress(), new String(receivedPacket.getData()));
|
||||||
|
|
@ -156,6 +169,9 @@ public class NetDiscoverer {
|
||||||
Log.e(this.getClass().getSimpleName(), "Error receiving broadcast message", e);
|
Log.e(this.getClass().getSimpleName(), "Error receiving broadcast message", e);
|
||||||
errorCallback.onError(e);
|
errorCallback.onError(e);
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
if (socket != null)
|
||||||
|
socket.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,8 +202,7 @@ public class NetDiscoverer {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
byte[] buf = message.getBytes();
|
byte[] buf = message.getBytes();
|
||||||
try {
|
try (DatagramSocket responseSocket = new DatagramSocket()) {
|
||||||
DatagramSocket responseSocket = new DatagramSocket();
|
|
||||||
responseSocket.send(new DatagramPacket(buf, buf.length, address, RESPONSE_PORT));
|
responseSocket.send(new DatagramPacket(buf, buf.length, address, RESPONSE_PORT));
|
||||||
Log.v(this.getClass().getSimpleName(), "Broadcast response sent to ip " + address.toString());
|
Log.v(this.getClass().getSimpleName(), "Broadcast response sent to ip " + address.toString());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
@ -217,12 +232,8 @@ public class NetDiscoverer {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
socket = new DatagramSocket(RESPONSE_PORT);
|
||||||
while (!shouldStop) {
|
while (!shouldStop) {
|
||||||
socket = new DatagramSocket(null);
|
|
||||||
socket.setOption(StandardSocketOptions.SO_REUSEPORT, true);
|
|
||||||
socket.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
|
||||||
socket.bind(new InetSocketAddress((InetAddress) null, RESPONSE_PORT));
|
|
||||||
|
|
||||||
byte[] buffer = new byte[RESPONSE_BUFFER_SIZE];
|
byte[] buffer = new byte[RESPONSE_BUFFER_SIZE];
|
||||||
DatagramPacket receivedPacket = new DatagramPacket(buffer, RESPONSE_BUFFER_SIZE);
|
DatagramPacket receivedPacket = new DatagramPacket(buffer, RESPONSE_BUFFER_SIZE);
|
||||||
socket.receive(receivedPacket);
|
socket.receive(receivedPacket);
|
||||||
|
|
@ -243,13 +254,4 @@ public class NetDiscoverer {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private interface BroadcastReceivedCallback {
|
|
||||||
void onBroadcastReceived(InetAddress ipAddr, String data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ResponseReceivedCallback {
|
|
||||||
void onResponseReceived(InetAddress ipAddr, String data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package fr.insa.clavardator.network;
|
package fr.insa.clavardator.client.network;
|
||||||
|
|
||||||
import fr.insa.clavardator.util.Log;
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InterfaceAddress;
|
import java.net.InterfaceAddress;
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package fr.insa.clavardator.network;
|
package fr.insa.clavardator.client.network;
|
||||||
|
|
||||||
import fr.insa.clavardator.errors.UsernameTakenException;
|
import fr.insa.clavardator.client.users.CurrentUser;
|
||||||
import fr.insa.clavardator.users.CurrentUser;
|
import fr.insa.clavardator.lib.errors.UsernameTakenException;
|
||||||
import fr.insa.clavardator.users.UserInformation;
|
import fr.insa.clavardator.lib.network.TcpConnection;
|
||||||
import fr.insa.clavardator.util.ErrorCallback;
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
import fr.insa.clavardator.util.Log;
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
@ -14,12 +15,13 @@ public class PeerHandshake {
|
||||||
private TcpConnection connection;
|
private TcpConnection connection;
|
||||||
private UserInformation userInformation;
|
private UserInformation userInformation;
|
||||||
|
|
||||||
public void createConnection(InetAddress ipAddr, UserConnectedCallback callback, ErrorCallback errorCallback) {
|
public void createConnection(InetAddress ipAddr, int port, UserConnectedCallback callback, ErrorCallback errorCallback) {
|
||||||
closeConnection();
|
closeConnection();
|
||||||
Log.v(this.getClass().getSimpleName(), "Creating new TCP connection ");
|
Log.v(this.getClass().getSimpleName(), "Creating new TCP connection ");
|
||||||
|
|
||||||
connection = new TcpConnection(
|
connection = new TcpConnection(
|
||||||
ipAddr,
|
ipAddr,
|
||||||
|
port,
|
||||||
(thisConnection) -> init(thisConnection,
|
(thisConnection) -> init(thisConnection,
|
||||||
false,
|
false,
|
||||||
callback,
|
callback,
|
||||||
|
|
@ -70,7 +72,9 @@ public class PeerHandshake {
|
||||||
|
|
||||||
private void sendUsernameTaken(TcpConnection thisConnection) {
|
private void sendUsernameTaken(TcpConnection thisConnection) {
|
||||||
Log.v(this.getClass().getSimpleName(), "Received username request using current username");
|
Log.v(this.getClass().getSimpleName(), "Received username request using current username");
|
||||||
thisConnection.send(new UsernameTakenException("Username taken"), this::closeConnection, null);
|
UsernameTakenException exception = new UsernameTakenException("Username taken",
|
||||||
|
CurrentUser.getInstance().getId(), userInformation.id);
|
||||||
|
thisConnection.send(exception, this::closeConnection, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package fr.insa.clavardator.client.server;
|
||||||
|
|
||||||
|
public enum ServerType {
|
||||||
|
INSA,
|
||||||
|
TEST,
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
package fr.insa.clavardator.server;
|
package fr.insa.clavardator.client.server.presence;
|
||||||
|
|
||||||
import fr.insa.clavardator.network.TcpConnection;
|
import fr.insa.clavardator.client.server.proxy.InsaProxy;
|
||||||
import fr.insa.clavardator.users.CurrentUser;
|
import fr.insa.clavardator.client.server.proxy.Proxy;
|
||||||
import fr.insa.clavardator.users.UserInformation;
|
import fr.insa.clavardator.lib.message.Message;
|
||||||
import fr.insa.clavardator.util.ErrorCallback;
|
import fr.insa.clavardator.lib.network.TcpConnection;
|
||||||
import fr.insa.clavardator.util.Log;
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
import fr.insa.clavardator.util.ParametrizedCallback;
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
import fr.insa.clavardator.util.SimpleCallback;
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
|
import fr.insa.clavardator.lib.util.ParametrizedCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.SimpleCallback;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
|
@ -26,31 +28,28 @@ import java.util.Arrays;
|
||||||
* such as subscribe, unsubscribe, publish, and receive notifications.
|
* such as subscribe, unsubscribe, publish, and receive notifications.
|
||||||
* <br/>
|
* <br/>
|
||||||
* On the proxy port, the client will be able to send regular
|
* On the proxy port, the client will be able to send regular
|
||||||
* {@link fr.insa.clavardator.chat.Message messages} like on the local network.
|
* {@link Message messages} like on the local network.
|
||||||
* The proxy will forward the message to the appropriate recipient using the provided id.
|
* The proxy will forward the message to the appropriate recipient using the provided id.
|
||||||
*/
|
*/
|
||||||
public class InsaPresence implements Presence {
|
public class InsaPresence implements Presence {
|
||||||
|
|
||||||
private final String path;
|
private final String path;
|
||||||
private final int presencePort;
|
private final int presencePort;
|
||||||
private final int proxyPort;
|
private final Proxy proxy;
|
||||||
|
|
||||||
private TcpConnection presenceConnection;
|
private TcpConnection presenceConnection;
|
||||||
private TcpConnection proxyConnection;
|
|
||||||
|
|
||||||
|
|
||||||
public InsaPresence(String path, int presencePort, int proxyPort) {
|
public InsaPresence(String path, int presencePort, int proxyPort) {
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.presencePort = presencePort;
|
this.presencePort = presencePort;
|
||||||
this.proxyPort = proxyPort;
|
this.proxy = new InsaProxy(path, proxyPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void subscribe(ParametrizedCallback<ArrayList<UserInformation>> callback, ErrorCallback errorCallback) {
|
public void subscribe(UserInformation userInformation, ParametrizedCallback<ArrayList<UserInformation>> callback, ErrorCallback errorCallback) {
|
||||||
if (!isConnected()) {
|
if (!isConnected()) {
|
||||||
connectToPresence(
|
connectToPresence(
|
||||||
() -> connectToProxy(() -> {
|
() -> proxy.connect(() -> {
|
||||||
sendSubscribeMessage(errorCallback);
|
notify(userInformation, null, errorCallback);
|
||||||
receiveSubscribeNotifications(callback, errorCallback);
|
receiveSubscribeNotifications(callback, errorCallback);
|
||||||
}, errorCallback),
|
}, errorCallback),
|
||||||
errorCallback);
|
errorCallback);
|
||||||
|
|
@ -59,15 +58,6 @@ public class InsaPresence implements Presence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Send current user information to tell the server who is subscribing
|
|
||||||
*
|
|
||||||
* @param errorCallback Called on connection error
|
|
||||||
*/
|
|
||||||
private void sendSubscribeMessage(ErrorCallback errorCallback) {
|
|
||||||
presenceConnection.send(new UserInformation(CurrentUser.getInstance()), null, errorCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for presence server response to the subscribe request
|
* Waits for presence server response to the subscribe request
|
||||||
*
|
*
|
||||||
|
|
@ -82,14 +72,19 @@ public class InsaPresence implements Presence {
|
||||||
final ArrayList<?> list = (ArrayList<?>) msg;
|
final ArrayList<?> list = (ArrayList<?>) msg;
|
||||||
for (Object obj : list) {
|
for (Object obj : list) {
|
||||||
if (obj instanceof UserInformation) {
|
if (obj instanceof UserInformation) {
|
||||||
userList.add((UserInformation)obj);
|
userList.add((UserInformation) obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
Log.v(getClass().getSimpleName(), "Receive subscribe response: " + msg);
|
||||||
Log.v(getClass().getSimpleName(), "Receive subscribe response: " + msg);
|
Log.v(getClass().getSimpleName(), "Converted list: " + Arrays.toString(userList.toArray()));
|
||||||
Log.v(getClass().getSimpleName(), "Converted list: " + Arrays.toString(userList.toArray()));
|
if (callback != null) {
|
||||||
if (callback != null) {
|
callback.call(userList);
|
||||||
callback.call(userList);
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(getClass().getSimpleName(), "Unexpected object received: " + msg.getClass().getSimpleName());
|
||||||
|
if (errorCallback != null) {
|
||||||
|
errorCallback.onError(new RuntimeException("Unexpected object received"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, errorCallback);
|
}, errorCallback);
|
||||||
}
|
}
|
||||||
|
|
@ -103,6 +98,11 @@ public class InsaPresence implements Presence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notify(UserInformation newInformation, @Nullable SimpleCallback callback, @Nullable ErrorCallback errorCallback) {
|
||||||
|
presenceConnection.send(newInformation, callback, errorCallback);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects to the presence server by TCP
|
* Connects to the presence server by TCP
|
||||||
*
|
*
|
||||||
|
|
@ -124,27 +124,6 @@ public class InsaPresence implements Presence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Connects to the presence proxy by TCP
|
|
||||||
*
|
|
||||||
* @param callback Called when connection is successful
|
|
||||||
* @param errorCallback Called on connection error
|
|
||||||
*/
|
|
||||||
private void connectToProxy(SimpleCallback callback, ErrorCallback errorCallback) {
|
|
||||||
try {
|
|
||||||
proxyConnection = new TcpConnection(InetAddress.getByName(path),
|
|
||||||
proxyPort,
|
|
||||||
(newConnection) -> {
|
|
||||||
if (callback != null) {
|
|
||||||
callback.call();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
errorCallback);
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
Log.e(getClass().getSimpleName(), "Could not connect to presence proxy", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the given connection
|
* Closes the given connection
|
||||||
*/
|
*/
|
||||||
|
|
@ -162,8 +141,7 @@ public class InsaPresence implements Presence {
|
||||||
private boolean isConnected() {
|
private boolean isConnected() {
|
||||||
return presenceConnection != null &&
|
return presenceConnection != null &&
|
||||||
presenceConnection.isOpen() &&
|
presenceConnection.isOpen() &&
|
||||||
proxyConnection != null &&
|
proxy.isConnected();
|
||||||
proxyConnection.isOpen();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -172,13 +150,12 @@ public class InsaPresence implements Presence {
|
||||||
private void disconnect() {
|
private void disconnect() {
|
||||||
Log.v(this.getClass().getSimpleName(), "Disconnecting presence server");
|
Log.v(this.getClass().getSimpleName(), "Disconnecting presence server");
|
||||||
closeConnection(presenceConnection);
|
closeConnection(presenceConnection);
|
||||||
closeConnection(proxyConnection);
|
|
||||||
presenceConnection = null;
|
presenceConnection = null;
|
||||||
proxyConnection = null;
|
proxy.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TcpConnection getProxyConnection() {
|
public Proxy getProxy() {
|
||||||
return proxyConnection;
|
return proxy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
package fr.insa.clavardator.client.server.presence;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.client.server.proxy.Proxy;
|
||||||
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.ParametrizedCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.SimpleCallback;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface exposing public methods necessary for any presence server.
|
||||||
|
*
|
||||||
|
* @implNote Implement this interface when creating your own presence server class,
|
||||||
|
* then update the {@link PresenceFactory factory}
|
||||||
|
* to add your new implementation.
|
||||||
|
*/
|
||||||
|
public interface Presence {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes to this presence server notifications and publish current status.
|
||||||
|
* A list of Ids representing the current active users is returned.
|
||||||
|
*
|
||||||
|
* @param callback Called when subscription completes
|
||||||
|
*/
|
||||||
|
void subscribe(UserInformation userInformation, ParametrizedCallback<ArrayList<UserInformation>> callback, @Nullable ErrorCallback errorCallback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops subscription to the presence server by closing TCP connections.
|
||||||
|
* This will stop notifications.
|
||||||
|
*
|
||||||
|
* @implNote Call this before exiting the app.
|
||||||
|
* If not, the presence server will wait for a tcp timeout before marking this user as disconnected.
|
||||||
|
*/
|
||||||
|
void unsubscribe(SimpleCallback callback, @Nullable ErrorCallback errorCallback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the presence server of any changes to the current user
|
||||||
|
*
|
||||||
|
* @param newInformation The new information to send
|
||||||
|
* @param callback Called when notify completes
|
||||||
|
*/
|
||||||
|
void notify(UserInformation newInformation, @Nullable SimpleCallback callback, @Nullable ErrorCallback errorCallback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the proxy.
|
||||||
|
* This can be used to initialize a
|
||||||
|
* {@link fr.insa.clavardator.client.users.PeerUser Peeruser}
|
||||||
|
* and send messages like on a local network.
|
||||||
|
*
|
||||||
|
* @return The proxy
|
||||||
|
*/
|
||||||
|
Proxy getProxy();
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
package fr.insa.clavardator.server;
|
package fr.insa.clavardator.client.server.presence;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.client.server.ServerType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static factory class used to create concrete presence server implementations.
|
* Static factory class used to create concrete presence server implementations.
|
||||||
*/
|
*/
|
||||||
public class PresenceFactory {
|
public class PresenceFactory {
|
||||||
public static Presence create(PresenceType type, String uri, int serverPort, int proxyPort) throws UnknownPresenceException {
|
public static Presence create(ServerType type, String uri, int serverPort, int proxyPort) throws UnknownPresenceException {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case INSA:
|
case INSA:
|
||||||
return new InsaPresence(uri, serverPort, proxyPort);
|
return new InsaPresence(uri, serverPort, proxyPort);
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package fr.insa.clavardator.server;
|
package fr.insa.clavardator.client.server.presence;
|
||||||
|
|
||||||
public class UnknownPresenceException extends Exception {
|
public class UnknownPresenceException extends Exception {
|
||||||
public UnknownPresenceException() {
|
public UnknownPresenceException() {
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
package fr.insa.clavardator.client.server.proxy;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.client.users.CurrentUser;
|
||||||
|
import fr.insa.clavardator.lib.message.SenderInfo;
|
||||||
|
import fr.insa.clavardator.lib.network.TcpConnection;
|
||||||
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
|
import fr.insa.clavardator.lib.util.SimpleCallback;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class InsaProxy implements Proxy {
|
||||||
|
private final String path;
|
||||||
|
private final int proxyPort;
|
||||||
|
TcpConnection proxyConnection;
|
||||||
|
Map<String, TcpConnection.MessageReceivedCallback> callbackMap = new HashMap<>();
|
||||||
|
private boolean receiving = false;
|
||||||
|
|
||||||
|
public InsaProxy(String path, int proxyPort) {
|
||||||
|
this.path = path;
|
||||||
|
this.proxyPort = proxyPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connect(SimpleCallback callback, ErrorCallback errorCallback) {
|
||||||
|
try {
|
||||||
|
proxyConnection = new TcpConnection(InetAddress.getByName(path), proxyPort,
|
||||||
|
(newConnection) -> newConnection.send(new UserInformation(CurrentUser.getInstance()), () -> {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.call();
|
||||||
|
}
|
||||||
|
}, errorCallback),
|
||||||
|
errorCallback);
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
Log.e(getClass().getSimpleName(), "Could not connect to the proxy", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(Serializable message, @Nullable SimpleCallback callback, @Nullable ErrorCallback errorCallback) {
|
||||||
|
proxyConnection.send(message, callback, errorCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void receive(String userId, TcpConnection.MessageReceivedCallback callback, ErrorCallback errorCallback) {
|
||||||
|
Log.v(getClass().getSimpleName(), "Registering a new receive request from " + userId);
|
||||||
|
callbackMap.put(userId, callback);
|
||||||
|
if (!receiving) {
|
||||||
|
proxyConnection.receive(this::onMessageReceived, errorCallback);
|
||||||
|
receiving = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onMessageReceived(Object msg) throws IOException {
|
||||||
|
if (msg instanceof SenderInfo) {
|
||||||
|
TcpConnection.MessageReceivedCallback callback = callbackMap.get(((SenderInfo) msg).getSenderId());
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onMessageReceived(msg);
|
||||||
|
} else {
|
||||||
|
Log.w(getClass().getSimpleName(),
|
||||||
|
"Nobody is waiting for messages from " + ((SenderInfo) msg).getSenderId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(getClass().getSimpleName(),
|
||||||
|
"Proxy sent a message that does not contain sender information: " +
|
||||||
|
msg.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return proxyConnection != null &&
|
||||||
|
proxyConnection.isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
proxyConnection.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnectUser(String id) {
|
||||||
|
callbackMap.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package fr.insa.clavardator.client.server.proxy;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.lib.network.TcpConnection;
|
||||||
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.SimpleCallback;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public interface Proxy {
|
||||||
|
void connect(SimpleCallback callback, ErrorCallback errorCallback);
|
||||||
|
|
||||||
|
void send(Serializable message, @Nullable SimpleCallback callback, @Nullable ErrorCallback errorCallback);
|
||||||
|
|
||||||
|
void receive(String userId, TcpConnection.MessageReceivedCallback callback, ErrorCallback errorCallback);
|
||||||
|
|
||||||
|
boolean isConnected();
|
||||||
|
|
||||||
|
void close();
|
||||||
|
|
||||||
|
void disconnectUser(String id);
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
package fr.insa.clavardator.ui;
|
package fr.insa.clavardator.client.ui;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface used to create callbacks for button press events
|
* Interface used to create callbacks for button press events
|
||||||
*/
|
*/
|
||||||
public interface ButtonPressEvent {
|
public interface ButtonPressEvent {
|
||||||
public void onPress();
|
void onPress();
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package fr.insa.clavardator.ui;
|
package fr.insa.clavardator.client.ui;
|
||||||
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package fr.insa.clavardator.ui;
|
package fr.insa.clavardator.client.ui;
|
||||||
|
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
|
@ -35,6 +35,7 @@ public class LoadingScreenController implements Initializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantly shows the loading screen
|
* Instantly shows the loading screen
|
||||||
|
*
|
||||||
* @param label The text to display bellow the loading indicator
|
* @param label The text to display bellow the loading indicator
|
||||||
*/
|
*/
|
||||||
public void show(String label) {
|
public void show(String label) {
|
||||||
|
|
@ -51,6 +52,7 @@ public class LoadingScreenController implements Initializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the text to display bellow the loading indicator
|
* Sets the text to display bellow the loading indicator
|
||||||
|
*
|
||||||
* @param label The text to use
|
* @param label The text to use
|
||||||
*/
|
*/
|
||||||
public void setLabel(@Nullable String label) {
|
public void setLabel(@Nullable String label) {
|
||||||
|
|
@ -62,6 +64,7 @@ public class LoadingScreenController implements Initializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the main container styles to apply custom styling
|
* Gets the main container styles to apply custom styling
|
||||||
|
*
|
||||||
* @return The style list
|
* @return The style list
|
||||||
*/
|
*/
|
||||||
public ObservableList<String> getRootStyle() {
|
public ObservableList<String> getRootStyle() {
|
||||||
|
|
@ -1,21 +1,22 @@
|
||||||
package fr.insa.clavardator.ui;
|
package fr.insa.clavardator.client.ui;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXSnackbar;
|
import com.jfoenix.controls.JFXSnackbar;
|
||||||
import fr.insa.clavardator.config.Config;
|
import fr.insa.clavardator.client.config.Config;
|
||||||
import fr.insa.clavardator.config.ConfigLoader;
|
import fr.insa.clavardator.client.config.ConfigLoader;
|
||||||
import fr.insa.clavardator.db.DatabaseController;
|
import fr.insa.clavardator.client.db.DatabaseController;
|
||||||
import fr.insa.clavardator.server.Presence;
|
import fr.insa.clavardator.client.server.ServerType;
|
||||||
import fr.insa.clavardator.server.PresenceFactory;
|
import fr.insa.clavardator.client.server.presence.Presence;
|
||||||
import fr.insa.clavardator.server.PresenceType;
|
import fr.insa.clavardator.client.server.presence.PresenceFactory;
|
||||||
import fr.insa.clavardator.server.UnknownPresenceException;
|
import fr.insa.clavardator.client.server.presence.UnknownPresenceException;
|
||||||
import fr.insa.clavardator.ui.chat.ChatController;
|
import fr.insa.clavardator.client.ui.chat.ChatController;
|
||||||
import fr.insa.clavardator.ui.dialogs.AboutDialogController;
|
import fr.insa.clavardator.client.ui.dialogs.AboutDialogController;
|
||||||
import fr.insa.clavardator.ui.dialogs.EditUsernameDialogController;
|
import fr.insa.clavardator.client.ui.dialogs.EditUsernameDialogController;
|
||||||
import fr.insa.clavardator.ui.dialogs.SnackbarController;
|
import fr.insa.clavardator.client.ui.dialogs.SnackbarController;
|
||||||
import fr.insa.clavardator.ui.users.UserListController;
|
import fr.insa.clavardator.client.ui.users.UserListController;
|
||||||
import fr.insa.clavardator.users.CurrentUser;
|
import fr.insa.clavardator.client.users.CurrentUser;
|
||||||
import fr.insa.clavardator.users.UserList;
|
import fr.insa.clavardator.client.users.UserList;
|
||||||
import fr.insa.clavardator.util.Log;
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
|
|
@ -31,6 +32,7 @@ import java.util.TimerTask;
|
||||||
public class MainController implements Initializable {
|
public class MainController implements Initializable {
|
||||||
|
|
||||||
private final CurrentUser currentUser;
|
private final CurrentUser currentUser;
|
||||||
|
private final int DEFAULT_PORT = 31598;
|
||||||
@FXML
|
@FXML
|
||||||
private StackPane root;
|
private StackPane root;
|
||||||
@FXML
|
@FXML
|
||||||
|
|
@ -49,23 +51,42 @@ public class MainController implements Initializable {
|
||||||
private LoadingScreenController loadingController;
|
private LoadingScreenController loadingController;
|
||||||
@FXML
|
@FXML
|
||||||
private ErrorScreenController errorController;
|
private ErrorScreenController errorController;
|
||||||
|
|
||||||
private JFXSnackbar snackbar;
|
private JFXSnackbar snackbar;
|
||||||
private UserList userList;
|
private UserList userList;
|
||||||
private Presence presenceServer;
|
private Presence presenceServer;
|
||||||
private boolean online;
|
private boolean online;
|
||||||
|
private int port = DEFAULT_PORT;
|
||||||
|
private boolean localEnabled = true;
|
||||||
|
|
||||||
public MainController() {
|
public MainController() {
|
||||||
online = false;
|
online = false;
|
||||||
currentUser = CurrentUser.getInstance();
|
currentUser = CurrentUser.getInstance();
|
||||||
currentUser.addObserver(propertyChangeEvent -> {
|
currentUser.addObserver(propertyChangeEvent -> {
|
||||||
if (propertyChangeEvent.getPropertyName().equals("state")) {
|
final String propertyName = propertyChangeEvent.getPropertyName();
|
||||||
|
if (propertyName.equals("state")) {
|
||||||
final CurrentUser.State newState = (CurrentUser.State) propertyChangeEvent.getNewValue();
|
final CurrentUser.State newState = (CurrentUser.State) propertyChangeEvent.getNewValue();
|
||||||
onCurrentUserStateChange(newState);
|
onCurrentUserStateChange(newState);
|
||||||
|
} else if (propertyName.equals("username")) {
|
||||||
|
final String newUsername = (String) propertyChangeEvent.getNewValue();
|
||||||
|
onCurrentUserNameChange(newUsername);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onCurrentUserNameChange(String newUsername) {
|
||||||
|
if (online) {
|
||||||
|
if (userList != null) {
|
||||||
|
userList.propagateUsernameChange();
|
||||||
|
}
|
||||||
|
if (presenceServer != null) {
|
||||||
|
presenceServer.notify(
|
||||||
|
new UserInformation(CurrentUser.getInstance().getId(), newUsername),
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the current user becomes valid, start the chat.
|
* If the current user becomes valid, start the chat.
|
||||||
* If it is invalid or not set, show the login screen.
|
* If it is invalid or not set, show the login screen.
|
||||||
|
|
@ -149,11 +170,13 @@ public class MainController implements Initializable {
|
||||||
* If any error happens, disconnect the user from chat and ask for reconnection.
|
* If any error happens, disconnect the user from chat and ask for reconnection.
|
||||||
*/
|
*/
|
||||||
private void discoverActiveUsers() {
|
private void discoverActiveUsers() {
|
||||||
if (userList != null && online) {
|
if (userList != null && online && localEnabled) {
|
||||||
userList.discoverActiveUsers((e) -> {
|
userList.discoverActiveUsers(
|
||||||
Log.e(this.getClass().getSimpleName(), "Error discovering users", e);
|
port,
|
||||||
CurrentUser.getInstance().setState(CurrentUser.State.INVALID);
|
(e) -> {
|
||||||
});
|
Log.e(this.getClass().getSimpleName(), "Error discovering users", e);
|
||||||
|
CurrentUser.getInstance().setState(CurrentUser.State.INVALID);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,12 +185,14 @@ public class MainController implements Initializable {
|
||||||
* If any error happens, disconnect the user from chat and ask for reconnection.
|
* If any error happens, disconnect the user from chat and ask for reconnection.
|
||||||
*/
|
*/
|
||||||
private void startListening() {
|
private void startListening() {
|
||||||
if (userList != null) {
|
if (userList != null && online && localEnabled) {
|
||||||
userList.startDiscoveryListening();
|
userList.startDiscoveryListening();
|
||||||
userList.startUserListening((e) -> {
|
userList.startUserListening(
|
||||||
Log.e(this.getClass().getSimpleName(), "Error listening to users", e);
|
port,
|
||||||
CurrentUser.getInstance().setState(CurrentUser.State.INVALID);
|
(e) -> {
|
||||||
});
|
Log.e(this.getClass().getSimpleName(), "Error listening to users", e);
|
||||||
|
CurrentUser.getInstance().setState(CurrentUser.State.INVALID);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,25 +274,37 @@ public class MainController implements Initializable {
|
||||||
*/
|
*/
|
||||||
private void initBackend() {
|
private void initBackend() {
|
||||||
ConfigLoader.load(
|
ConfigLoader.load(
|
||||||
(Config config) -> new DatabaseController().initTables(
|
(Config config) -> {
|
||||||
() -> userList.retrievedPreviousUsers(
|
initLocalNetwork(config.getLocalConfig());
|
||||||
() -> currentUser.init(
|
new DatabaseController().initTables(
|
||||||
() -> initPresenceServer(config),
|
() -> userList.retrievedPreviousUsers(
|
||||||
this::onInitError),
|
() -> currentUser.init(
|
||||||
this::onInitError
|
() -> initPresenceServer(config.getServerConfig()),
|
||||||
), this::onInitError));
|
this::onInitError),
|
||||||
|
this::onInitError
|
||||||
|
), this::onInitError);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initLocalNetwork(Config.LocalConfig localConfig) {
|
||||||
|
port = localConfig.getPort();
|
||||||
|
localEnabled = localConfig.isEnabled();
|
||||||
|
if (localEnabled) {
|
||||||
|
Log.v(getClass().getSimpleName(), "Local network support enabled on port: " + port);
|
||||||
|
} else {
|
||||||
|
Log.v(getClass().getSimpleName(), "Local network support disabled");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the presence server based on user config
|
* Initializes the presence server based on user config
|
||||||
*
|
*
|
||||||
* @param config The user config
|
* @param serverConfig The server config
|
||||||
*/
|
*/
|
||||||
public void initPresenceServer(Config config) {
|
private void initPresenceServer(Config.ServerConfig serverConfig) {
|
||||||
final Config.ServerConfig serverConfig = config.getServerConfig();
|
|
||||||
if (serverConfig.isEnabled()) {
|
if (serverConfig.isEnabled()) {
|
||||||
try {
|
try {
|
||||||
final PresenceType type = serverConfig.getType();
|
final ServerType type = serverConfig.getType();
|
||||||
final String uri = serverConfig.getUri();
|
final String uri = serverConfig.getUri();
|
||||||
final int presencePort = serverConfig.getPresencePort();
|
final int presencePort = serverConfig.getPresencePort();
|
||||||
final int proxyPort = serverConfig.getProxyPort();
|
final int proxyPort = serverConfig.getProxyPort();
|
||||||
|
|
@ -286,13 +323,14 @@ public class MainController implements Initializable {
|
||||||
private void subscribeToPresenceServer() {
|
private void subscribeToPresenceServer() {
|
||||||
if (presenceServer != null && online) {
|
if (presenceServer != null && online) {
|
||||||
presenceServer.subscribe(
|
presenceServer.subscribe(
|
||||||
|
new UserInformation(CurrentUser.getInstance()),
|
||||||
param -> userList.onReceivePresenceNotification(
|
param -> userList.onReceivePresenceNotification(
|
||||||
param,
|
param,
|
||||||
presenceServer.getProxyConnection()),
|
presenceServer.getProxy()),
|
||||||
(e) -> Log.v(
|
(e) -> Log.v(
|
||||||
getClass().getSimpleName(),
|
getClass().getSimpleName(),
|
||||||
"Error subscribing to presence server",
|
"Error subscribing to presence server",
|
||||||
e));
|
e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package fr.insa.clavardator.ui;
|
package fr.insa.clavardator.client.ui;
|
||||||
|
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
package fr.insa.clavardator.ui;
|
package fr.insa.clavardator.client.ui;
|
||||||
|
|
||||||
import fr.insa.clavardator.users.CurrentUser;
|
import fr.insa.clavardator.client.users.CurrentUser;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -25,6 +23,7 @@ public class ToolbarController implements Initializable {
|
||||||
public void setEditListener(ButtonPressEvent listener) {
|
public void setEditListener(ButtonPressEvent listener) {
|
||||||
editListeners = listener;
|
editListeners = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAboutListener(ButtonPressEvent listener) {
|
public void setAboutListener(ButtonPressEvent listener) {
|
||||||
aboutListeners = listener;
|
aboutListeners = listener;
|
||||||
}
|
}
|
||||||
|
|
@ -34,6 +33,7 @@ public class ToolbarController implements Initializable {
|
||||||
editListeners.onPress();
|
editListeners.onPress();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onAboutPress() {
|
public void onAboutPress() {
|
||||||
if (aboutListeners != null) {
|
if (aboutListeners != null) {
|
||||||
aboutListeners.onPress();
|
aboutListeners.onPress();
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package fr.insa.clavardator.client.ui;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.client.users.PeerUser;
|
||||||
|
|
||||||
|
public interface UserSelectedEvent {
|
||||||
|
void onSelected(PeerUser user);
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
package fr.insa.clavardator.ui.chat;
|
package fr.insa.clavardator.client.ui.chat;
|
||||||
|
|
||||||
import fr.insa.clavardator.chat.ChatHistory;
|
import fr.insa.clavardator.client.chat.ChatHistory;
|
||||||
import fr.insa.clavardator.chat.Message;
|
import fr.insa.clavardator.client.ui.LoadingScreenController;
|
||||||
import fr.insa.clavardator.ui.LoadingScreenController;
|
import fr.insa.clavardator.client.ui.NoSelectionModel;
|
||||||
import fr.insa.clavardator.ui.NoSelectionModel;
|
import fr.insa.clavardator.client.users.PeerUser;
|
||||||
import fr.insa.clavardator.users.PeerUser;
|
import fr.insa.clavardator.lib.message.Message;
|
||||||
import fr.insa.clavardator.util.ErrorCallback;
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
import fr.insa.clavardator.util.Log;
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
|
@ -36,18 +36,19 @@ public class ChatController implements Initializable {
|
||||||
private VBox emptyContainer;
|
private VBox emptyContainer;
|
||||||
private PeerUser remoteUser;
|
private PeerUser remoteUser;
|
||||||
|
|
||||||
// public void setAttachmentListener(ButtonPressEvent listener) {
|
// public void setAttachmentListener(ButtonPressEvent listener) {
|
||||||
// chatFooterController.setAttachmentListener(listener);
|
// chatFooterController.setAttachmentListener(listener);
|
||||||
// }
|
// }
|
||||||
public void setSendErrorListener(ErrorCallback listener) {
|
public void setSendErrorListener(ErrorCallback listener) {
|
||||||
chatFooterController.setSendErrorListener(listener);
|
chatFooterController.setSendErrorListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the user that finished loading is the right one then set the chat state to done
|
* Check the user that finished loading is the right one then set the chat state to done
|
||||||
|
*
|
||||||
* @param user The user that finished loading
|
* @param user The user that finished loading
|
||||||
*/
|
*/
|
||||||
private void onHistoryLoaded (PeerUser user) {
|
private void onHistoryLoaded(PeerUser user) {
|
||||||
if (user.equals(remoteUser)) {
|
if (user.equals(remoteUser)) {
|
||||||
setState(State.DONE);
|
setState(State.DONE);
|
||||||
scrollToEnd();
|
scrollToEnd();
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package fr.insa.clavardator.ui.chat;
|
package fr.insa.clavardator.client.ui.chat;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
import com.jfoenix.controls.JFXButton;
|
||||||
import com.jfoenix.controls.JFXTextField;
|
import com.jfoenix.controls.JFXTextField;
|
||||||
import fr.insa.clavardator.users.PeerUser;
|
import fr.insa.clavardator.client.users.PeerUser;
|
||||||
import fr.insa.clavardator.util.ErrorCallback;
|
import fr.insa.clavardator.lib.users.UserState;
|
||||||
import fr.insa.clavardator.util.Log;
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
|
@ -23,6 +24,8 @@ import java.util.ResourceBundle;
|
||||||
* Controller for the chat input field and associated buttons
|
* Controller for the chat input field and associated buttons
|
||||||
*/
|
*/
|
||||||
public class ChatFooterController implements Initializable {
|
public class ChatFooterController implements Initializable {
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
File attachedFile;
|
||||||
@FXML
|
@FXML
|
||||||
private HBox container;
|
private HBox container;
|
||||||
@FXML
|
@FXML
|
||||||
|
|
@ -31,15 +34,10 @@ public class ChatFooterController implements Initializable {
|
||||||
private JFXButton sendButton;
|
private JFXButton sendButton;
|
||||||
@FXML
|
@FXML
|
||||||
private JFXButton attachButton;
|
private JFXButton attachButton;
|
||||||
|
|
||||||
private ErrorCallback sendErrorListeners;
|
private ErrorCallback sendErrorListeners;
|
||||||
|
|
||||||
private PeerUser remoteUser;
|
private PeerUser remoteUser;
|
||||||
private HashMap<PeerUser, String> savedText;
|
private HashMap<PeerUser, String> savedText;
|
||||||
|
|
||||||
FileChooser fileChooser = new FileChooser();
|
|
||||||
File attachedFile;
|
|
||||||
|
|
||||||
public void setSendErrorListener(ErrorCallback listener) {
|
public void setSendErrorListener(ErrorCallback listener) {
|
||||||
sendErrorListeners = listener;
|
sendErrorListeners = listener;
|
||||||
}
|
}
|
||||||
|
|
@ -157,8 +155,8 @@ public class ChatFooterController implements Initializable {
|
||||||
private void onUserStateChange(PropertyChangeEvent propertyChangeEvent) {
|
private void onUserStateChange(PropertyChangeEvent propertyChangeEvent) {
|
||||||
final String propName = propertyChangeEvent.getPropertyName();
|
final String propName = propertyChangeEvent.getPropertyName();
|
||||||
if (propName.equals("state")) {
|
if (propName.equals("state")) {
|
||||||
final PeerUser.State newState = (PeerUser.State) propertyChangeEvent.getNewValue();
|
final UserState newState = (UserState) propertyChangeEvent.getNewValue();
|
||||||
Platform.runLater(() -> setEnabled(newState == PeerUser.State.CONNECTED));
|
Platform.runLater(() -> setEnabled(newState == UserState.CONNECTED));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package fr.insa.clavardator.ui.chat;
|
package fr.insa.clavardator.client.ui.chat;
|
||||||
|
|
||||||
import fr.insa.clavardator.ui.users.UserActiveIndicatorController;
|
import fr.insa.clavardator.client.ui.users.UserActiveIndicatorController;
|
||||||
import fr.insa.clavardator.users.PeerUser;
|
import fr.insa.clavardator.client.users.PeerUser;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
|
|
@ -9,7 +9,6 @@ import javafx.scene.control.Label;
|
||||||
|
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -53,5 +52,6 @@ public class ChatHeaderController implements Initializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {}
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package fr.insa.clavardator.ui.chat;
|
package fr.insa.clavardator.client.ui.chat;
|
||||||
|
|
||||||
import fr.insa.clavardator.chat.Message;
|
import fr.insa.clavardator.lib.message.Message;
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.ListCell;
|
import javafx.scene.control.ListCell;
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package fr.insa.clavardator.ui.chat;
|
package fr.insa.clavardator.client.ui.chat;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
import com.jfoenix.controls.JFXButton;
|
||||||
import fr.insa.clavardator.chat.FileMessage;
|
import fr.insa.clavardator.client.users.CurrentUser;
|
||||||
import fr.insa.clavardator.chat.Message;
|
import fr.insa.clavardator.lib.message.FileMessage;
|
||||||
import fr.insa.clavardator.users.CurrentUser;
|
import fr.insa.clavardator.lib.message.Message;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
|
@ -30,6 +30,7 @@ public class MessageListItemController implements Initializable {
|
||||||
private Label timestamp;
|
private Label timestamp;
|
||||||
@FXML
|
@FXML
|
||||||
private FontIcon attachmentIcon;
|
private FontIcon attachmentIcon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the message to display
|
* Sets the message to display
|
||||||
*
|
*
|
||||||
|
|
@ -108,5 +109,6 @@ public class MessageListItemController implements Initializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL url, ResourceBundle rb) {}
|
public void initialize(URL url, ResourceBundle rb) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package fr.insa.clavardator.ui.dialogs;
|
package fr.insa.clavardator.client.ui.dialogs;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXDialog;
|
import com.jfoenix.controls.JFXDialog;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
package fr.insa.clavardator.ui.dialogs;
|
package fr.insa.clavardator.client.ui.dialogs;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
import com.jfoenix.controls.JFXButton;
|
||||||
import com.jfoenix.controls.JFXDialog;
|
import com.jfoenix.controls.JFXDialog;
|
||||||
import com.jfoenix.controls.JFXTextField;
|
import com.jfoenix.controls.JFXTextField;
|
||||||
import com.jfoenix.validation.base.ValidatorBase;
|
import com.jfoenix.validation.base.ValidatorBase;
|
||||||
import fr.insa.clavardator.ui.ButtonPressEvent;
|
import fr.insa.clavardator.client.ui.ButtonPressEvent;
|
||||||
import fr.insa.clavardator.users.CurrentUser;
|
import fr.insa.clavardator.client.users.CurrentUser;
|
||||||
import fr.insa.clavardator.users.UserList;
|
import fr.insa.clavardator.client.users.UserList;
|
||||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
|
@ -67,6 +67,7 @@ public class EditUsernameDialogController implements Initializable {
|
||||||
public void setOnSuccessListener(ButtonPressEvent listener) {
|
public void setOnSuccessListener(ButtonPressEvent listener) {
|
||||||
this.successListener = listener;
|
this.successListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnCancelListener(ButtonPressEvent listener) {
|
public void setOnCancelListener(ButtonPressEvent listener) {
|
||||||
this.cancelListener = listener;
|
this.cancelListener = listener;
|
||||||
}
|
}
|
||||||
|
|
@ -74,12 +75,11 @@ public class EditUsernameDialogController implements Initializable {
|
||||||
/**
|
/**
|
||||||
* Plays the dialog show animation
|
* Plays the dialog show animation
|
||||||
*
|
*
|
||||||
|
* @param root The dialog's root component
|
||||||
|
* @param mode The dialog display mode
|
||||||
* @implNote WARNING: Do not try to open the dialog instantly after closing it.
|
* @implNote WARNING: Do not try to open the dialog instantly after closing it.
|
||||||
* Due to JFoenix animations, the dialog will not reopen.
|
* Due to JFoenix animations, the dialog will not reopen.
|
||||||
* If you must, wait at least 500ms before reopening.
|
* If you must, wait at least 500ms before reopening.
|
||||||
*
|
|
||||||
* @param root The dialog's root component
|
|
||||||
* @param mode The dialog display mode
|
|
||||||
*/
|
*/
|
||||||
public void show(StackPane root, Mode mode) {
|
public void show(StackPane root, Mode mode) {
|
||||||
setLocked(false);
|
setLocked(false);
|
||||||
|
|
@ -179,9 +179,6 @@ public class EditUsernameDialogController implements Initializable {
|
||||||
if (successListener != null) {
|
if (successListener != null) {
|
||||||
successListener.onPress();
|
successListener.onPress();
|
||||||
}
|
}
|
||||||
if (userList != null) {
|
|
||||||
userList.propagateUsernameChange();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -203,7 +200,7 @@ public class EditUsernameDialogController implements Initializable {
|
||||||
textField.setOnKeyPressed(event -> {
|
textField.setOnKeyPressed(event -> {
|
||||||
if (event.getCode() == KeyCode.ENTER) {
|
if (event.getCode() == KeyCode.ENTER) {
|
||||||
event.consume();
|
event.consume();
|
||||||
if(!textField.getText().isEmpty()){
|
if (!textField.getText().isEmpty()) {
|
||||||
onConfirm();
|
onConfirm();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package fr.insa.clavardator.ui.dialogs;
|
package fr.insa.clavardator.client.ui.dialogs;
|
||||||
|
|
||||||
import com.jfoenix.effects.JFXDepthManager;
|
import com.jfoenix.effects.JFXDepthManager;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
|
@ -9,7 +9,6 @@ import javafx.scene.layout.HBox;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package fr.insa.clavardator.ui.users;
|
package fr.insa.clavardator.client.ui.users;
|
||||||
|
|
||||||
import fr.insa.clavardator.users.PeerUser;
|
import fr.insa.clavardator.client.users.PeerUser;
|
||||||
|
import fr.insa.clavardator.lib.users.UserState;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
|
|
@ -41,8 +42,8 @@ public class UserActiveIndicatorController implements Initializable {
|
||||||
*/
|
*/
|
||||||
private void onStateChange(PropertyChangeEvent propertyChangeEvent) {
|
private void onStateChange(PropertyChangeEvent propertyChangeEvent) {
|
||||||
if (propertyChangeEvent.getPropertyName().equals("state")) {
|
if (propertyChangeEvent.getPropertyName().equals("state")) {
|
||||||
final PeerUser.State newState = (PeerUser.State) propertyChangeEvent.getNewValue();
|
final UserState newState = (UserState) propertyChangeEvent.getNewValue();
|
||||||
Platform.runLater(() -> updateState(newState == PeerUser.State.CONNECTED));
|
Platform.runLater(() -> updateState(newState == UserState.CONNECTED));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,6 +72,7 @@ public class UserActiveIndicatorController implements Initializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {}
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package fr.insa.clavardator.ui.users;
|
package fr.insa.clavardator.client.ui.users;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
import com.jfoenix.controls.JFXButton;
|
||||||
import fr.insa.clavardator.ui.ButtonPressEvent;
|
import fr.insa.clavardator.client.ui.ButtonPressEvent;
|
||||||
import fr.insa.clavardator.ui.UserSelectedEvent;
|
import fr.insa.clavardator.client.ui.UserSelectedEvent;
|
||||||
import fr.insa.clavardator.users.PeerUser;
|
import fr.insa.clavardator.client.users.PeerUser;
|
||||||
import fr.insa.clavardator.users.User;
|
import fr.insa.clavardator.client.users.UserList;
|
||||||
import fr.insa.clavardator.users.UserList;
|
import fr.insa.clavardator.lib.users.User;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.control.ListView;
|
import javafx.scene.control.ListView;
|
||||||
|
|
@ -22,14 +22,17 @@ public class UserListController implements Initializable {
|
||||||
private ButtonPressEvent refreshUserListener;
|
private ButtonPressEvent refreshUserListener;
|
||||||
private UserSelectedEvent userSelectedListener;
|
private UserSelectedEvent userSelectedListener;
|
||||||
|
|
||||||
public UserListController() {}
|
public UserListController() {
|
||||||
|
}
|
||||||
|
|
||||||
public void setRefreshUserListener(ButtonPressEvent listener) {
|
public void setRefreshUserListener(ButtonPressEvent listener) {
|
||||||
refreshUserListener = listener;
|
refreshUserListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUserSelectedListener(UserSelectedEvent listener) {
|
public void setUserSelectedListener(UserSelectedEvent listener) {
|
||||||
userSelectedListener = listener;
|
userSelectedListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRefreshUserListPress() {
|
public void onRefreshUserListPress() {
|
||||||
if (refreshUserListener != null) {
|
if (refreshUserListener != null) {
|
||||||
refreshUserListener.onPress();
|
refreshUserListener.onPress();
|
||||||
|
|
@ -38,6 +41,7 @@ public class UserListController implements Initializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables or disables the refresh button
|
* Enables or disables the refresh button
|
||||||
|
*
|
||||||
* @param enabled True to enable, false otherwise
|
* @param enabled True to enable, false otherwise
|
||||||
*/
|
*/
|
||||||
public void setRefreshButtonEnabled(boolean enabled) {
|
public void setRefreshButtonEnabled(boolean enabled) {
|
||||||
|
|
@ -63,6 +67,7 @@ public class UserListController implements Initializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the user list to subscribe to
|
* Sets the user list to subscribe to
|
||||||
|
*
|
||||||
* @param userList The user list to use
|
* @param userList The user list to use
|
||||||
*/
|
*/
|
||||||
public void setUserList(UserList userList) {
|
public void setUserList(UserList userList) {
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package fr.insa.clavardator.ui.users;
|
package fr.insa.clavardator.client.ui.users;
|
||||||
|
|
||||||
import fr.insa.clavardator.ui.UserSelectedEvent;
|
import fr.insa.clavardator.client.ui.UserSelectedEvent;
|
||||||
import fr.insa.clavardator.users.PeerUser;
|
import fr.insa.clavardator.client.users.PeerUser;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.ListCell;
|
import javafx.scene.control.ListCell;
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
package fr.insa.clavardator.ui.users;
|
package fr.insa.clavardator.client.ui.users;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
import com.jfoenix.controls.JFXButton;
|
||||||
import fr.insa.clavardator.ui.ButtonPressEvent;
|
import fr.insa.clavardator.client.ui.ButtonPressEvent;
|
||||||
import fr.insa.clavardator.users.PeerUser;
|
import fr.insa.clavardator.client.users.PeerUser;
|
||||||
|
import fr.insa.clavardator.lib.users.UserState;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
|
|
@ -56,8 +57,8 @@ public class UserListItemController implements Initializable {
|
||||||
final String newUsername = (String) propertyChangeEvent.getNewValue();
|
final String newUsername = (String) propertyChangeEvent.getNewValue();
|
||||||
Platform.runLater(() -> button.setText(newUsername));
|
Platform.runLater(() -> button.setText(newUsername));
|
||||||
} else if (propName.equals("state") && !selected) {
|
} else if (propName.equals("state") && !selected) {
|
||||||
final PeerUser.State newState = (PeerUser.State) propertyChangeEvent.getNewValue();
|
final UserState newState = (UserState) propertyChangeEvent.getNewValue();
|
||||||
Platform.runLater(() -> resetBackground(newState == PeerUser.State.CONNECTED));
|
Platform.runLater(() -> resetBackground(newState == UserState.CONNECTED));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,5 +111,6 @@ public class UserListItemController implements Initializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {}
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package fr.insa.clavardator.users;
|
package fr.insa.clavardator.client.users;
|
||||||
|
|
||||||
import fr.insa.clavardator.db.DatabaseController;
|
import fr.insa.clavardator.client.db.DatabaseController;
|
||||||
import fr.insa.clavardator.util.ErrorCallback;
|
import fr.insa.clavardator.lib.users.User;
|
||||||
import fr.insa.clavardator.util.Log;
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -33,8 +35,8 @@ public class CurrentUser extends User {
|
||||||
final DatabaseController db = new DatabaseController();
|
final DatabaseController db = new DatabaseController();
|
||||||
db.getCurrentUser((user) -> {
|
db.getCurrentUser((user) -> {
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
id = generateUniqueId();
|
setId(generateUniqueId());
|
||||||
Log.v(getClass().getSimpleName(), "No previous user found, generating id: " + id);
|
Log.v(getClass().getSimpleName(), "No previous user found, generating id: " + getId());
|
||||||
db.addCurrentUser(
|
db.addCurrentUser(
|
||||||
new UserInformation(this),
|
new UserInformation(this),
|
||||||
() -> {
|
() -> {
|
||||||
|
|
@ -43,9 +45,9 @@ public class CurrentUser extends User {
|
||||||
},
|
},
|
||||||
errorCallback);
|
errorCallback);
|
||||||
} else {
|
} else {
|
||||||
id = user.id;
|
setId(user.id);
|
||||||
if (user.getUsername() != null) {
|
if (user.getUsername() != null) {
|
||||||
Log.v(getClass().getSimpleName(), "Last user found : " + id + " / " + getUsername());
|
Log.v(getClass().getSimpleName(), "Last user found : " + getId() + " / " + getUsername());
|
||||||
setUsername(user.getUsername());
|
setUsername(user.getUsername());
|
||||||
} else {
|
} else {
|
||||||
Log.v(getClass().getSimpleName(), "No username found, asking user");
|
Log.v(getClass().getSimpleName(), "No username found, asking user");
|
||||||
|
|
@ -75,7 +77,7 @@ public class CurrentUser extends User {
|
||||||
public void setState(State state) {
|
public void setState(State state) {
|
||||||
Log.v(this.getClass().getSimpleName(),
|
Log.v(this.getClass().getSimpleName(),
|
||||||
"State changed from " + this.state.toString() + " to " + state.toString());
|
"State changed from " + this.state.toString() + " to " + state.toString());
|
||||||
instance.pcs.firePropertyChange("state", this.state, state);
|
instance.getPcs().firePropertyChange("state", this.state, state);
|
||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package fr.insa.clavardator.client.users;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.lib.network.TcpConnection;
|
||||||
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.SimpleCallback;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class DirectPeerConnection extends PeerConnection {
|
||||||
|
private final TcpConnection connection;
|
||||||
|
|
||||||
|
public DirectPeerConnection(TcpConnection connection) {
|
||||||
|
this.connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void send(Serializable message, @Nullable SimpleCallback callback, @Nullable ErrorCallback errorCallback) {
|
||||||
|
connection.send(message, callback, errorCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void receive(TcpConnection.MessageReceivedCallback callback, ErrorCallback errorCallback) {
|
||||||
|
connection.receive(callback, errorCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
if (connection != null && connection.isOpen()) {
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package fr.insa.clavardator.client.users;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.lib.network.TcpConnection;
|
||||||
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.SimpleCallback;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public abstract class PeerConnection {
|
||||||
|
protected abstract void send(Serializable message, @Nullable SimpleCallback calback, @Nullable ErrorCallback errorCallback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to this user messages.
|
||||||
|
* If receiving new user info, update this user.
|
||||||
|
* If receiving text message, store it in the history.
|
||||||
|
*
|
||||||
|
* @param errorCallback Callback on error
|
||||||
|
*/
|
||||||
|
protected abstract void receive(TcpConnection.MessageReceivedCallback callback, ErrorCallback errorCallback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the connection and set state to disconnected
|
||||||
|
*/
|
||||||
|
public abstract void disconnect();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,218 @@
|
||||||
|
package fr.insa.clavardator.client.users;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.client.chat.ChatHistory;
|
||||||
|
import fr.insa.clavardator.client.db.DatabaseController;
|
||||||
|
import fr.insa.clavardator.client.server.proxy.Proxy;
|
||||||
|
import fr.insa.clavardator.lib.errors.UsernameTakenException;
|
||||||
|
import fr.insa.clavardator.lib.message.FileMessage;
|
||||||
|
import fr.insa.clavardator.lib.message.Message;
|
||||||
|
import fr.insa.clavardator.lib.network.TcpConnection;
|
||||||
|
import fr.insa.clavardator.lib.users.User;
|
||||||
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
|
import fr.insa.clavardator.lib.users.UserState;
|
||||||
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class PeerUser extends User implements Comparable<PeerUser> {
|
||||||
|
private final transient ChatHistory history;
|
||||||
|
private UserState state = UserState.DISCONNECTED;
|
||||||
|
private PeerConnection connection;
|
||||||
|
|
||||||
|
public PeerUser(String id, String username) {
|
||||||
|
super(id, username);
|
||||||
|
history = new ChatHistory(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerUser() {
|
||||||
|
super();
|
||||||
|
history = new ChatHistory(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(String id, String username, ErrorCallback errorCallback) {
|
||||||
|
setId(id);
|
||||||
|
setUsername(username);
|
||||||
|
setState(UserState.CONNECTED);
|
||||||
|
connection.receive(msg -> onMessageReceived(msg, errorCallback), e -> {
|
||||||
|
disconnect();
|
||||||
|
if (!(e instanceof EOFException)) {
|
||||||
|
Log.e(this.getClass().getSimpleName(), "Error receiving message from " + getId(), e);
|
||||||
|
errorCallback.onError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(TcpConnection tcpConnection, String id, String username, ErrorCallback errorCallback) {
|
||||||
|
connection = new DirectPeerConnection(tcpConnection);
|
||||||
|
init(id, username, errorCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(Proxy proxy, String id, String username, ErrorCallback errorCallback) {
|
||||||
|
connection = new ProxyPeerConnection(proxy, getId());
|
||||||
|
init(id, username, errorCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a basic text message to this user
|
||||||
|
*
|
||||||
|
* @param msg The text message to send
|
||||||
|
* @param errorCallback Callback on error
|
||||||
|
*/
|
||||||
|
public void sendTextMessage(String msg, @Nullable ErrorCallback errorCallback) {
|
||||||
|
if (connection != null) {
|
||||||
|
Log.v(this.getClass().getSimpleName(),
|
||||||
|
"Sending message to " + getUsername() + " / " + getId() + ": " + msg);
|
||||||
|
final Message message = new Message(CurrentUser.getInstance(), this, new Date(), msg);
|
||||||
|
connection.send(message, () -> addMessageToHistory(message, errorCallback), errorCallback);
|
||||||
|
} else {
|
||||||
|
Log.e(this.getClass().getSimpleName(), "Could not send message: connection is not initialized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a message containing a file to this user
|
||||||
|
*
|
||||||
|
* @param msg The text message to send
|
||||||
|
* @param errorCallback Callback on error
|
||||||
|
*/
|
||||||
|
public void sendFileMessage(String msg, File file, @Nullable ErrorCallback errorCallback) {
|
||||||
|
if (connection != null) {
|
||||||
|
Log.v(this.getClass().getSimpleName(),
|
||||||
|
"Sending file message to " + this.getUsername() + " / " + this.getId() + ": " + msg);
|
||||||
|
try {
|
||||||
|
final FileMessage message = new FileMessage(CurrentUser.getInstance(), this, new Date(), msg, file.getPath());
|
||||||
|
message.storeFile();
|
||||||
|
connection.send(message, () -> addMessageToHistory(message, errorCallback), errorCallback);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(this.getClass().getSimpleName(), "Could not send message: error while opening file", e);
|
||||||
|
if (errorCallback != null) {
|
||||||
|
errorCallback.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(this.getClass().getSimpleName(), "Could not send message: connection is not initialized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends current user information to this user
|
||||||
|
*
|
||||||
|
* @param errorCallback Callback on error
|
||||||
|
*/
|
||||||
|
public void sendCurrentUser(@Nullable ErrorCallback errorCallback) {
|
||||||
|
if (connection != null) {
|
||||||
|
final String username = CurrentUser.getInstance().getUsername();
|
||||||
|
Log.v(this.getClass().getSimpleName(),
|
||||||
|
"Sending current user information to " + this.getUsername() + " / " + this.getId() + ": " + username);
|
||||||
|
connection.send(new UserInformation(CurrentUser.getInstance()), null, errorCallback);
|
||||||
|
} else {
|
||||||
|
Log.e(this.getClass().getSimpleName(), "Could not send new username: connection is not initialized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void sendUsernameTaken() {
|
||||||
|
if (connection != null) {
|
||||||
|
Log.v(this.getClass().getSimpleName(), "Received username request using current username");
|
||||||
|
UsernameTakenException exception = new UsernameTakenException("Username taken",
|
||||||
|
CurrentUser.getInstance().getId(), getId());
|
||||||
|
connection.send(exception, null, null);
|
||||||
|
} else {
|
||||||
|
Log.e(this.getClass().getSimpleName(), "Could not send UsernameTaken: connection is not initialized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addMessageToHistory(Message message, @Nullable ErrorCallback errorCallback) {
|
||||||
|
history.addMessage(message, errorCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void onMessageReceived(Object msg, ErrorCallback errorCallback) throws IOException {
|
||||||
|
Log.v(this.getClass().getSimpleName(), "Received message from " + getId());
|
||||||
|
if (msg instanceof UserInformation) {
|
||||||
|
assert ((UserInformation) msg).id.equals(getId());
|
||||||
|
final String receivedUsername = ((UserInformation) msg).getUsername();
|
||||||
|
Log.v(this.getClass().getSimpleName(), "Message username: " + receivedUsername);
|
||||||
|
if (CurrentUser.getInstance().getUsername().equals(receivedUsername)) {
|
||||||
|
sendUsernameTaken();
|
||||||
|
} else {
|
||||||
|
setUsername(receivedUsername);
|
||||||
|
}
|
||||||
|
} else if (msg instanceof Message) {
|
||||||
|
assert !((Message) msg).getRecipient().id.equals(getId());
|
||||||
|
|
||||||
|
Log.v(this.getClass().getSimpleName(), "Message text: " + ((Message) msg).getText());
|
||||||
|
if (msg instanceof FileMessage) {
|
||||||
|
((FileMessage) msg).storeFile();
|
||||||
|
}
|
||||||
|
history.addMessage((Message) msg, errorCallback);
|
||||||
|
} else if (msg instanceof UsernameTakenException) {
|
||||||
|
CurrentUser.getInstance().setState(CurrentUser.State.INVALID);
|
||||||
|
errorCallback.onError(new Exception("Received username already taken message"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect() {
|
||||||
|
if (connection != null) {
|
||||||
|
Log.v(this.getClass().getSimpleName(), "Disconnecting from user: " + getId());
|
||||||
|
connection.disconnect();
|
||||||
|
connection = null;
|
||||||
|
}
|
||||||
|
setState(UserState.DISCONNECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of history
|
||||||
|
*
|
||||||
|
* @return the value of history
|
||||||
|
*/
|
||||||
|
public ChatHistory getHistory() {
|
||||||
|
return history;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this user state
|
||||||
|
*
|
||||||
|
* @param state The new state
|
||||||
|
*/
|
||||||
|
protected void setState(UserState state) {
|
||||||
|
getPcs().firePropertyChange("state", this.state, state);
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUsername(String newUsername) {
|
||||||
|
super.setUsername(newUsername);
|
||||||
|
final DatabaseController db = new DatabaseController();
|
||||||
|
db.updateUsername(new UserInformation(this),
|
||||||
|
null,
|
||||||
|
e -> Log.e(getClass().getSimpleName(), "Unable to update the username", e));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this user is active.
|
||||||
|
*
|
||||||
|
* @return True id active, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean isActive() {
|
||||||
|
return state == UserState.CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(@NotNull PeerUser peerUser) {
|
||||||
|
if (peerUser.isActive() && !this.isActive()) {
|
||||||
|
return 1;
|
||||||
|
} else if (!peerUser.isActive() && this.isActive()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return getUsername().compareTo(peerUser.getUsername());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package fr.insa.clavardator.client.users;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.client.server.proxy.Proxy;
|
||||||
|
import fr.insa.clavardator.lib.network.TcpConnection;
|
||||||
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.SimpleCallback;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class ProxyPeerConnection extends PeerConnection {
|
||||||
|
private final Proxy proxy;
|
||||||
|
private final String userId;
|
||||||
|
|
||||||
|
public ProxyPeerConnection(Proxy proxy, String userId) {
|
||||||
|
this.proxy = proxy;
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void send(Serializable message, @Nullable SimpleCallback callback, @Nullable ErrorCallback errorCallback) {
|
||||||
|
proxy.send(message, callback, errorCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void receive(TcpConnection.MessageReceivedCallback callback, ErrorCallback errorCallback) {
|
||||||
|
proxy.receive(userId, callback, errorCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
proxy.disconnectUser(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
package fr.insa.clavardator.users;
|
package fr.insa.clavardator.client.users;
|
||||||
|
|
||||||
import fr.insa.clavardator.db.DatabaseController;
|
import fr.insa.clavardator.client.db.DatabaseController;
|
||||||
import fr.insa.clavardator.network.NetDiscoverer;
|
import fr.insa.clavardator.client.network.NetDiscoverer;
|
||||||
import fr.insa.clavardator.network.PeerHandshake;
|
import fr.insa.clavardator.client.network.PeerHandshake;
|
||||||
import fr.insa.clavardator.network.TcpConnection;
|
import fr.insa.clavardator.client.server.proxy.Proxy;
|
||||||
import fr.insa.clavardator.network.TcpListener;
|
import fr.insa.clavardator.lib.network.TcpListener;
|
||||||
import fr.insa.clavardator.util.ErrorCallback;
|
import fr.insa.clavardator.lib.users.User;
|
||||||
import fr.insa.clavardator.util.Log;
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
|
import fr.insa.clavardator.lib.users.UserState;
|
||||||
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
|
@ -35,32 +38,53 @@ public class UserList {
|
||||||
*
|
*
|
||||||
* @param errorCallback The function to call on error
|
* @param errorCallback The function to call on error
|
||||||
*/
|
*/
|
||||||
public void discoverActiveUsers(ErrorCallback errorCallback) {
|
public void discoverActiveUsers(int port, ErrorCallback errorCallback) {
|
||||||
netDiscoverer.discoverActiveUsers("CLAVARDATOR_BROADCAST", (ipAddr, data) -> {
|
netDiscoverer.discoverActiveUsers("CLAVARDATOR_BROADCAST", (ipAddr, data) -> {
|
||||||
Log.v(this.getClass().getSimpleName(), "Discovered new user: " + data);
|
Log.v(this.getClass().getSimpleName(), "Discovered new user: " + data);
|
||||||
new PeerHandshake().createConnection(
|
new PeerHandshake().createConnection(
|
||||||
ipAddr,
|
ipAddr,
|
||||||
|
port,
|
||||||
this::onUserConnectionSuccess,
|
this::onUserConnectionSuccess,
|
||||||
errorCallback);
|
errorCallback);
|
||||||
}, errorCallback);
|
}, errorCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onReceivePresenceNotification(ArrayList<UserInformation> newPresenceUsers, TcpConnection proxyConnection) {
|
public void onReceivePresenceNotification(ArrayList<UserInformation> newPresenceUsers, Proxy proxy) {
|
||||||
newPresenceUsers.forEach((userInfo -> {
|
newPresenceUsers.forEach((userInfo -> {
|
||||||
final PeerUser savedUser = userHashmap.get(userInfo.id);
|
final PeerUser savedUser = userHashmap.get(userInfo.id);
|
||||||
if (savedUser != null) {
|
if (savedUser != null) {
|
||||||
if (savedUser.isActive()) {
|
if (savedUser.isActive()) {
|
||||||
Log.v(getClass().getSimpleName(), "Received user from presence server already known and connected");
|
if (userInfo.getState() == UserState.DISCONNECTED) {
|
||||||
|
Log.v(getClass().getSimpleName(), "Received disconnected user from presence server was already known and connected, disconnecting...");
|
||||||
|
savedUser.disconnect();
|
||||||
|
} else {
|
||||||
|
Log.v(getClass().getSimpleName(), "Received user from presence server was already known and connected, updating info...");
|
||||||
|
savedUser.setState(userInfo.getState());
|
||||||
|
savedUser.setUsername(userInfo.getUsername());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.v(getClass().getSimpleName(), "Received user from presence server already known and connected");
|
if (userInfo.getState() == UserState.CONNECTED) {
|
||||||
savedUser.init(proxyConnection, userInfo.id, userInfo.getUsername(), null);
|
Log.v(getClass().getSimpleName(), "Received user from presence server was already known but not connected, connecting...");
|
||||||
|
savedUser.init(proxy, userInfo.id, userInfo.getUsername(),
|
||||||
|
e -> Log.e(getClass().getSimpleName(), "Error with user " + userInfo.getUsername(), e));
|
||||||
|
} else {
|
||||||
|
Log.v(getClass().getSimpleName(), "Received disconnected user from presence server was already known and disconnected.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.v(getClass().getSimpleName(), "Received new user from presence server");
|
if (userInfo.getState() == UserState.CONNECTED) {
|
||||||
final PeerUser user = new PeerUser();
|
Log.v(getClass().getSimpleName(), "Received new connected user from presence server");
|
||||||
user.init(proxyConnection, userInfo.id, userInfo.getUsername(), null);
|
final PeerUser user = new PeerUser();
|
||||||
userHashmap.put(user.id, user);
|
user.init(proxy, userInfo.id, userInfo.getUsername(),
|
||||||
Platform.runLater(() -> userObservableList.add(user));
|
e -> Log.e(getClass().getSimpleName(), "Error with user " + userInfo.getUsername(), e));
|
||||||
|
userHashmap.put(user.getId(), user);
|
||||||
|
Platform.runLater(() -> userObservableList.add(user));
|
||||||
|
} else {
|
||||||
|
Log.v(getClass().getSimpleName(), "Received new disconnected user from presence server");
|
||||||
|
final PeerUser user = new PeerUser(userInfo.id, userInfo.getUsername());
|
||||||
|
userHashmap.put(user.getId(), user);
|
||||||
|
Platform.runLater(() -> userObservableList.add(user));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +102,7 @@ public class UserList {
|
||||||
|
|
||||||
public void retrievedPreviousUsers(UserListLoadedCallback onFinish, ErrorCallback errorCallback) {
|
public void retrievedPreviousUsers(UserListLoadedCallback onFinish, ErrorCallback errorCallback) {
|
||||||
db.getAllUsers(users -> {
|
db.getAllUsers(users -> {
|
||||||
users.forEach(user -> createNewInactiveUser(user.id, user.getUsername()));
|
users.forEach(user -> createNewInactiveUser(user.getId(), user.getUsername()));
|
||||||
onFinish.onLoaded();
|
onFinish.onLoaded();
|
||||||
}, errorCallback);
|
}, errorCallback);
|
||||||
}
|
}
|
||||||
|
|
@ -88,8 +112,9 @@ public class UserList {
|
||||||
*
|
*
|
||||||
* @param errorCallback Callback on error
|
* @param errorCallback Callback on error
|
||||||
*/
|
*/
|
||||||
public void startUserListening(ErrorCallback errorCallback) {
|
public void startUserListening(int port, ErrorCallback errorCallback) {
|
||||||
tcpListener.acceptConnection(
|
tcpListener.acceptConnection(
|
||||||
|
port,
|
||||||
(clientSocket) -> {
|
(clientSocket) -> {
|
||||||
Log.v(this.getClass().getSimpleName(),
|
Log.v(this.getClass().getSimpleName(),
|
||||||
"new connection from user at address: " + clientSocket.getInetAddress().toString());
|
"new connection from user at address: " + clientSocket.getInetAddress().toString());
|
||||||
|
|
@ -106,11 +131,14 @@ public class UserList {
|
||||||
final UserInformation userInfo = handshake.getUserInformation();
|
final UserInformation userInfo = handshake.getUserInformation();
|
||||||
final PeerUser savedUser = userHashmap.get(userInfo.id);
|
final PeerUser savedUser = userHashmap.get(userInfo.id);
|
||||||
if (savedUser != null) {
|
if (savedUser != null) {
|
||||||
savedUser.init(handshake.getConnection(), userInfo.id, userInfo.getUsername(), null);
|
savedUser.init(handshake.getConnection(), userInfo.id, userInfo.getUsername(),
|
||||||
|
e -> Log.e(getClass().getSimpleName(), "Error with user " + userInfo.getUsername(), e));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
final PeerUser user = new PeerUser();
|
final PeerUser user = new PeerUser();
|
||||||
user.init(handshake.getConnection(), userInfo.id, userInfo.getUsername(), null);
|
user.init(handshake.getConnection(), userInfo.id, userInfo.getUsername(),
|
||||||
userHashmap.put(user.id, user);
|
e -> Log.e(getClass().getSimpleName(), "Error with user " + userInfo.getUsername(), e));
|
||||||
|
userHashmap.put(user.getId(), user);
|
||||||
Platform.runLater(() -> userObservableList.add(user));
|
Platform.runLater(() -> userObservableList.add(user));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!--suppress JavaFxUnresolvedFxIdReference -->
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.ListView?>
|
<?import javafx.scene.control.ListView?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<!--suppress JavaFxUnresolvedFxIdReference -->
|
<AnchorPane xmlns:fx="http://javafx.com/fxml/1"
|
||||||
<?import javafx.scene.control.Label?>
|
xmlns="http://javafx.com/javafx/11.0.1" fx:controller="fr.insa.clavardator.client.ui.chat.ChatController"
|
||||||
<?import com.jfoenix.controls.JFXSpinner?>
|
|
||||||
<?import javafx.geometry.Insets?>
|
|
||||||
<AnchorPane xmlns="http://javafx.com/javafx/11.0.1"
|
|
||||||
xmlns:fx="http://javafx.com/fxml/1" fx:controller="fr.insa.clavardator.ui.chat.ChatController"
|
|
||||||
stylesheets="@../styles.css" styleClass="container">
|
stylesheets="@../styles.css" styleClass="container">
|
||||||
<StackPane AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
<StackPane AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.layout.HBox?>
|
<?import javafx.scene.layout.HBox?>
|
||||||
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
||||||
<HBox xmlns="http://javafx.com/javafx"
|
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="fr.insa.clavardator.ui.chat.ChatFooterController"
|
fx:controller="fr.insa.clavardator.client.ui.chat.ChatFooterController"
|
||||||
stylesheets="@../styles.css" styleClass="container" alignment="CENTER" spacing="10.0" fx:id="container">
|
stylesheets="@../styles.css" styleClass="container" alignment="CENTER" spacing="10.0" fx:id="container">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
|
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="fr.insa.clavardator.ui.chat.ChatHeaderController"
|
fx:controller="fr.insa.clavardator.client.ui.chat.ChatHeaderController"
|
||||||
stylesheets="@../styles.css" styleClass="container">
|
stylesheets="@../styles.css" styleClass="container">
|
||||||
<HBox alignment="CENTER_LEFT" spacing="5" prefHeight="64">
|
<HBox alignment="CENTER_LEFT" spacing="5" prefHeight="64">
|
||||||
<padding>
|
<padding>
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import com.jfoenix.controls.JFXButton?>
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
||||||
|
<VBox xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
xmlns="http://javafx.com/javafx/11.0.1"
|
||||||
|
fx:controller="fr.insa.clavardator.client.ui.chat.MessageListItemController"
|
||||||
|
stylesheets="@../styles.css"
|
||||||
|
styleClass="inner"
|
||||||
|
fx:id="container"
|
||||||
|
spacing="10">
|
||||||
|
<padding>
|
||||||
|
<Insets left="50" right="50" top="10" bottom="10"/>
|
||||||
|
</padding>
|
||||||
|
<JFXButton fx:id="button"
|
||||||
|
mnemonicParsing="false"
|
||||||
|
text="Message"
|
||||||
|
textAlignment="CENTER">
|
||||||
|
<graphic>
|
||||||
|
<FontIcon iconLiteral="fas-paperclip" iconSize="24" fx:id="attachmentIcon"/>
|
||||||
|
</graphic>
|
||||||
|
</JFXButton>
|
||||||
|
<Label fx:id="timestamp"
|
||||||
|
text="Timestamp"
|
||||||
|
styleClass="timestamp"/>
|
||||||
|
</VBox>
|
||||||
|
|
@ -2,14 +2,13 @@
|
||||||
|
|
||||||
<?import com.jfoenix.controls.JFXButton?>
|
<?import com.jfoenix.controls.JFXButton?>
|
||||||
<?import com.jfoenix.controls.JFXDialog?>
|
<?import com.jfoenix.controls.JFXDialog?>
|
||||||
<?import com.jfoenix.controls.JFXTextField?>
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
||||||
<JFXDialog xmlns:fx="http://javafx.com/fxml"
|
<JFXDialog xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="fr.insa.clavardator.ui.dialogs.AboutDialogController"
|
fx:controller="fr.insa.clavardator.client.ui.dialogs.AboutDialogController"
|
||||||
stylesheets="@../styles.css" fx:id="dialog">
|
stylesheets="@../styles.css" fx:id="dialog">
|
||||||
<AnchorPane
|
<AnchorPane
|
||||||
prefWidth="600"
|
prefWidth="600"
|
||||||
|
|
@ -22,7 +21,7 @@
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="10"/>
|
<Insets bottom="10"/>
|
||||||
</padding>
|
</padding>
|
||||||
<Label styleClass="dialog-title" >À Propos</Label>
|
<Label styleClass="dialog-title">À Propos</Label>
|
||||||
<VBox VBox.vgrow="ALWAYS" alignment="CENTER" spacing="30">
|
<VBox VBox.vgrow="ALWAYS" alignment="CENTER" spacing="30">
|
||||||
<Label VBox.vgrow="ALWAYS">Application réalisée par Yohan SIMARD et Arnaud VERGNET</Label>
|
<Label VBox.vgrow="ALWAYS">Application réalisée par Yohan SIMARD et Arnaud VERGNET</Label>
|
||||||
<Label VBox.vgrow="ALWAYS">INSA Toulouse 2020-2021</Label>
|
<Label VBox.vgrow="ALWAYS">INSA Toulouse 2020-2021</Label>
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
||||||
<JFXDialog xmlns:fx="http://javafx.com/fxml"
|
<JFXDialog xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="fr.insa.clavardator.ui.dialogs.EditUsernameDialogController"
|
fx:controller="fr.insa.clavardator.client.ui.dialogs.EditUsernameDialogController"
|
||||||
stylesheets="@../styles.css" fx:id="dialog">
|
stylesheets="@../styles.css" fx:id="dialog">
|
||||||
<AnchorPane
|
<AnchorPane
|
||||||
prefWidth="600"
|
prefWidth="600"
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import javafx.scene.control.Label?>
|
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
|
||||||
<?import javafx.scene.layout.HBox?>
|
|
||||||
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
||||||
<AnchorPane xmlns:fx="http://javafx.com/fxml"
|
<AnchorPane xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="fr.insa.clavardator.ui.dialogs.SnackbarController"
|
fx:controller="fr.insa.clavardator.client.ui.dialogs.SnackbarController"
|
||||||
stylesheets="@../styles.css">
|
stylesheets="@../styles.css">
|
||||||
<HBox fx:id="container" alignment="CENTER_LEFT" prefHeight="50.0" prefWidth="400" AnchorPane.rightAnchor="0"
|
<HBox fx:id="container" alignment="CENTER_LEFT" prefHeight="50.0" prefWidth="400" AnchorPane.rightAnchor="0"
|
||||||
AnchorPane.leftAnchor="0" AnchorPane.topAnchor="0" AnchorPane.bottomAnchor="100"
|
AnchorPane.leftAnchor="0" AnchorPane.topAnchor="0" AnchorPane.bottomAnchor="100"
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import com.jfoenix.controls.JFXButton?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import com.jfoenix.controls.JFXButton?>
|
|
||||||
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
||||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="fr.insa.clavardator.ui.ErrorScreenController"
|
fx:controller="fr.insa.clavardator.client.ui.ErrorScreenController"
|
||||||
stylesheets="@styles.css"
|
stylesheets="@styles.css"
|
||||||
styleClass="container"
|
styleClass="container"
|
||||||
alignment="CENTER"
|
alignment="CENTER"
|
||||||
|
|
@ -14,9 +14,9 @@
|
||||||
fx:id="container">
|
fx:id="container">
|
||||||
<Label>Une erreur est survenue et Clavardator ne peut pas démarrer</Label>
|
<Label>Une erreur est survenue et Clavardator ne peut pas démarrer</Label>
|
||||||
<JFXButton styleClass="background-danger" onAction="#onPress">
|
<JFXButton styleClass="background-danger" onAction="#onPress">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontIcon iconLiteral="fas-times"/>
|
<FontIcon iconLiteral="fas-times"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
Quitter
|
Quitter
|
||||||
</JFXButton>
|
</JFXButton>
|
||||||
</VBox>
|
</VBox>
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<StackPane xmlns:fx="http://javafx.com/fxml"
|
<StackPane xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="fr.insa.clavardator.ui.LoadingScreenController"
|
fx:controller="fr.insa.clavardator.client.ui.LoadingScreenController"
|
||||||
stylesheets="@styles.css"
|
stylesheets="@styles.css"
|
||||||
styleClass="container"
|
styleClass="container"
|
||||||
fx:id="container">
|
fx:id="container">
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<!--suppress JavaFxUnresolvedFxIdReference -->
|
<!--suppress JavaFxUnresolvedFxIdReference -->
|
||||||
<?import javafx.scene.control.Label?>
|
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<StackPane xmlns:fx="http://javafx.com/fxml/1"
|
<StackPane xmlns:fx="http://javafx.com/fxml/1"
|
||||||
xmlns="http://javafx.com/javafx/11.0.1"
|
xmlns="http://javafx.com/javafx/11.0.1"
|
||||||
fx:controller="fr.insa.clavardator.ui.MainController"
|
fx:controller="fr.insa.clavardator.client.ui.MainController"
|
||||||
stylesheets="@styles.css"
|
stylesheets="@styles.css"
|
||||||
styleClass="container"
|
styleClass="container"
|
||||||
fx:id="root">
|
fx:id="root">
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/********************************************************
|
/********************************************************
|
||||||
THEME
|
THEME
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
|
|
@ -17,7 +16,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/********************************************************
|
/********************************************************
|
||||||
STYLE
|
STYLE
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
|
|
@ -3,12 +3,14 @@
|
||||||
<?import com.jfoenix.controls.JFXButton?>
|
<?import com.jfoenix.controls.JFXButton?>
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.Pane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
||||||
<VBox xmlns="http://javafx.com/javafx"
|
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="fr.insa.clavardator.ui.ToolbarController"
|
fx:controller="fr.insa.clavardator.client.ui.ToolbarController"
|
||||||
stylesheets="@styles.css" styleClass="container">
|
stylesheets="@styles.css" styleClass="container">
|
||||||
<HBox alignment="CENTER_LEFT" prefHeight="64.0" spacing="10">
|
<HBox alignment="CENTER_LEFT" prefHeight="64.0" spacing="10">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets left="20" right="20"/>
|
<Insets left="20" right="20"/>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.shape.Circle?>
|
||||||
|
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||||
|
xmlns="http://javafx.com/javafx"
|
||||||
|
fx:controller="fr.insa.clavardator.client.ui.users.UserActiveIndicatorController"
|
||||||
|
stylesheets="@../styles.css" alignment="CENTER">
|
||||||
|
<Circle fx:id="circle" radius="5.0" styleClass="active-user-dot"/>
|
||||||
|
</HBox>
|
||||||
|
|
@ -4,13 +4,14 @@
|
||||||
<?import javafx.scene.control.ListView?>
|
<?import javafx.scene.control.ListView?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
<?import org.kordamp.ikonli.javafx.FontIcon?>
|
||||||
<AnchorPane xmlns="http://javafx.com/javafx/11.0.1"
|
<AnchorPane xmlns:fx="http://javafx.com/fxml/1"
|
||||||
xmlns:fx="http://javafx.com/fxml/1" fx:controller="fr.insa.clavardator.ui.users.UserListController"
|
xmlns="http://javafx.com/javafx/11.0.1" fx:controller="fr.insa.clavardator.client.ui.users.UserListController"
|
||||||
stylesheets="@../styles.css" styleClass="container">
|
stylesheets="@../styles.css" styleClass="container">
|
||||||
<VBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
<VBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<HBox alignment="CENTER" prefHeight="64.0" spacing="10.0">
|
<HBox alignment="CENTER" prefHeight="64.0" spacing="10.0">
|
||||||
<JFXButton mnemonicParsing="false" text="Utilisateurs" onMouseClicked="#onRefreshUserListPress" fx:id="refreshButton">
|
<JFXButton mnemonicParsing="false" text="Utilisateurs" onMouseClicked="#onRefreshUserListPress"
|
||||||
|
fx:id="refreshButton">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontIcon iconLiteral="fas-sync-alt" iconSize="24"/>
|
<FontIcon iconLiteral="fas-sync-alt" iconSize="24"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!--suppress JavaFxUnresolvedFxIdReference -->
|
||||||
<?import com.jfoenix.controls.JFXButton?>
|
<?import com.jfoenix.controls.JFXButton?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<!--suppress JavaFxUnresolvedFxIdReference -->
|
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" prefHeight="40.0" xmlns="http://javafx.com/javafx/11.0.1"
|
||||||
<AnchorPane prefHeight="40.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1"
|
fx:controller="fr.insa.clavardator.client.ui.users.UserListItemController" stylesheets="@../styles.css"
|
||||||
fx:controller="fr.insa.clavardator.ui.users.UserListItemController" stylesheets="@../styles.css"
|
|
||||||
styleClass="inner">
|
styleClass="inner">
|
||||||
|
<!--suppress JavaFxRedundantPropertyValue -->
|
||||||
<JFXButton fx:id="button" alignment="CENTER_LEFT" AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0"
|
<JFXButton fx:id="button" alignment="CENTER_LEFT" AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0"
|
||||||
AnchorPane.rightAnchor="0" AnchorPane.topAnchor="0" onAction="#onPress">
|
AnchorPane.rightAnchor="0" AnchorPane.topAnchor="0" onAction="#onPress">
|
||||||
<graphic>
|
<graphic>
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
package fr.insa.clavardator;
|
package fr.insa.clavardator;
|
||||||
|
|
||||||
import fr.insa.clavardator.chat.Message;
|
import fr.insa.clavardator.client.db.DatabaseController;
|
||||||
import fr.insa.clavardator.db.DatabaseController;
|
import fr.insa.clavardator.lib.message.Message;
|
||||||
import fr.insa.clavardator.users.UserInformation;
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -30,9 +30,9 @@ public class DatabaseTest {
|
||||||
|
|
||||||
db.getChatHistory(new UserInformation("1", "Yohan"),
|
db.getChatHistory(new UserInformation("1", "Yohan"),
|
||||||
new UserInformation("0", "Arnaud"), new Date(0), new Date(), history -> {
|
new UserInformation("0", "Arnaud"), new Date(0), new Date(), history -> {
|
||||||
assertEquals(history.size(), 0);
|
assertEquals(history.size(), 0);
|
||||||
latch2.countDown();
|
latch2.countDown();
|
||||||
}, Assertions::fail);
|
}, Assertions::fail);
|
||||||
latch2.await();
|
latch2.await();
|
||||||
|
|
||||||
CountDownLatch latch8 = new CountDownLatch(1);
|
CountDownLatch latch8 = new CountDownLatch(1);
|
||||||
|
|
@ -67,8 +67,8 @@ public class DatabaseTest {
|
||||||
|
|
||||||
db.getChatHistory(new UserInformation("1", "Yohan"),
|
db.getChatHistory(new UserInformation("1", "Yohan"),
|
||||||
new UserInformation("2", "Arnaud"), new Date(0), new Date(), history -> {
|
new UserInformation("2", "Arnaud"), new Date(0), new Date(), history -> {
|
||||||
assertEquals(5, history.size());
|
assertEquals(5, history.size());
|
||||||
assertEquals("Coucou Arnaud !", history.get(0).getText());
|
assertEquals("Coucou Arnaud !", history.get(0).getText());
|
||||||
assertEquals("1", history.get(0).getSender().id);
|
assertEquals("1", history.get(0).getSender().id);
|
||||||
assertEquals("2", history.get(0).getRecipient().id);
|
assertEquals("2", history.get(0).getRecipient().id);
|
||||||
assertEquals("Yohan", history.get(0).getSender().getUsername());
|
assertEquals("Yohan", history.get(0).getSender().getUsername());
|
||||||
14
lib/build.gradle
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
group 'fr.insa.clavardator.lib'
|
||||||
|
version '0.0.1'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.jetbrains:annotations:20.1.0'
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package fr.insa.clavardator.lib.errors;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.lib.message.RecipientInfo;
|
||||||
|
import fr.insa.clavardator.lib.message.SenderInfo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class UsernameTakenException extends Exception implements Serializable, RecipientInfo, SenderInfo {
|
||||||
|
public final String recipient;
|
||||||
|
public final String sender;
|
||||||
|
|
||||||
|
public UsernameTakenException(String message, String sender, String recipient) {
|
||||||
|
super(message);
|
||||||
|
this.sender = sender;
|
||||||
|
this.recipient = recipient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRecipientId() {
|
||||||
|
return recipient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSenderId() {
|
||||||
|
return sender;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package fr.insa.clavardator.chat;
|
package fr.insa.clavardator.lib.message;
|
||||||
|
|
||||||
import fr.insa.clavardator.users.User;
|
import fr.insa.clavardator.lib.users.User;
|
||||||
import fr.insa.clavardator.users.UserInformation;
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
|
@ -14,8 +14,8 @@ public class FileMessage extends Message {
|
||||||
public static final String STORED_FILES_FOLDER = "clavardator_stored_files";
|
public static final String STORED_FILES_FOLDER = "clavardator_stored_files";
|
||||||
|
|
||||||
private final String fileName;
|
private final String fileName;
|
||||||
private String path;
|
|
||||||
byte[] rawFile = null;
|
byte[] rawFile = null;
|
||||||
|
private String path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a FileMessage
|
* Constructs a FileMessage
|
||||||
|
|
@ -32,13 +32,13 @@ public class FileMessage extends Message {
|
||||||
|
|
||||||
File file = new File(filePath);
|
File file = new File(filePath);
|
||||||
if (!file.exists())
|
if (!file.exists())
|
||||||
throw new IOException("The file " + filePath + " does not exist");
|
throw new IOException("The file " + file.getAbsolutePath() + " does not exist");
|
||||||
if (!file.canRead())
|
if (!file.canRead())
|
||||||
throw new IOException("The file " + filePath + " is not readable");
|
throw new IOException("The file " + file.getAbsolutePath() + " is not readable");
|
||||||
if (!file.isFile())
|
if (!file.isFile())
|
||||||
throw new IOException("The path " + filePath + " does not lead to a file");
|
throw new IOException("The path " + file.getAbsolutePath() + " does not lead to a file");
|
||||||
if (file.length() > MAX_FILE_SIZE)
|
if (file.length() > MAX_FILE_SIZE)
|
||||||
throw new IOException("The file " + filePath + " is too large");
|
throw new IOException("The file " + file.getAbsolutePath() + " is too large");
|
||||||
|
|
||||||
fileName = file.getName();
|
fileName = file.getName();
|
||||||
path = filePath;
|
path = filePath;
|
||||||
|
|
@ -63,12 +63,20 @@ public class FileMessage extends Message {
|
||||||
* @return the path to the file
|
* @return the path to the file
|
||||||
*/
|
*/
|
||||||
public String storeFile() throws IOException {
|
public String storeFile() throws IOException {
|
||||||
path = STORED_FILES_FOLDER + File.separatorChar + fileName;
|
|
||||||
|
|
||||||
// Create directory
|
// Create clavardator directory
|
||||||
File dir = new File(STORED_FILES_FOLDER);
|
File dir = new File(STORED_FILES_FOLDER);
|
||||||
dir.mkdirs();
|
dir.mkdirs();
|
||||||
|
|
||||||
|
// Copy the source file to rawFile
|
||||||
|
if (rawFile == null) {
|
||||||
|
FileInputStream istream = new FileInputStream(path);
|
||||||
|
rawFile = istream.readAllBytes();
|
||||||
|
istream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
path = STORED_FILES_FOLDER + File.separatorChar + fileName;
|
||||||
|
|
||||||
// Create new file
|
// Create new file
|
||||||
int extensionBeginning = fileName.lastIndexOf('.');
|
int extensionBeginning = fileName.lastIndexOf('.');
|
||||||
String name = fileName;
|
String name = fileName;
|
||||||
|
|
@ -84,12 +92,7 @@ public class FileMessage extends Message {
|
||||||
file = new File(path);
|
file = new File(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// write to the file
|
// Write the file in clavardator dir
|
||||||
if (rawFile == null) {
|
|
||||||
FileInputStream stream = new FileInputStream(fileName);
|
|
||||||
rawFile = stream.readAllBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
FileOutputStream ostream = new FileOutputStream(file);
|
FileOutputStream ostream = new FileOutputStream(file);
|
||||||
ostream.write(rawFile);
|
ostream.write(rawFile);
|
||||||
ostream.close();
|
ostream.close();
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package fr.insa.clavardator.chat;
|
package fr.insa.clavardator.lib.message;
|
||||||
|
|
||||||
import fr.insa.clavardator.users.UserInformation;
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
package fr.insa.clavardator.chat;
|
package fr.insa.clavardator.lib.message;
|
||||||
|
|
||||||
import fr.insa.clavardator.users.CurrentUser;
|
import fr.insa.clavardator.lib.users.User;
|
||||||
import fr.insa.clavardator.users.User;
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
import fr.insa.clavardator.users.UserInformation;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
public class Message implements Serializable {
|
public class Message implements Serializable, SenderInfo, RecipientInfo {
|
||||||
private final String text;
|
private final String text;
|
||||||
private final Date date;
|
private final Date date;
|
||||||
private final UserInformation sender;
|
private final UserInformation sender;
|
||||||
|
|
@ -18,7 +17,7 @@ public class Message implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message(User sender, User recipient, Date date) {
|
public Message(User sender, User recipient, Date date) {
|
||||||
this(sender, recipient, date,"");
|
this(sender, recipient, date, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message(UserInformation sender, UserInformation recipient, Date date, String text) {
|
public Message(UserInformation sender, UserInformation recipient, Date date, String text) {
|
||||||
|
|
@ -47,14 +46,6 @@ public class Message implements Serializable {
|
||||||
return recipient;
|
return recipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserInformation getCorrespondent() {
|
|
||||||
if (CurrentUser.getInstance().getId() != null && CurrentUser.getInstance().getId().equals(sender.id)) {
|
|
||||||
return recipient;
|
|
||||||
} else {
|
|
||||||
return sender;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getDate() {
|
public Date getDate() {
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
@ -68,4 +59,14 @@ public class Message implements Serializable {
|
||||||
", recipient=" + recipient +
|
", recipient=" + recipient +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSenderId() {
|
||||||
|
return sender.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRecipientId() {
|
||||||
|
return recipient.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package fr.insa.clavardator.lib.message;
|
||||||
|
|
||||||
|
public interface RecipientInfo {
|
||||||
|
String getRecipientId();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package fr.insa.clavardator.lib.message;
|
||||||
|
|
||||||
|
public interface SenderInfo {
|
||||||
|
String getSenderId();
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package fr.insa.clavardator.network;
|
package fr.insa.clavardator.lib.network;
|
||||||
|
|
||||||
import fr.insa.clavardator.util.ErrorCallback;
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
import fr.insa.clavardator.lib.util.SimpleCallback;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
|
@ -12,24 +13,13 @@ import java.net.InetAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
public class TcpConnection {
|
public class TcpConnection {
|
||||||
public static final int TCP_PORT = 31598;
|
|
||||||
|
|
||||||
|
private final int port;
|
||||||
|
private final Object outputStreamGuard = new Object();
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
private ObjectOutputStream outputStream;
|
private ObjectOutputStream outputStream;
|
||||||
private ObjectInputStream inputStream;
|
private ObjectInputStream inputStream;
|
||||||
private boolean shouldStop = false;
|
private boolean shouldStop = false;
|
||||||
private final int port;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new connection, and connects to the peer
|
|
||||||
*
|
|
||||||
* @param ipAddr The IP address of the peer
|
|
||||||
* @param callback The function to call when connected
|
|
||||||
* @param errorCallback The function to call on error
|
|
||||||
*/
|
|
||||||
public TcpConnection(InetAddress ipAddr, SocketConnectedCallback callback, ErrorCallback errorCallback) {
|
|
||||||
this(ipAddr, TCP_PORT, callback, errorCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new connection, and connects to the peer
|
* Creates a new connection, and connects to the peer
|
||||||
|
|
@ -61,7 +51,7 @@ public class TcpConnection {
|
||||||
* @param callback The function to call on success
|
* @param callback The function to call on success
|
||||||
* @param errorCallback The function to call on error
|
* @param errorCallback The function to call on error
|
||||||
*/
|
*/
|
||||||
public void send(Serializable message, @Nullable MessageSentCallback callback, @Nullable ErrorCallback errorCallback) {
|
public void send(Serializable message, @Nullable SimpleCallback callback, @Nullable ErrorCallback errorCallback) {
|
||||||
Sender sender = new Sender(message, callback, errorCallback);
|
Sender sender = new Sender(message, callback, errorCallback);
|
||||||
sender.start();
|
sender.start();
|
||||||
}
|
}
|
||||||
|
|
@ -93,22 +83,35 @@ public class TcpConnection {
|
||||||
*/
|
*/
|
||||||
public void close() {
|
public void close() {
|
||||||
shouldStop = true;
|
shouldStop = true;
|
||||||
if (socket != null) {
|
try {
|
||||||
try {
|
if (outputStream != null)
|
||||||
|
outputStream.close();
|
||||||
|
if (inputStream != null)
|
||||||
|
inputStream.close();
|
||||||
|
if (socket != null)
|
||||||
socket.close();
|
socket.close();
|
||||||
} catch (IOException ignored) {
|
} catch (IOException ignored) {
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the current connection is open
|
* Checks if the current connection is open
|
||||||
* @return True if the socket is still open
|
*
|
||||||
|
* @return True if the socket is still open
|
||||||
*/
|
*/
|
||||||
public boolean isOpen() {
|
public boolean isOpen() {
|
||||||
return socket != null && socket.isConnected() && !socket.isClosed();
|
return socket != null && socket.isConnected() && !socket.isClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface SocketConnectedCallback {
|
||||||
|
void onSocketConnected(TcpConnection connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface MessageReceivedCallback {
|
||||||
|
void onMessageReceived(Object msg) throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
private class Connector extends Thread {
|
private class Connector extends Thread {
|
||||||
private final InetAddress ipAddr;
|
private final InetAddress ipAddr;
|
||||||
private final SocketConnectedCallback callback;
|
private final SocketConnectedCallback callback;
|
||||||
|
|
@ -135,34 +138,35 @@ public class TcpConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class Sender extends Thread {
|
private class Sender extends Thread {
|
||||||
private final Serializable message;
|
private final Serializable message;
|
||||||
private final MessageSentCallback callback;
|
private final SimpleCallback callback;
|
||||||
private final ErrorCallback errorCallback;
|
private final ErrorCallback errorCallback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a thread that sends a message using the socket of the outer class
|
* Constructs a thread that sends a message using the socket of the outer class
|
||||||
*
|
*
|
||||||
* @param message The message to send
|
* @param message The message to send
|
||||||
* @param callback The function to call on success
|
* @param callback The function to call on success
|
||||||
* @param errorCallback The function to call on error
|
* @param errorCallback The function to call on error
|
||||||
*/
|
*/
|
||||||
public Sender(Serializable message, @Nullable MessageSentCallback callback, @Nullable ErrorCallback errorCallback) {
|
public Sender(Serializable message, @Nullable SimpleCallback callback, @Nullable ErrorCallback errorCallback) {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.errorCallback = errorCallback;
|
this.errorCallback = errorCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
synchronized public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
if (outputStream == null) {
|
synchronized (outputStreamGuard) {
|
||||||
outputStream = new ObjectOutputStream(socket.getOutputStream());
|
if (outputStream == null) {
|
||||||
|
outputStream = new ObjectOutputStream(socket.getOutputStream());
|
||||||
|
}
|
||||||
|
outputStream.writeObject(message);
|
||||||
}
|
}
|
||||||
outputStream.writeObject(message);
|
|
||||||
if (callback != null)
|
if (callback != null)
|
||||||
callback.onMessageSent();
|
callback.call();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (errorCallback != null && !shouldStop)
|
if (errorCallback != null && !shouldStop)
|
||||||
errorCallback.onError(e);
|
errorCallback.onError(e);
|
||||||
|
|
@ -202,18 +206,4 @@ public class TcpConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public interface SocketConnectedCallback {
|
|
||||||
void onSocketConnected(TcpConnection connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface MessageReceivedCallback {
|
|
||||||
void onMessageReceived(Object msg) throws IOException;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface MessageSentCallback {
|
|
||||||
void onMessageSent();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,30 +1,22 @@
|
||||||
package fr.insa.clavardator.network;
|
package fr.insa.clavardator.lib.network;
|
||||||
|
|
||||||
import fr.insa.clavardator.util.ErrorCallback;
|
import fr.insa.clavardator.lib.util.ErrorCallback;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
import static fr.insa.clavardator.network.TcpConnection.TCP_PORT;
|
|
||||||
|
|
||||||
public class TcpListener {
|
public class TcpListener {
|
||||||
Acceptor acceptor = null;
|
Acceptor acceptor = null;
|
||||||
|
|
||||||
public TcpListener() {
|
public TcpListener() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void acceptConnection(int port, NewConnectionCallback callback, ErrorCallback errorCallback) {
|
||||||
* Start accepting incoming connections
|
|
||||||
*
|
|
||||||
* @param callback The function to call when a user connects
|
|
||||||
* @param errorCallback The function to call on error
|
|
||||||
*/
|
|
||||||
public void acceptConnection(NewConnectionCallback callback, ErrorCallback errorCallback) {
|
|
||||||
if (acceptor != null) {
|
if (acceptor != null) {
|
||||||
acceptor.stopAccepting();
|
acceptor.stopAccepting();
|
||||||
}
|
}
|
||||||
acceptor = new Acceptor(callback, errorCallback);
|
acceptor = new Acceptor(port, callback, errorCallback);
|
||||||
acceptor.start();
|
acceptor.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,13 +30,19 @@ public class TcpListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface NewConnectionCallback {
|
||||||
|
void onNewConnection(Socket clientSocket);
|
||||||
|
}
|
||||||
|
|
||||||
private static class Acceptor extends Thread {
|
private static class Acceptor extends Thread {
|
||||||
private boolean shouldStop = false;
|
|
||||||
private final NewConnectionCallback callback;
|
private final NewConnectionCallback callback;
|
||||||
private final ErrorCallback errorCallback;
|
private final ErrorCallback errorCallback;
|
||||||
|
private final int port;
|
||||||
|
private boolean shouldStop = false;
|
||||||
private ServerSocket server;
|
private ServerSocket server;
|
||||||
|
|
||||||
public Acceptor(NewConnectionCallback callback, ErrorCallback errorCallback) {
|
public Acceptor(int port, NewConnectionCallback callback, ErrorCallback errorCallback) {
|
||||||
|
this.port = port;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.errorCallback = errorCallback;
|
this.errorCallback = errorCallback;
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +50,7 @@ public class TcpListener {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
server = new ServerSocket(TCP_PORT);
|
server = new ServerSocket(port);
|
||||||
while (!shouldStop) {
|
while (!shouldStop) {
|
||||||
Socket clientSocket = server.accept();
|
Socket clientSocket = server.accept();
|
||||||
callback.onNewConnection(clientSocket);
|
callback.onNewConnection(clientSocket);
|
||||||
|
|
@ -61,20 +59,15 @@ public class TcpListener {
|
||||||
if (!shouldStop)
|
if (!shouldStop)
|
||||||
errorCallback.onError(e);
|
errorCallback.onError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopAccepting() {
|
public void stopAccepting() {
|
||||||
shouldStop = true;
|
shouldStop = true;
|
||||||
|
// TODO: call interrupt() with ChannelServerSocket instead of closing the socket?
|
||||||
try {
|
try {
|
||||||
server.close();
|
server.close();
|
||||||
} catch (IOException ignored) {
|
} catch (IOException ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public interface NewConnectionCallback {
|
|
||||||
void onNewConnection(Socket clientSocket);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,24 +1,14 @@
|
||||||
package fr.insa.clavardator.users;
|
package fr.insa.clavardator.lib.users;
|
||||||
|
|
||||||
import fr.insa.clavardator.db.DatabaseController;
|
|
||||||
import fr.insa.clavardator.util.Log;
|
|
||||||
|
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.beans.PropertyChangeSupport;
|
import java.beans.PropertyChangeSupport;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
public class User implements Serializable {
|
public class User implements Serializable {
|
||||||
private String username;
|
|
||||||
protected String id;
|
|
||||||
|
|
||||||
// Make this class observable
|
// Make this class observable
|
||||||
protected final transient PropertyChangeSupport pcs = new PropertyChangeSupport(this);
|
private final transient PropertyChangeSupport pcs = new PropertyChangeSupport(this);
|
||||||
public void addObserver(PropertyChangeListener listener) {
|
private String username;
|
||||||
pcs.addPropertyChangeListener(listener);
|
private String id;
|
||||||
}
|
|
||||||
public void removeObserver(PropertyChangeListener listener) {
|
|
||||||
pcs.removePropertyChangeListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public User() {
|
public User() {
|
||||||
}
|
}
|
||||||
|
|
@ -32,6 +22,17 @@ public class User implements Serializable {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addObserver(PropertyChangeListener listener) {
|
||||||
|
pcs.addPropertyChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeObserver(PropertyChangeListener listener) {
|
||||||
|
pcs.removePropertyChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PropertyChangeSupport getPcs() {
|
||||||
|
return pcs;
|
||||||
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package fr.insa.clavardator.lib.users;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.lib.message.SenderInfo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to serialize useful user information
|
||||||
|
*/
|
||||||
|
public class UserInformation implements Serializable, SenderInfo {
|
||||||
|
public final String id;
|
||||||
|
private final String username;
|
||||||
|
private final UserState state;
|
||||||
|
|
||||||
|
public UserInformation(String id, String username) {
|
||||||
|
this.id = id;
|
||||||
|
this.username = username;
|
||||||
|
this.state = UserState.CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserInformation(String id, String username, UserState state) {
|
||||||
|
this.id = id;
|
||||||
|
this.username = username;
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserInformation(User user) {
|
||||||
|
this.id = user.getId();
|
||||||
|
this.username = user.getUsername();
|
||||||
|
this.state = UserState.CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserInformation(User user, UserState state) {
|
||||||
|
this.id = user.getId();
|
||||||
|
this.username = user.getUsername();
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserState getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "UserInfo " + id + '(' + username + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSenderId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package fr.insa.clavardator.lib.users;
|
||||||
|
|
||||||
|
public enum UserState {
|
||||||
|
CONNECTED,
|
||||||
|
DISCONNECTED,
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package fr.insa.clavardator.util;
|
package fr.insa.clavardator.lib.util;
|
||||||
|
|
||||||
public interface ErrorCallback {
|
public interface ErrorCallback {
|
||||||
void onError(Exception e);
|
void onError(Exception e);
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package fr.insa.clavardator.util;
|
package fr.insa.clavardator.lib.util;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
|
@ -16,13 +16,13 @@ public class Log {
|
||||||
public static int verboseLevel = 4;
|
public static int verboseLevel = 4;
|
||||||
|
|
||||||
private static void print(String prefix, String message, String mode, int requiredLevel, @Nullable Exception e) {
|
private static void print(String prefix, String message, String mode, int requiredLevel, @Nullable Exception e) {
|
||||||
if (verboseLevel >= requiredLevel) {
|
if (verboseLevel >= requiredLevel) {
|
||||||
Date date = new Date();
|
Date date = new Date();
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
|
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
|
||||||
System.out.println(sdf.format(date) + " | " + mode + " | " + prefix + ": " + message);
|
System.out.println(sdf.format(date) + " | " + mode + " | " + prefix + ": " + message);
|
||||||
if (e != null)
|
if (e != null)
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void v(String prefix, String message) {
|
public static void v(String prefix, String message) {
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package fr.insa.clavardator.util;
|
package fr.insa.clavardator.lib.util;
|
||||||
|
|
||||||
public interface ParametrizedCallback<T> {
|
public interface ParametrizedCallback<T> {
|
||||||
void call(T param);
|
void call(T param);
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package fr.insa.clavardator.util;
|
package fr.insa.clavardator.lib.util;
|
||||||
|
|
||||||
public interface SimpleCallback {
|
public interface SimpleCallback {
|
||||||
void call();
|
void call();
|
||||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
BIN
report/report.pdf
Normal file
26
server/build.gradle
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
plugins {
|
||||||
|
id 'application'
|
||||||
|
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
group 'fr.insa.clavardator.server'
|
||||||
|
version '0.0.1'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':lib')
|
||||||
|
implementation 'org.jetbrains:annotations:20.1.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
mainClassName = 'fr.insa.clavardator.server.Main'
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
mergeServiceFiles()
|
||||||
|
}
|
||||||
|
|
||||||
|
run {
|
||||||
|
standardInput = System.in
|
||||||
|
}
|
||||||
93
server/src/main/java/fr/insa/clavardator/server/Main.java
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
package fr.insa.clavardator.server;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
private static final int PROXY_PORT = 35750;
|
||||||
|
private static final int PRESENCE_PORT = 35650;
|
||||||
|
|
||||||
|
private static final List<String> HELP_ARGS = List.of("-h", "--help");
|
||||||
|
private static final List<String> VERSION_ARGS = List.of("-v", "--version");
|
||||||
|
private static final List<String> PROXY_ARGS = List.of("-x", "--proxy");
|
||||||
|
private static final List<String> PRESENCE_ARGS = List.of("-p", "--presence");
|
||||||
|
|
||||||
|
private static int proxyPort = PROXY_PORT;
|
||||||
|
private static int presencePort = PRESENCE_PORT;
|
||||||
|
|
||||||
|
private static void printHelp() {
|
||||||
|
System.out.println("\nClavardator INSA presence server v1.0.0\n");
|
||||||
|
System.out.println("usage: <command> [options]\n");
|
||||||
|
System.out.println("Options:");
|
||||||
|
System.out.println(" -h, --help Display help message.");
|
||||||
|
System.out.println(" -v, --version Display version information.");
|
||||||
|
System.out.println(" -X <port>, --proxy <port> Set the proxy port.");
|
||||||
|
System.out.println(" -p <port>, --presence <port> Set the presence server port.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printVersion() {
|
||||||
|
System.out.println("\nClavardator INSA presence server v1.0.0\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getPort(String value, int defaultValue) {
|
||||||
|
int port;
|
||||||
|
try {
|
||||||
|
port = Integer.parseInt(value.trim());
|
||||||
|
if (port <= 1024 || port >= 64000) {
|
||||||
|
System.out.println("Port should be between 1024 and 64000. Falling back to default.");
|
||||||
|
port = defaultValue;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
System.out.println("Could not read port " + value + ". Falling back to default.");
|
||||||
|
port = defaultValue;
|
||||||
|
}
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void readArgs(String[] args) {
|
||||||
|
if (args.length > 0) {
|
||||||
|
if (HELP_ARGS.contains(args[0])) {
|
||||||
|
printHelp();
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
if (VERSION_ARGS.contains(args[0])) {
|
||||||
|
printVersion();
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < args.length - 1; i++) {
|
||||||
|
if (PROXY_ARGS.contains(args[i])) {
|
||||||
|
proxyPort = getPort(args[i + 1], PROXY_PORT);
|
||||||
|
}
|
||||||
|
if (PRESENCE_ARGS.contains(args[i])) {
|
||||||
|
presencePort = getPort(args[i + 1], PRESENCE_PORT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (presencePort == proxyPort) {
|
||||||
|
System.out.println("Proxy and presence ports must be different");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
readArgs(args);
|
||||||
|
|
||||||
|
System.out.println("\nType q then press enter to exit\n");
|
||||||
|
Log.v(Main.class.getSimpleName(), "Starting proxy on port " + proxyPort);
|
||||||
|
Proxy proxy = new Proxy(proxyPort);
|
||||||
|
Log.v(Main.class.getSimpleName(), "Starting presence server on port " + presencePort);
|
||||||
|
Presence presence = new Presence(presencePort);
|
||||||
|
|
||||||
|
String line;
|
||||||
|
Scanner scanner = new Scanner(System.in);
|
||||||
|
do {
|
||||||
|
line = scanner.nextLine();
|
||||||
|
} while (!line.equals("q"));
|
||||||
|
|
||||||
|
proxy.stop();
|
||||||
|
presence.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
110
server/src/main/java/fr/insa/clavardator/server/Presence.java
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
package fr.insa.clavardator.server;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.lib.network.TcpConnection;
|
||||||
|
import fr.insa.clavardator.lib.network.TcpListener;
|
||||||
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
|
import fr.insa.clavardator.lib.users.UserState;
|
||||||
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class Presence {
|
||||||
|
|
||||||
|
private final Map<String, TcpConnection> subscribers = new HashMap<>();
|
||||||
|
private final ArrayList<UserInformation> connectedUsers = new ArrayList<>();
|
||||||
|
private final TcpListener presenceListener;
|
||||||
|
|
||||||
|
public Presence(int port) {
|
||||||
|
presenceListener = new TcpListener();
|
||||||
|
presenceListener.acceptConnection(
|
||||||
|
port,
|
||||||
|
this::subscribe,
|
||||||
|
e -> Log.e(getClass().getSimpleName(), "Error while registering a user", e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void publish(UserInformation info) {
|
||||||
|
ArrayList<UserInformation> msg = new ArrayList<>(1);
|
||||||
|
msg.add(info);
|
||||||
|
for (Map.Entry<String, TcpConnection> entry : subscribers.entrySet()) {
|
||||||
|
// Do not send update to self
|
||||||
|
if (!entry.getKey().equals(info.id)) {
|
||||||
|
entry.getValue().send(msg, null,
|
||||||
|
e -> Log.e(getClass().getSimpleName(), "Error while publishing user information", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateUser(UserInformation userInformation) {
|
||||||
|
connectedUsers.removeIf(ui -> userInformation.id.equals(ui.id));
|
||||||
|
connectedUsers.add(userInformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adduser(UserInformation userInformation, TcpConnection userConnection) {
|
||||||
|
subscribers.put(userInformation.id, userConnection);
|
||||||
|
connectedUsers.add(userInformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void subscribe(Socket socket) {
|
||||||
|
TcpConnection userConnection = new TcpConnection(socket);
|
||||||
|
|
||||||
|
// Receive user information
|
||||||
|
userConnection.receiveOne(o -> {
|
||||||
|
if (o instanceof UserInformation) {
|
||||||
|
UserInformation userInformation = ((UserInformation) o);
|
||||||
|
Log.v(getClass().getSimpleName(), "Registering user " + userInformation.id +
|
||||||
|
" (" + userInformation.getUsername() + ")");
|
||||||
|
|
||||||
|
// publish new user info to all subscribers
|
||||||
|
publish(userInformation);
|
||||||
|
|
||||||
|
// Send the list of connected users to the new subscriber. We're cloning it because we're modifying it
|
||||||
|
// just after this call, while the sender thread might not have send it yet
|
||||||
|
userConnection.send((Serializable) connectedUsers.clone(), null,
|
||||||
|
e -> Log.e(getClass().getSimpleName(), "Error while receiving user information", e));
|
||||||
|
|
||||||
|
adduser(userInformation, userConnection);
|
||||||
|
|
||||||
|
userConnection.receive(msg -> {
|
||||||
|
if (msg instanceof UserInformation) {
|
||||||
|
UserInformation newUserInformation = ((UserInformation) msg);
|
||||||
|
Log.v(getClass().getSimpleName(), "Receiving user status update " + newUserInformation.id +
|
||||||
|
" (" + newUserInformation.getUsername() + ")");
|
||||||
|
updateUser(newUserInformation);
|
||||||
|
publish(newUserInformation);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
e -> {
|
||||||
|
if (e instanceof EOFException) {
|
||||||
|
unsubscribe(userInformation);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Log.e(getClass().getSimpleName(), "Received unexpected message type: " + o.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
}, e -> {
|
||||||
|
if (!(e instanceof EOFException)) {
|
||||||
|
Log.e(getClass().getSimpleName(), "Error while receiving user information", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unsubscribe(UserInformation userInformation) {
|
||||||
|
Log.v(getClass().getSimpleName(), "Unsubscribing user " + userInformation.id + "(" + userInformation.getUsername() + ")");
|
||||||
|
subscribers.get(userInformation.id).close();
|
||||||
|
subscribers.remove(userInformation.id);
|
||||||
|
publish(new UserInformation(userInformation.id, userInformation.getUsername(), UserState.DISCONNECTED));
|
||||||
|
connectedUsers.removeIf(ui -> userInformation.id.equals(ui.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
presenceListener.stopAccepting();
|
||||||
|
for (TcpConnection connection : subscribers.values()) {
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
server/src/main/java/fr/insa/clavardator/server/Proxy.java
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
package fr.insa.clavardator.server;
|
||||||
|
|
||||||
|
import fr.insa.clavardator.lib.message.RecipientInfo;
|
||||||
|
import fr.insa.clavardator.lib.network.TcpConnection;
|
||||||
|
import fr.insa.clavardator.lib.network.TcpListener;
|
||||||
|
import fr.insa.clavardator.lib.users.UserInformation;
|
||||||
|
import fr.insa.clavardator.lib.util.Log;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class Proxy {
|
||||||
|
private final HashMap<String, TcpConnection> users = new HashMap<>();
|
||||||
|
private final TcpListener proxyListener;
|
||||||
|
|
||||||
|
public Proxy(int port) {
|
||||||
|
proxyListener = new TcpListener();
|
||||||
|
proxyListener.acceptConnection(
|
||||||
|
port,
|
||||||
|
clientSocket -> {
|
||||||
|
|
||||||
|
Log.v(getClass().getSimpleName(), "Accepting a new user");
|
||||||
|
TcpConnection connection = new TcpConnection(clientSocket);
|
||||||
|
connection.receive(msg -> {
|
||||||
|
if (msg instanceof UserInformation) {
|
||||||
|
// Send UserInformation to all other connected users
|
||||||
|
Log.v(getClass().getSimpleName(), "Received UserInformation: " + msg);
|
||||||
|
users.put(((UserInformation) msg).id, connection);
|
||||||
|
for (String userId : users.keySet()) {
|
||||||
|
UserInformation userInfo = ((UserInformation) msg);
|
||||||
|
if (!userId.equals(userInfo.id)) {
|
||||||
|
transmitMessage((Serializable) msg, userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (msg instanceof RecipientInfo) {
|
||||||
|
// Send other messages only to the recipient
|
||||||
|
transmitMessage((Serializable) msg, ((RecipientInfo) msg).getRecipientId());
|
||||||
|
} else {
|
||||||
|
Log.e(getClass().getSimpleName(), "Unexpected message received: " + msg.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}, e -> {
|
||||||
|
if (!(e instanceof EOFException)) {
|
||||||
|
Log.e(getClass().getSimpleName(), "Error while receiving message to transmit", e);
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, TcpConnection> user : users.entrySet()) {
|
||||||
|
if (user.getValue().equals(connection)) {
|
||||||
|
Log.v(getClass().getSimpleName(), "Disconnecting user " + user.getKey());
|
||||||
|
users.remove(user.getKey());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
e -> Log.e(getClass().getSimpleName(), "Error while accepting a user", e));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void transmitMessage(Serializable msg, String recipientId) {
|
||||||
|
TcpConnection user = users.get(recipientId);
|
||||||
|
if (user == null) {
|
||||||
|
Log.e(getClass().getSimpleName(), "Cannot find the recipient in the connected users");
|
||||||
|
} else {
|
||||||
|
Log.v(getClass().getSimpleName(), "Transmitting message: " + msg);
|
||||||
|
user.send(msg, null, e -> Log.e(getClass().getSimpleName(), "Error while sending message", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
proxyListener.stopAccepting();
|
||||||
|
for (TcpConnection connection : users.values()) {
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
rootProject.name = 'clavardator'
|
rootProject.name = 'clavardator'
|
||||||
|
include 'client'
|
||||||
|
include 'lib'
|
||||||
|
include 'server'
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
package fr.insa.clavardator.errors;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
public class UsernameTakenException extends Exception implements Serializable {
|
|
||||||
public UsernameTakenException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
package fr.insa.clavardator.server;
|
|
||||||
|
|
||||||
import fr.insa.clavardator.network.TcpConnection;
|
|
||||||
import fr.insa.clavardator.users.UserInformation;
|
|
||||||
import fr.insa.clavardator.util.ErrorCallback;
|
|
||||||
import fr.insa.clavardator.util.ParametrizedCallback;
|
|
||||||
import fr.insa.clavardator.util.SimpleCallback;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface exposing public methods necessary for any presence server.
|
|
||||||
*
|
|
||||||
* @implNote Implement this interface when creating your own presence server class,
|
|
||||||
* then update the {@link fr.insa.clavardator.server.PresenceFactory factory}
|
|
||||||
* to add your new implementation.
|
|
||||||
*/
|
|
||||||
public interface Presence {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscribes to this presence server notifications.
|
|
||||||
* A list of Ids representing the current active users is returned.
|
|
||||||
*
|
|
||||||
* @param callback Called when subscription completes
|
|
||||||
*/
|
|
||||||
void subscribe(ParametrizedCallback<ArrayList<UserInformation>> callback, @Nullable ErrorCallback errorCallback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops subscription to the presence server by closing TCP connections.
|
|
||||||
* This will stop notifications.
|
|
||||||
*
|
|
||||||
* @implNote Call this before exiting the app.
|
|
||||||
* If not, the presence server will wait for a tcp timeout before marking this user as disconnected.
|
|
||||||
*/
|
|
||||||
void unsubscribe(SimpleCallback callback, @Nullable ErrorCallback errorCallback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a connection to the proxy.
|
|
||||||
* This can be used to initialize a
|
|
||||||
* {@link fr.insa.clavardator.users.PeerUser Peeruser}
|
|
||||||
* and send messages like on a local network.
|
|
||||||
*
|
|
||||||
* @return The server address
|
|
||||||
*/
|
|
||||||
TcpConnection getProxyConnection();
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
package fr.insa.clavardator.server;
|
|
||||||
|
|
||||||
public enum PresenceType {
|
|
||||||
INSA,
|
|
||||||
TEST,
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
package fr.insa.clavardator.ui;
|
|
||||||
|
|
||||||
import fr.insa.clavardator.users.PeerUser;
|
|
||||||
|
|
||||||
public interface UserSelectedEvent {
|
|
||||||
void onSelected(PeerUser user);
|
|
||||||
}
|
|
||||||