Add presence server skeleton

This commit is contained in:
Arnaud Vergnet 2021-01-07 09:37:51 +01:00
parent 1cf3f738cb
commit 20e341c190
10 changed files with 298 additions and 56 deletions

View file

@ -1,3 +1,7 @@
{
"serveur" :"test"
"serveur": {
"actif": 1,
"uri": "test",
"type": "INSA"
}
}

View file

@ -28,8 +28,7 @@ public class MainApp extends Application {
final Parent content = mainLoader.load();
MainController mainController = mainLoader.getController();
userList = new UserList();
mainController.setUserList(userList);
userList = mainController.getUserList();
Scene scene = new Scene(content);
@ -44,7 +43,9 @@ public class MainApp extends Application {
@Override
public void stop() throws Exception {
// Stop all threads and active connections before exiting
userList.destroy();
if (userList != null) {
userList.destroy();
}
super.stop();
}
}

View file

@ -0,0 +1,124 @@
package fr.insa.clavardator.config;
import fr.insa.clavardator.server.PresenceType;
import org.json.JSONException;
import org.json.JSONObject;
@SuppressWarnings("FieldCanBeLocal")
public class Config {
private ServerConfig serverConfig;
private final String SERVER_KEY = "serveur";
public Config() {
serverConfig = new ServerConfig();
}
public Config(JSONObject obj) {
this();
if (obj.has(SERVER_KEY)) {
this.serverConfig = new ServerConfig(obj.getJSONObject(SERVER_KEY));
}
}
public ServerConfig getServerConfig() {
return serverConfig;
}
@SuppressWarnings("FieldCanBeLocal")
public static class ServerConfig {
private JSONObject obj;
private boolean enabled;
private String uri;
private PresenceType type;
private final String ENABLED_KEY = "actif";
private final String URI_KEY = "uri";
private final String TYPE_KEY = "type";
/**
* Basic constructor setting the default server configuration
*/
public ServerConfig() {
enabled = false;
uri = "";
type = PresenceType.INSA;
}
/**
* Tries to read the given JSON object to extract custom user configuration
*
* @param obj THe JSON object to read config from
*/
public ServerConfig(JSONObject obj) {
this();
this.obj = obj;
readServerEnabled();
if (enabled) {
readServerUri();
if (uri.isBlank()) {
enabled = false;
} else {
readServerType();
}
}
}
/**
* Reads if the server is enabled.
* Uses false by default.
*/
private void readServerEnabled() {
try {
enabled = obj.getInt(ENABLED_KEY) == 1;
} catch (JSONException e) {
enabled = false;
}
}
/**
* Reads the server URI.
* Uses the empty string as default
*/
private void readServerUri() {
try {
uri = obj.getString(URI_KEY);
} catch (JSONException e) {
uri = "";
}
}
/**
* Reads the server type.
* Uses INSA as default.
*/
private void readServerType() {
try {
type = obj.getEnum(PresenceType.class, TYPE_KEY);
} catch (JSONException e) {
type = PresenceType.INSA;
}
}
/**
* Checks if the presence server 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 String getUri() {
return uri;
}
public PresenceType getType() {
return type;
}
}
}

View file

@ -1,6 +1,6 @@
package fr.insa.clavardator.util;
package fr.insa.clavardator.config;
import org.jetbrains.annotations.Nullable;
import fr.insa.clavardator.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
@ -34,25 +34,4 @@ public class ConfigLoader {
void onLoaded(Config config);
}
public static class Config {
@Nullable
private String serverUri;
private final String SERVER_URI_KEY = "serveur";
public Config() {
serverUri = null;
}
public Config(JSONObject obj) {
this();
if (obj.has(SERVER_URI_KEY)) {
serverUri = obj.getString(SERVER_URI_KEY);
}
}
public @Nullable String getServerUri() {
return serverUri;
}
}
}

View file

@ -0,0 +1,38 @@
package fr.insa.clavardator.server;
import java.net.InetAddress;
import java.util.ArrayList;
public class InsaPresence implements Presence {
private final String path;
public InsaPresence(String path) {
this.path = path;
}
@Override
public ArrayList<String> subscribe() {
return null;
}
@Override
public void publish(boolean connected) {
}
@Override
public void acceptNotifications() {
}
@Override
public void stopNotifications() {
}
@Override
public InetAddress getInetAddress() {
return null;
}
}

View file

@ -0,0 +1,41 @@
package fr.insa.clavardator.server;
import java.net.InetAddress;
import java.util.ArrayList;
public interface Presence {
/**
* Subscribes to this presence server notifications.
* A list of Ids representing the current active users is returned.
*
* @return The list of connected user's Ids
*/
ArrayList<String> subscribe();
/**
* Updates the current user state on the server.
* This function must be called on app exit,
* or the server won't know this user is inactive.
*
* @param connected The new user state
*/
void publish(boolean connected);
/**
* Starts listening to presence server update notifications.
*/
void acceptNotifications();
/**
* Stops listening to presence server update notifications.
*/
void stopNotifications();
/**
* Gets the presence server InetAddress
*
* @return The server address
*/
InetAddress getInetAddress();
}

View file

@ -0,0 +1,12 @@
package fr.insa.clavardator.server;
public class PresenceFactory {
public static Presence create(PresenceType type, String uri) throws UnknownPresenceException {
switch (type) {
case INSA:
return new InsaPresence(uri);
default:
throw new UnknownPresenceException();
}
}
}

View file

@ -0,0 +1,6 @@
package fr.insa.clavardator.server;
public enum PresenceType {
INSA,
TEST,
}

View file

@ -0,0 +1,7 @@
package fr.insa.clavardator.server;
public class UnknownPresenceException extends Exception {
public UnknownPresenceException() {
super("Unknown presence server sepcified");
}
}

View file

@ -1,7 +1,12 @@
package fr.insa.clavardator.ui;
import com.jfoenix.controls.JFXSnackbar;
import fr.insa.clavardator.config.Config;
import fr.insa.clavardator.db.DatabaseController;
import fr.insa.clavardator.server.Presence;
import fr.insa.clavardator.server.PresenceFactory;
import fr.insa.clavardator.server.PresenceType;
import fr.insa.clavardator.server.UnknownPresenceException;
import fr.insa.clavardator.ui.chat.ChatController;
import fr.insa.clavardator.ui.dialogs.AboutDialogController;
import fr.insa.clavardator.ui.dialogs.EditUsernameDialogController;
@ -9,7 +14,7 @@ import fr.insa.clavardator.ui.dialogs.SnackbarController;
import fr.insa.clavardator.ui.users.UserListController;
import fr.insa.clavardator.users.CurrentUser;
import fr.insa.clavardator.users.UserList;
import fr.insa.clavardator.util.ConfigLoader;
import fr.insa.clavardator.config.ConfigLoader;
import fr.insa.clavardator.util.Log;
import javafx.application.Platform;
import javafx.fxml.FXML;
@ -47,6 +52,7 @@ public class MainController implements Initializable {
private JFXSnackbar snackbar;
private UserList userList;
private Presence presenceServer;
private boolean online;
public MainController() {
@ -92,7 +98,7 @@ public class MainController implements Initializable {
* </li>
* </ul>
*
* @param mode
* @param mode The dialog mode
*/
private void openEditUsernameDialog(EditUsernameDialogController.Mode mode) {
final boolean initialMode = mode == EditUsernameDialogController.Mode.INITIAL;
@ -233,36 +239,19 @@ public class MainController implements Initializable {
errorController.show();
}
@Override
public void initialize(URL url, ResourceBundle rb) {
loadingController.show("Initialisation de Clavardator...");
snackbar = new JFXSnackbar(root);
listController.setUserSelectedListener((user) -> chatController.setRemoteUser(user));
// chatController.setAttachmentListener(() -> System.out.println("attach event"));
chatController.setSendErrorListener((e) -> showSnackbarEvent("Erreur: Message non envoyé", SnackbarController.Mode.ERROR));
toolbarController.setEditListener(() -> openEditUsernameDialog(EditUsernameDialogController.Mode.EDIT));
toolbarController.setAboutListener(this::openAboutDialog);
}
private void onInitError(Exception e) {
Log.e("INIT", "Error during initialization", e);
showError();
}
/**
* Creates database if needed, then init current user, and finally load user list
* Reads user config, then creates database if needed,
* then init current user, and finally load user list.
*/
private void initBackend() {
ConfigLoader.load((ConfigLoader.Config config) -> {
final String serverUri = config.getServerUri();
if (serverUri != null) {
Log.v("INIT", "Found server URI: " + serverUri);
} else {
Log.v("INIT", "Server URI not found. Proceeding without");
}
final DatabaseController db = new DatabaseController();
db.initTables(
ConfigLoader.load((Config config) -> {
initPresenceServer(config);
new DatabaseController().initTables(
() -> userList.retrievedPreviousUsers(
() -> currentUser.init(this::onInitError),
this::onInitError
@ -271,16 +260,57 @@ public class MainController implements Initializable {
}
/**
* Sets the user list to use.
* We must set it from the MainApp to allow destroying it on exit.
* Initializes the presence server based on user config
*
* @param userList The user list to use
* @param config The user config
*/
public void setUserList(UserList userList) {
this.userList = userList;
private void initPresenceServer(Config config) {
final Config.ServerConfig serverConfig = config.getServerConfig();
if (serverConfig.isEnabled()) {
try {
final PresenceType type = serverConfig.getType();
final String uri = serverConfig.getUri();
presenceServer = PresenceFactory.create(type, uri);
Log.v("INIT", "Presence server support enabled: " + type + " @ " + uri);
} catch (UnknownPresenceException e) {
Log.e("INIT", "Presence server type not found", e);
presenceServer = null;
}
} else {
Log.v("INIT", "Presence server support disabled.");
}
}
/**
* Gets the current user list.
*
* @implNote BE SURE TO CALL .destroy() BEFORE EXITING THE APP.
*
* @return The current user list
*/
public UserList getUserList() {
return userList;
}
@Override
public void initialize(URL url, ResourceBundle rb) {
loadingController.show("Initialisation de Clavardator...");
snackbar = new JFXSnackbar(root);
userList = new UserList();
listController.setUserList(userList);
listController.setRefreshUserListener(this::discoverActiveUsers);
listController.setUserSelectedListener((user) -> chatController.setRemoteUser(user));
chatController.setSendErrorListener((e) -> showSnackbarEvent("Erreur: Message non envoyé", SnackbarController.Mode.ERROR));
toolbarController.setEditListener(() -> openEditUsernameDialog(EditUsernameDialogController.Mode.EDIT));
toolbarController.setAboutListener(this::openAboutDialog);
editUserDialogController.setUserList(userList);
initBackend();
}
}