Implemented offline mode for Client and LocalDB
This commit is contained in:
parent
5b84578a0a
commit
ea3ad85611
@ -1,6 +1,7 @@
|
|||||||
package envoy.client;
|
package envoy.client;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.ws.rs.client.ClientBuilder;
|
import javax.ws.rs.client.ClientBuilder;
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
@ -32,13 +33,11 @@ public class Client {
|
|||||||
private User sender, recipient;
|
private User sender, recipient;
|
||||||
private boolean online = false;
|
private boolean online = false;
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(Client.class.getSimpleName());
|
public Client(Config config) { this.config = config; }
|
||||||
|
|
||||||
public Client(Config config, String userName) throws EnvoyException {
|
public void onlineInit(String userName) throws EnvoyException {
|
||||||
this.config = config;
|
|
||||||
sender = getUser(userName);
|
sender = getUser(userName);
|
||||||
|
online = true;
|
||||||
logger.info("Client user ID: " + sender.getID());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T, R> R post(String uri, T body, Class<R> responseBodyClass) {
|
private <T, R> R post(String uri, T body, Class<R> responseBodyClass) {
|
||||||
@ -53,22 +52,25 @@ public class Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link Sync} with all users on the server.
|
* Returns a {@code Map<String, User>} of all users on the server with their
|
||||||
|
* user names as keys.
|
||||||
*
|
*
|
||||||
* @return Sync - List of all users on the server.
|
* @return Sync - List of all users on the server.
|
||||||
* @since Envoy v0.1-alpha
|
* @since Envoy v0.2-alpha
|
||||||
*/
|
*/
|
||||||
public Sync getUsersListXml() {
|
public Map<String, User> getUsers() {
|
||||||
Sync sendSync = objectFactory.createSync();
|
Sync sendSync = objectFactory.createSync();
|
||||||
User user = objectFactory.createUser();
|
User user = objectFactory.createUser();
|
||||||
user.setID(-1);
|
user.setID(-1);
|
||||||
sendSync.getUsers().add(user);
|
sendSync.getUsers().add(user);
|
||||||
|
|
||||||
Sync returnSendSync = post(String.format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), 0),
|
Sync returnSync = post(String.format("%s:%d/envoy-server/rest/sync/syncData?userId=%d", config.getServer(), config.getPort(), 0),
|
||||||
sendSync,
|
sendSync,
|
||||||
Sync.class);
|
Sync.class);
|
||||||
return returnSendSync;
|
|
||||||
|
|
||||||
|
Map<String, User> users = new HashMap<>();
|
||||||
|
returnSync.getUsers().forEach(u -> users.put(u.getName(), u));
|
||||||
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -160,6 +162,8 @@ public class Client {
|
|||||||
*/
|
*/
|
||||||
public User getSender() { return sender; }
|
public User getSender() { return sender; }
|
||||||
|
|
||||||
|
public void setSender(User sender) { this.sender = sender; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the current recipient of the current chat.
|
* @return the current recipient of the current chat.
|
||||||
* @since Envoy v0.1-alpha
|
* @since Envoy v0.1-alpha
|
||||||
|
@ -8,7 +8,9 @@ import java.io.ObjectInputStream;
|
|||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.xml.datatype.DatatypeConfigurationException;
|
import javax.xml.datatype.DatatypeConfigurationException;
|
||||||
@ -34,9 +36,9 @@ import envoy.schema.User;
|
|||||||
*/
|
*/
|
||||||
public class LocalDB {
|
public class LocalDB {
|
||||||
|
|
||||||
private File localDBFile, usersFile;
|
private File localDBDir, localDBFile, usersFile;
|
||||||
private User user;
|
private User user;
|
||||||
private List<User> users = new ArrayList<>();
|
private Map<String, User> users = new HashMap<>();
|
||||||
private List<Chat> chats = new ArrayList<>();
|
private List<Chat> chats = new ArrayList<>();
|
||||||
|
|
||||||
private ObjectFactory objectFactory = new ObjectFactory();
|
private ObjectFactory objectFactory = new ObjectFactory();
|
||||||
@ -54,12 +56,19 @@ public class LocalDB {
|
|||||||
*
|
*
|
||||||
* @since Envoy v0.1-alpha
|
* @since Envoy v0.1-alpha
|
||||||
*/
|
*/
|
||||||
public LocalDB() {
|
public LocalDB(File localDBDir) throws IOException {
|
||||||
|
this.localDBDir = localDBDir;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
datatypeFactory = DatatypeFactory.newInstance();
|
datatypeFactory = DatatypeFactory.newInstance();
|
||||||
} catch (DatatypeConfigurationException e) {
|
} catch (DatatypeConfigurationException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize local database directory
|
||||||
|
if (localDBDir.exists() && !localDBDir.isDirectory())
|
||||||
|
throw new IOException(String.format("LocalDBDir '%s' is not a directory!", localDBDir.getAbsolutePath()));
|
||||||
|
usersFile = new File(localDBDir, "users.db");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,18 +79,9 @@ public class LocalDB {
|
|||||||
* @throws EnvoyException if the directory selected is not an actual directory.
|
* @throws EnvoyException if the directory selected is not an actual directory.
|
||||||
* @since Envoy v0.1-alpha
|
* @since Envoy v0.1-alpha
|
||||||
*/
|
*/
|
||||||
public void initializeDBFile(File localDBDir) throws EnvoyException {
|
public void initializeDBFile() throws EnvoyException {
|
||||||
if (user == null) throw new NullPointerException("Client user is null");
|
if (user == null) throw new NullPointerException("Client user is null");
|
||||||
if (localDBDir.exists() && !localDBDir.isDirectory())
|
|
||||||
throw new EnvoyException(String.format("LocalDBDir '%s' is not a directory!", localDBDir.getAbsolutePath()));
|
|
||||||
usersFile = new File(localDBDir, "users.db");
|
|
||||||
localDBFile = new File(localDBDir, user.getID() + ".db");
|
localDBFile = new File(localDBDir, user.getID() + ".db");
|
||||||
try {
|
|
||||||
loadUsers();
|
|
||||||
loadChats();
|
|
||||||
} catch (ClassNotFoundException | IOException e) {
|
|
||||||
throw new EnvoyException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -99,7 +99,7 @@ public class LocalDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void loadUsers() throws ClassNotFoundException, IOException { users = read(usersFile, ArrayList.class); }
|
public void loadUsers() throws ClassNotFoundException, IOException { users = read(usersFile, HashMap.class); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all chats saved by Envoy for the client user.
|
* Loads all chats saved by Envoy for the client user.
|
||||||
@ -110,7 +110,7 @@ public class LocalDB {
|
|||||||
* @since Envoy v0.1-alpha
|
* @since Envoy v0.1-alpha
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void loadChats() throws ClassNotFoundException, IOException { chats = read(localDBFile, ArrayList.class); }
|
public void loadChats() throws ClassNotFoundException, IOException { chats = read(localDBFile, ArrayList.class); }
|
||||||
|
|
||||||
private <T> T read(File file, Class<T> serializedClass) throws ClassNotFoundException, IOException {
|
private <T> T read(File file, Class<T> serializedClass) throws ClassNotFoundException, IOException {
|
||||||
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) {
|
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) {
|
||||||
@ -308,12 +308,12 @@ public class LocalDB {
|
|||||||
/**
|
/**
|
||||||
* @return the users
|
* @return the users
|
||||||
*/
|
*/
|
||||||
public List<User> getUsers() { return users; }
|
public Map<String, User> getUsers() { return users; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param users the users to set
|
* @param users the users to set
|
||||||
*/
|
*/
|
||||||
public void setUsers(List<User> users) { this.users = users; }
|
public void setUsers(Map<String, User> users) { this.users = users; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return all saved {@link Chat} objects that list the client user as the
|
* @return all saved {@link Chat} objects that list the client user as the
|
||||||
|
@ -34,7 +34,6 @@ import envoy.client.Config;
|
|||||||
import envoy.client.LocalDB;
|
import envoy.client.LocalDB;
|
||||||
import envoy.client.Settings;
|
import envoy.client.Settings;
|
||||||
import envoy.schema.Message;
|
import envoy.schema.Message;
|
||||||
import envoy.schema.Sync;
|
|
||||||
import envoy.schema.User;
|
import envoy.schema.User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,8 +131,7 @@ public class ChatWindow extends JFrame {
|
|||||||
@Override
|
@Override
|
||||||
public void keyReleased(KeyEvent e) {
|
public void keyReleased(KeyEvent e) {
|
||||||
if (e.getKeyCode() == KeyEvent.VK_ENTER
|
if (e.getKeyCode() == KeyEvent.VK_ENTER
|
||||||
&& ((Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0)
|
&& ((Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0) || (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK))) {
|
||||||
|| (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK))) {
|
|
||||||
postMessage(messageList);
|
postMessage(messageList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,7 +243,6 @@ public class ChatWindow extends JFrame {
|
|||||||
contentPane.revalidate();
|
contentPane.revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to immediately reload the ChatWindow when settings were changed.
|
* Used to immediately reload the ChatWindow when settings were changed.
|
||||||
*
|
*
|
||||||
@ -287,18 +284,14 @@ public class ChatWindow extends JFrame {
|
|||||||
|
|
||||||
private void postMessage(JList<Message> messageList) {
|
private void postMessage(JList<Message> messageList) {
|
||||||
if (!client.hasRecipient()) {
|
if (!client.hasRecipient()) {
|
||||||
JOptionPane.showMessageDialog(this,
|
JOptionPane.showMessageDialog(this, "Please select a recipient!", "Cannot send message", JOptionPane.INFORMATION_MESSAGE);
|
||||||
"Please select a recipient!",
|
|
||||||
"Cannot send message",
|
|
||||||
JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!messageEnterTextArea.getText().isEmpty()) try {
|
if (!messageEnterTextArea.getText().isEmpty()) try {
|
||||||
|
|
||||||
// Create and send message object
|
// Create and send message object
|
||||||
final Message message = localDB.createMessage(messageEnterTextArea.getText(),
|
final Message message = localDB.createMessage(messageEnterTextArea.getText(), currentChat.getRecipient().getID());
|
||||||
currentChat.getRecipient().getID());
|
|
||||||
currentChat.appendMessage(message);
|
currentChat.appendMessage(message);
|
||||||
messageList.setModel(currentChat.getModel());
|
messageList.setModel(currentChat.getModel());
|
||||||
|
|
||||||
@ -322,9 +315,8 @@ public class ChatWindow extends JFrame {
|
|||||||
*/
|
*/
|
||||||
private void loadUsersAndChats() {
|
private void loadUsersAndChats() {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
Sync users = client.getUsersListXml();
|
|
||||||
DefaultListModel<User> userListModel = new DefaultListModel<>();
|
DefaultListModel<User> userListModel = new DefaultListModel<>();
|
||||||
users.getUsers().forEach(user -> {
|
localDB.getUsers().values().forEach(user -> {
|
||||||
userListModel.addElement(user);
|
userListModel.addElement(user);
|
||||||
|
|
||||||
// Check if user exists in local DB
|
// Check if user exists in local DB
|
||||||
@ -348,8 +340,7 @@ public class ChatWindow extends JFrame {
|
|||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
|
|
||||||
// Synchronize
|
// Synchronize
|
||||||
localDB.applySync(
|
localDB.applySync(client.sendSync(client.getSender().getID(), localDB.fillSync(client.getSender().getID())));
|
||||||
client.sendSync(client.getSender().getID(), localDB.fillSync(client.getSender().getID())));
|
|
||||||
|
|
||||||
// Process unread messages
|
// Process unread messages
|
||||||
localDB.addUnreadMessagesToLocalDB();
|
localDB.addUnreadMessagesToLocalDB();
|
||||||
@ -359,8 +350,7 @@ public class ChatWindow extends JFrame {
|
|||||||
readCurrentChat();
|
readCurrentChat();
|
||||||
|
|
||||||
// Update UI
|
// Update UI
|
||||||
SwingUtilities
|
SwingUtilities.invokeLater(() -> { updateUserStates(); contentPane.revalidate(); contentPane.repaint(); });
|
||||||
.invokeLater(() -> { updateUserStates(); contentPane.revalidate(); contentPane.repaint(); });
|
|
||||||
}).start();
|
}).start();
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
@ -377,4 +367,3 @@ public class ChatWindow extends JFrame {
|
|||||||
*/
|
*/
|
||||||
private void readCurrentChat() { if (currentChat != null) { localDB.setMessagesToRead(currentChat); } }
|
private void readCurrentChat() { if (currentChat != null) { localDB.setMessagesToRead(currentChat); } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package envoy.client.ui;
|
package envoy.client.ui;
|
||||||
|
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -10,6 +11,7 @@ import envoy.client.Client;
|
|||||||
import envoy.client.Config;
|
import envoy.client.Config;
|
||||||
import envoy.client.LocalDB;
|
import envoy.client.LocalDB;
|
||||||
import envoy.exception.EnvoyException;
|
import envoy.exception.EnvoyException;
|
||||||
|
import envoy.schema.User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the Envoy client and prompts the user to enter their name.
|
* Starts the Envoy client and prompts the user to enter their name.
|
||||||
@ -55,24 +57,47 @@ public class Startup {
|
|||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Acquire the client user (with ID) either from the server or from the local
|
// Initialize the local database
|
||||||
// database, which triggers offline mode
|
LocalDB localDB;
|
||||||
Client client;
|
|
||||||
try {
|
try {
|
||||||
client = new Client(config, userName);
|
localDB = new LocalDB(config.getLocalDB());
|
||||||
} catch (Exception e1) {
|
} catch (IOException e3) {
|
||||||
logger.log(Level.SEVERE, "Failed to acquire client user ID", e1);
|
logger.log(Level.SEVERE, "Could not initialize local database", e3);
|
||||||
JOptionPane.showMessageDialog(null, e1.toString(), "Client error", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(null, "Could not initialize local database!\n" + e3.toString());
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the local database
|
// Acquire the client user (with ID) either from the server or from the local
|
||||||
LocalDB localDB = new LocalDB();
|
// database, which triggers offline mode
|
||||||
localDB.setUser(client.getSender());
|
Client client = new Client(config);
|
||||||
try {
|
try {
|
||||||
localDB.initializeDBFile(config.getLocalDB());
|
// Try entering online mode first
|
||||||
} catch (EnvoyException e) {
|
client.onlineInit(userName);
|
||||||
|
} catch (Exception e1) {
|
||||||
|
logger.warning("Could not connect to server. Trying offline mode...");
|
||||||
|
try {
|
||||||
|
// Try entering offline mode
|
||||||
|
localDB.loadUsers();
|
||||||
|
User clientUser = localDB.getUsers().get(userName);
|
||||||
|
if(clientUser == null)
|
||||||
|
throw new EnvoyException("Could not enter offline mode: user name unknown");
|
||||||
|
client.setSender(clientUser);
|
||||||
|
} catch(Exception e2) {
|
||||||
|
JOptionPane.showMessageDialog(null, e1.toString(), "Client error", JOptionPane.ERROR_MESSAGE);
|
||||||
|
System.exit(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set client user in local database
|
||||||
|
localDB.setUser(client.getSender());
|
||||||
|
|
||||||
|
// Initialize chats in local database
|
||||||
|
try {
|
||||||
|
localDB.initializeDBFile();
|
||||||
|
localDB.loadChats();
|
||||||
|
} catch (EnvoyException | ClassNotFoundException | IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
JOptionPane.showMessageDialog(null,
|
JOptionPane.showMessageDialog(null,
|
||||||
"Error while loading local database: " + e.toString() + "\nChats will not be stored locally.",
|
"Error while loading local database: " + e.toString() + "\nChats will not be stored locally.",
|
||||||
@ -80,6 +105,12 @@ public class Startup {
|
|||||||
JOptionPane.WARNING_MESSAGE);
|
JOptionPane.WARNING_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.info("Client user ID: " + client.getSender().getID());
|
||||||
|
|
||||||
|
// Save all users to the local database
|
||||||
|
if(client.isOnline())
|
||||||
|
localDB.setUsers(client.getUsers());
|
||||||
|
|
||||||
EventQueue.invokeLater(() -> {
|
EventQueue.invokeLater(() -> {
|
||||||
try {
|
try {
|
||||||
ChatWindow chatWindow = new ChatWindow(client, localDB);
|
ChatWindow chatWindow = new ChatWindow(client, localDB);
|
||||||
|
Reference in New Issue
Block a user