Merge branch 'develop' into f/contacts
This commit is contained in:
		@@ -6,6 +6,7 @@ import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.prefs.Preferences;
 | 
			
		||||
 | 
			
		||||
import envoy.client.data.Config;
 | 
			
		||||
import envoy.client.ui.Color;
 | 
			
		||||
import envoy.client.ui.Theme;
 | 
			
		||||
import envoy.util.SerializationUtils;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										61
									
								
								src/main/java/envoy/client/data/Cache.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/main/java/envoy/client/data/Cache.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
package envoy.client.data;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.LinkedList;
 | 
			
		||||
import java.util.Queue;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import envoy.client.util.EnvoyLog;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Stores elements in a queue to process them later.<br>
 | 
			
		||||
 * <br>
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>Cache.java</strong><br>
 | 
			
		||||
 * Created: <strong>6 Feb 2020</strong><br>
 | 
			
		||||
 *
 | 
			
		||||
 * @param <T> the type of cached elements
 | 
			
		||||
 * @author Kai S. K. Engelbart
 | 
			
		||||
 * @since Envoy v0.3-alpha
 | 
			
		||||
 */
 | 
			
		||||
public class Cache<T> implements Consumer<T>, Serializable {
 | 
			
		||||
 | 
			
		||||
	private final Queue<T>			elements	= new LinkedList<>();
 | 
			
		||||
	private transient Consumer<T>	processor;
 | 
			
		||||
 | 
			
		||||
	private static final Logger logger = EnvoyLog.getLogger(Cache.class.getSimpleName());
 | 
			
		||||
	private static final long	serialVersionUID	= 7343544675545545076L;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Adds an element to the cache.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param element the element to add
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public void accept(T element) {
 | 
			
		||||
		logger.info(String.format("Adding element %s to cache", element));
 | 
			
		||||
		elements.offer(element);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sets the processor to which cached elements are relayed.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param processor the processor to set
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void setProcessor(Consumer<T> processor) { this.processor = processor; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Relays all cached elements to the processor.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @throws IllegalStateException if the processor is not initialized
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void relay() {
 | 
			
		||||
		if (processor == null) throw new IllegalStateException("Processor is not defined");
 | 
			
		||||
		elements.forEach(processor::accept);
 | 
			
		||||
		elements.clear();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -3,7 +3,7 @@ package envoy.client.data;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
import envoy.client.net.Client;
 | 
			
		||||
import envoy.client.net.WriteProxy;
 | 
			
		||||
import envoy.client.ui.list.ComponentListModel;
 | 
			
		||||
import envoy.data.Message;
 | 
			
		||||
import envoy.data.Message.MessageStatus;
 | 
			
		||||
@@ -52,22 +52,20 @@ public class Chat implements Serializable {
 | 
			
		||||
	 * {@code READ} starting from the bottom and stopping once a read message is
 | 
			
		||||
	 * found.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param client the client instance used to notify the server about the message
 | 
			
		||||
	 *               status changes
 | 
			
		||||
	 * @param writeProxy the write proxy instance used to notify the server about
 | 
			
		||||
	 *                   the message status changes
 | 
			
		||||
	 * @throws IOException if a {@link MessageStatusChangeEvent} could not be
 | 
			
		||||
	 *                     delivered to the server
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void read(Client client) throws IOException {
 | 
			
		||||
	public void read(WriteProxy writeProxy) throws IOException {
 | 
			
		||||
		for (int i = model.size() - 1; i >= 0; --i) {
 | 
			
		||||
			final Message m = model.get(i);
 | 
			
		||||
			if (m.getSenderId() == recipient.getId()) {
 | 
			
		||||
				if (m.getStatus() == MessageStatus.READ) break;
 | 
			
		||||
				else {
 | 
			
		||||
					m.setStatus(MessageStatus.READ);
 | 
			
		||||
 | 
			
		||||
					// TODO: Cache events in offline mode
 | 
			
		||||
					client.sendEvent(new MessageStatusChangeEvent(m));
 | 
			
		||||
					writeProxy.writeMessageStatusChangeEvent(new MessageStatusChangeEvent(m));
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,12 @@
 | 
			
		||||
package envoy.client;
 | 
			
		||||
package envoy.client.data;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.logging.Level;
 | 
			
		||||
 | 
			
		||||
import envoy.data.LoginCredentials;
 | 
			
		||||
import envoy.exception.EnvoyException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -26,14 +29,15 @@ public class Config {
 | 
			
		||||
	private static Config config;
 | 
			
		||||
 | 
			
		||||
	private Config() {
 | 
			
		||||
		items.put("server", new ConfigItem<>("server", "s", (input) -> input, null));
 | 
			
		||||
		items.put("port", new ConfigItem<>("port", "p", (input) -> Integer.parseInt(input), null));
 | 
			
		||||
		items.put("localDB", new ConfigItem<>("localDB", "db", (input) -> new File(input), new File("localDB")));
 | 
			
		||||
		items.put("ignoreLocalDB", new ConfigItem<>("ignoreLocalDB", "nodb", (input) -> Boolean.parseBoolean(input), false));
 | 
			
		||||
		items.put("homeDirectory",
 | 
			
		||||
				new ConfigItem<>("homeDirectory", "h", (input) -> new File(input), new File(System.getProperty("user.home"), ".envoy")));
 | 
			
		||||
		items.put("fileLevelBarrier", new ConfigItem<>("fileLevelBarrier", "fb", (input) -> Level.parse(input), Level.CONFIG));
 | 
			
		||||
		items.put("consoleLevelBarrier", new ConfigItem<>("consoleLevelBarrier", "cb", (input) -> Level.parse(input), Level.FINEST));
 | 
			
		||||
		items.put("server", new ConfigItem<>("server", "s", Function.identity(), null, true));
 | 
			
		||||
		items.put("port", new ConfigItem<>("port", "p", Integer::parseInt, null, true));
 | 
			
		||||
		items.put("localDB", new ConfigItem<>("localDB", "db", File::new, new File("localDB"), true));
 | 
			
		||||
		items.put("ignoreLocalDB", new ConfigItem<>("ignoreLocalDB", "nodb", Boolean::parseBoolean, false, false));
 | 
			
		||||
		items.put("homeDirectory", new ConfigItem<>("homeDirectory", "h", File::new, new File(System.getProperty("user.home"), ".envoy"), true));
 | 
			
		||||
		items.put("fileLevelBarrier", new ConfigItem<>("fileLevelBarrier", "fb", Level::parse, Level.CONFIG, true));
 | 
			
		||||
		items.put("consoleLevelBarrier", new ConfigItem<>("consoleLevelBarrier", "cb", Level::parse, Level.FINEST, true));
 | 
			
		||||
		items.put("user", new ConfigItem<>("user", "u", Function.identity()));
 | 
			
		||||
		items.put("password", new ConfigItem<>("password", "pw", String::toCharArray));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
@@ -95,10 +99,12 @@ public class Config {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return {@code true} if server, port and localDB directory are known.
 | 
			
		||||
	 * @return {@code true} if all mandatory config items are initialized
 | 
			
		||||
	 * @since Envoy v0.1-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean isInitialized() { return items.values().stream().map(ConfigItem::get).noneMatch(Objects::isNull); }
 | 
			
		||||
	public boolean isInitialized() {
 | 
			
		||||
		return items.values().stream().filter(ConfigItem::isMandatory).map(ConfigItem::get).noneMatch(Objects::isNull);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return the host name of the Envoy server
 | 
			
		||||
@@ -141,4 +147,35 @@ public class Config {
 | 
			
		||||
	 * @since Envoy v0.2-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public Level getConsoleLevelBarrier() { return (Level) items.get("consoleLevelBarrier").get(); }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return the user name
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public String getUser() { return (String) items.get("user").get(); }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return the password
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public char[] getPassword() { return (char[]) items.get("password").get(); }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return {@code true} if user name and password are set
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean hasLoginCredentials() { return getUser() != null && getPassword() != null; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return login credentials for the specified user name and password, without
 | 
			
		||||
	 *         the registration option
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public LoginCredentials getLoginCredentials() {
 | 
			
		||||
		try {
 | 
			
		||||
			return new LoginCredentials(getUser(), getPassword(), false);
 | 
			
		||||
		} catch (NoSuchAlgorithmException e) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package envoy.client;
 | 
			
		||||
package envoy.client.data;
 | 
			
		||||
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
@@ -9,49 +9,70 @@ import java.util.function.Function;
 | 
			
		||||
 * Project: <strong>envoy-clientChess</strong><br>
 | 
			
		||||
 * File: <strong>ConfigItem.javaEvent.java</strong><br>
 | 
			
		||||
 * Created: <strong>21.12.2019</strong><br>
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * @author Kai S. K. Engelbart
 | 
			
		||||
 * @param <T> the type of the config item's value
 | 
			
		||||
 * @since Envoy v0.2-alpha
 | 
			
		||||
 */
 | 
			
		||||
public class ConfigItem<T> {
 | 
			
		||||
 | 
			
		||||
	private String				commandLong, commandShort;
 | 
			
		||||
	private Function<String, T>	parseFunction;
 | 
			
		||||
	private T					value;
 | 
			
		||||
	private final String				commandLong, commandShort;
 | 
			
		||||
	private final Function<String, T>	parseFunction;
 | 
			
		||||
	private final boolean				mandatory;
 | 
			
		||||
 | 
			
		||||
	private T value;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Initializes a {@link ConfigItem}
 | 
			
		||||
	 * 
 | 
			
		||||
	 * Initializes a {@link ConfigItem}.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param commandLong   the long command line argument to set this value
 | 
			
		||||
	 * @param commandShort  the short command line argument to set this value
 | 
			
		||||
	 * @param parseFunction the {@code Function<String, T>} that parses the value
 | 
			
		||||
	 *                      from a string
 | 
			
		||||
	 * @param defaultValue  the optional default value to set before parsing
 | 
			
		||||
	 * @param mandatory     indicated that this config item must be initialized with
 | 
			
		||||
	 *                      a non-null value
 | 
			
		||||
	 * @since Envoy v0.2-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public ConfigItem(String commandLong, String commandShort, Function<String, T> parseFunction, T defaultValue) {
 | 
			
		||||
	public ConfigItem(String commandLong, String commandShort, Function<String, T> parseFunction, T defaultValue, boolean mandatory) {
 | 
			
		||||
		this.commandLong	= commandLong;
 | 
			
		||||
		this.commandShort	= commandShort;
 | 
			
		||||
		this.parseFunction	= parseFunction;
 | 
			
		||||
		this.mandatory		= mandatory;
 | 
			
		||||
		value				= defaultValue;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Parses this {@ConfigItem}'s value from a string
 | 
			
		||||
	 * Initializes an optional {@link ConfigItem} without a default value.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param commandLong   the long command line argument to set this value
 | 
			
		||||
	 * @param commandShort  the short command line argument to set this value
 | 
			
		||||
	 * @param parseFunction the {@code Function<String, T>} that parses the value
 | 
			
		||||
	 *                      from a string
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public ConfigItem(String commandLong, String commandShort, Function<String, T> parseFunction) {
 | 
			
		||||
		this(commandLong, commandShort, parseFunction, null, false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Parses this {@ConfigItem}'s value from a string.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param input the string to parse from
 | 
			
		||||
	 * @since Envoy v0.2-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void parse(String input) { value = parseFunction.apply(input); }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return The long command line argument to set the value of this {@link ConfigItem}
 | 
			
		||||
	 * @return The long command line argument to set the value of this
 | 
			
		||||
	 *         {@link ConfigItem}
 | 
			
		||||
	 * @since Envoy v0.2-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public String getCommandLong() { return commandLong; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return The short command line argument to set the value of this {@link ConfigItem}
 | 
			
		||||
	 * @return The short command line argument to set the value of this
 | 
			
		||||
	 *         {@link ConfigItem}
 | 
			
		||||
	 * @since Envoy v0.2-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public String getCommandShort() { return commandShort; }
 | 
			
		||||
@@ -61,4 +82,11 @@ public class ConfigItem<T> {
 | 
			
		||||
	 * @since Envoy v0.2-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public T get() { return value; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return {@code true} if this {@link ConfigItem} is mandatory for successful
 | 
			
		||||
	 *         application initialization
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean isMandatory() { return mandatory; }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,7 +3,9 @@ package envoy.client.data;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
import envoy.data.IdGenerator;
 | 
			
		||||
import envoy.data.Message;
 | 
			
		||||
import envoy.data.User;
 | 
			
		||||
import envoy.event.MessageStatusChangeEvent;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Stores information about the current {@link User} and their {@link Chat}s.
 | 
			
		||||
@@ -18,10 +20,12 @@ import envoy.data.User;
 | 
			
		||||
 */
 | 
			
		||||
public abstract class LocalDb {
 | 
			
		||||
 | 
			
		||||
	protected User				user;
 | 
			
		||||
	protected Map<String, User>	users	= new HashMap<>();
 | 
			
		||||
	protected List<Chat>		chats	= new ArrayList<>();
 | 
			
		||||
	protected IdGenerator		idGenerator;
 | 
			
		||||
	protected User								user;
 | 
			
		||||
	protected Map<String, User>					users	= new HashMap<>();
 | 
			
		||||
	protected List<Chat>						chats	= new ArrayList<>();
 | 
			
		||||
	protected IdGenerator						idGenerator;
 | 
			
		||||
	protected Cache<Message>					messageCache	= new Cache<>();
 | 
			
		||||
	protected Cache<MessageStatusChangeEvent>	statusCache		= new Cache<>();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Initializes a storage space for a user-specific list of chats.
 | 
			
		||||
@@ -48,12 +52,12 @@ public abstract class LocalDb {
 | 
			
		||||
	public void loadUsers() throws Exception {}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Loads all chat data of the client user.
 | 
			
		||||
	 * Loads all data of the client user.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @throws Exception if the loading process failed
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void loadChats() throws Exception {}
 | 
			
		||||
	public void loadUserData() throws Exception {}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Loads the ID generator. Any exception thrown during this process is ignored.
 | 
			
		||||
@@ -115,4 +119,28 @@ public abstract class LocalDb {
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean hasIdGenerator() { return idGenerator != null; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return the offline message cache
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public Cache<Message> getMessageCache() { return messageCache; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param messageCache the offline message cache to set
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void setMessageCache(Cache<Message> messageCache) { this.messageCache = messageCache; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return the offline status cache
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public Cache<MessageStatusChangeEvent> getStatusCache() { return statusCache; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param statusCache the offline status cache to set
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void setStatusCache(Cache<MessageStatusChangeEvent> statusCache) { this.statusCache = statusCache; }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ import java.io.IOException;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
 | 
			
		||||
import envoy.client.ConfigItem;
 | 
			
		||||
import envoy.data.IdGenerator;
 | 
			
		||||
import envoy.util.SerializationUtils;
 | 
			
		||||
 | 
			
		||||
@@ -23,7 +22,7 @@ import envoy.util.SerializationUtils;
 | 
			
		||||
 */
 | 
			
		||||
public class PersistentLocalDb extends LocalDb {
 | 
			
		||||
 | 
			
		||||
	private File localDBDir, localDBFile, usersFile, idGeneratorFile;
 | 
			
		||||
	private File localDBDir, localDBFile, usersFile, idGeneratorFile, messageCacheFile, statusCacheFile;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Initializes an empty local database without a directory. All changes made to
 | 
			
		||||
@@ -40,18 +39,18 @@ public class PersistentLocalDb extends LocalDb {
 | 
			
		||||
	 * Constructs an empty local database. To serialize any chats to the file
 | 
			
		||||
	 * system, call {@link PersistentLocalDb#initializeUserStorage()}.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param localDBDir the directory in which to store users and chats
 | 
			
		||||
	 * @param localDbDir the directory in which to store users and chats
 | 
			
		||||
	 * @throws IOException if the PersistentLocalDb could not be initialized
 | 
			
		||||
	 * @since Envoy v0.1-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public PersistentLocalDb(File localDBDir) throws IOException {
 | 
			
		||||
		this.localDBDir = localDBDir;
 | 
			
		||||
	public PersistentLocalDb(File localDbDir) throws IOException {
 | 
			
		||||
		localDBDir = localDbDir;
 | 
			
		||||
 | 
			
		||||
		// 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");
 | 
			
		||||
		idGeneratorFile	= new File(localDBDir, "id_generator.db");
 | 
			
		||||
		if (localDbDir.exists() && !localDbDir.isDirectory())
 | 
			
		||||
			throw new IOException(String.format("LocalDbDir '%s' is not a directory!", localDbDir.getAbsolutePath()));
 | 
			
		||||
		usersFile		= new File(localDbDir, "users.db");
 | 
			
		||||
		idGeneratorFile	= new File(localDbDir, "id_generator.db");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
@@ -63,7 +62,9 @@ public class PersistentLocalDb extends LocalDb {
 | 
			
		||||
	@Override
 | 
			
		||||
	public void initializeUserStorage() {
 | 
			
		||||
		if (user == null) throw new NullPointerException("Client user is null");
 | 
			
		||||
		localDBFile = new File(localDBDir, user.getId() + ".db");
 | 
			
		||||
		localDBFile			= new File(localDBDir, user.getId() + ".db");
 | 
			
		||||
		messageCacheFile	= new File(localDBDir, user.getId() + "_message_cache.db");
 | 
			
		||||
		statusCacheFile		= new File(localDBDir, user.getId() + "_status_cache.db");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
@@ -71,8 +72,12 @@ public class PersistentLocalDb extends LocalDb {
 | 
			
		||||
		// Save users
 | 
			
		||||
		SerializationUtils.write(usersFile, users);
 | 
			
		||||
 | 
			
		||||
		// Save chats
 | 
			
		||||
		if (user != null) SerializationUtils.write(localDBFile, chats);
 | 
			
		||||
		// Save user data
 | 
			
		||||
		if (user != null) {
 | 
			
		||||
			SerializationUtils.write(localDBFile, chats);
 | 
			
		||||
			SerializationUtils.write(messageCacheFile, messageCache);
 | 
			
		||||
			SerializationUtils.write(statusCacheFile, statusCache);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Save id generator
 | 
			
		||||
		if (hasIdGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator);
 | 
			
		||||
@@ -82,7 +87,11 @@ public class PersistentLocalDb extends LocalDb {
 | 
			
		||||
	public void loadUsers() throws ClassNotFoundException, IOException { users = SerializationUtils.read(usersFile, HashMap.class); }
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void loadChats() throws ClassNotFoundException, IOException { chats = SerializationUtils.read(localDBFile, ArrayList.class); }
 | 
			
		||||
	public void loadUserData() throws ClassNotFoundException, IOException {
 | 
			
		||||
		chats			= SerializationUtils.read(localDBFile, ArrayList.class);
 | 
			
		||||
		messageCache	= SerializationUtils.read(messageCacheFile, Cache.class);
 | 
			
		||||
		statusCache		= SerializationUtils.read(statusCacheFile, Cache.class);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void loadIdGenerator() {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,8 @@ import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import javax.naming.TimeLimitExceededException;
 | 
			
		||||
 | 
			
		||||
import envoy.client.Config;
 | 
			
		||||
import envoy.client.data.Cache;
 | 
			
		||||
import envoy.client.data.Config;
 | 
			
		||||
import envoy.client.data.LocalDb;
 | 
			
		||||
import envoy.client.event.SearchResultEvent;
 | 
			
		||||
import envoy.client.util.EnvoyLog;
 | 
			
		||||
@@ -51,16 +52,17 @@ public class Client implements Closeable {
 | 
			
		||||
	 * will block for up to 5 seconds. If the handshake does exceed this time limit,
 | 
			
		||||
	 * an exception is thrown.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param credentials the login credentials of the user
 | 
			
		||||
	 * @param localDb     the local database used to persist the current
 | 
			
		||||
	 *                    {@link IdGenerator}
 | 
			
		||||
	 * @return a message cache containing all unread messages from the server that
 | 
			
		||||
	 *         can be relayed after initialization
 | 
			
		||||
	 * @param credentials          the login credentials of the user
 | 
			
		||||
	 * @param localDb              the local database used to persist the current
 | 
			
		||||
	 *                             {@link IdGenerator}
 | 
			
		||||
	 * @param receivedMessageCache a message cache containing all unread messages
 | 
			
		||||
	 *                             from the server that can be relayed after
 | 
			
		||||
	 *                             initialization
 | 
			
		||||
	 * @throws Exception if the online mode could not be entered or the request
 | 
			
		||||
	 *                   failed for some other reason
 | 
			
		||||
	 * @since Envoy v0.2-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public MessageCache onlineInit(LoginCredentials credentials, LocalDb localDb) throws Exception {
 | 
			
		||||
	public void onlineInit(LoginCredentials credentials, LocalDb localDb, Cache<Message> receivedMessageCache) throws Exception {
 | 
			
		||||
		// Establish TCP connection
 | 
			
		||||
		logger.info(String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort()));
 | 
			
		||||
		socket = new Socket(config.getServer(), config.getPort());
 | 
			
		||||
@@ -69,13 +71,10 @@ public class Client implements Closeable {
 | 
			
		||||
		// Create message receiver
 | 
			
		||||
		receiver = new Receiver(socket.getInputStream());
 | 
			
		||||
 | 
			
		||||
		// Create cache for unread messages
 | 
			
		||||
		final MessageCache cache = new MessageCache();
 | 
			
		||||
 | 
			
		||||
		// Register user creation processor, contact list processor and message cache
 | 
			
		||||
		receiver.registerProcessor(User.class, sender -> { logger.info("Acquired user object " + sender); this.sender = sender; });
 | 
			
		||||
		receiver.registerProcessor(Contacts.class, contacts -> { logger.info("Acquired contacts object " + contacts); this.contacts = contacts; });
 | 
			
		||||
		receiver.registerProcessor(Message.class, cache);
 | 
			
		||||
		receiver.registerProcessor(Message.class, receivedMessageCache);
 | 
			
		||||
 | 
			
		||||
		// Start receiver
 | 
			
		||||
		new Thread(receiver).start();
 | 
			
		||||
@@ -98,15 +97,20 @@ public class Client implements Closeable {
 | 
			
		||||
		receiver.removeAllProcessors();
 | 
			
		||||
 | 
			
		||||
		// Register processors for message and status handling
 | 
			
		||||
 | 
			
		||||
		// Process incoming messages
 | 
			
		||||
		final ReceivedMessageProcessor receivedMessageProcessor = new ReceivedMessageProcessor();
 | 
			
		||||
		receiver.registerProcessor(Message.class, receivedMessageProcessor);
 | 
			
		||||
 | 
			
		||||
		// Relay cached unread messages
 | 
			
		||||
		cache.setProcessor(receivedMessageProcessor);
 | 
			
		||||
		receivedMessageCache.setProcessor(receivedMessageProcessor);
 | 
			
		||||
 | 
			
		||||
		// Process message status changes
 | 
			
		||||
		receiver.registerProcessor(MessageStatusChangeEvent.class, new MessageStatusChangeEventProcessor());
 | 
			
		||||
 | 
			
		||||
		// Process user status changes
 | 
			
		||||
		receiver.registerProcessor(UserStatusChangeEvent.class, new UserStatusChangeProcessor(this));
 | 
			
		||||
 | 
			
		||||
		// Process message ID generation
 | 
			
		||||
		receiver.registerProcessor(IdGenerator.class, localDb::setIdGenerator);
 | 
			
		||||
 | 
			
		||||
@@ -115,10 +119,19 @@ public class Client implements Closeable {
 | 
			
		||||
 | 
			
		||||
		// Request a generator if none is present or the existing one is consumed
 | 
			
		||||
		if (!localDb.hasIdGenerator() || !localDb.getIdGenerator().hasNext()) requestIdGenerator();
 | 
			
		||||
 | 
			
		||||
		return cache;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates a new write proxy that uses this client to communicate with the
 | 
			
		||||
	 * server.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param localDb the local database that the write proxy will use to access
 | 
			
		||||
	 *                caches
 | 
			
		||||
	 * @return a new write proxy
 | 
			
		||||
	 * @since Envoy Client v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public WriteProxy createWriteProxy(LocalDb localDb) { return new WriteProxy(this, localDb); }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sends a message to the server. The message's status will be incremented once
 | 
			
		||||
	 * it was delivered successfully.
 | 
			
		||||
@@ -197,4 +210,16 @@ public class Client implements Closeable {
 | 
			
		||||
	 * @since Envoy v0.2-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean isOnline() { return online; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return the contacts of this {@link Client}
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public Contacts getContacts() { return contacts; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param contacts the contacts to set
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void setContacts(Contacts contacts) { this.contacts = contacts; }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,54 +0,0 @@
 | 
			
		||||
package envoy.client.net;
 | 
			
		||||
 | 
			
		||||
import java.util.LinkedList;
 | 
			
		||||
import java.util.Queue;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import envoy.client.util.EnvoyLog;
 | 
			
		||||
import envoy.data.Message;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Stores messages in a queue until the application initialization is complete.
 | 
			
		||||
 * The messages can then be relayed to a processor.<br>
 | 
			
		||||
 * <br>
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>MessageCache.java</strong><br>
 | 
			
		||||
 * Created: <strong>4 Feb 2020</strong><br>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Kai S. K. Engelbart
 | 
			
		||||
 * @since Envoy v0.3-alpha
 | 
			
		||||
 */
 | 
			
		||||
public class MessageCache implements Consumer<Message> {
 | 
			
		||||
 | 
			
		||||
	private final Queue<Message>	messages	= new LinkedList<>();
 | 
			
		||||
	private Consumer<Message>		processor;
 | 
			
		||||
 | 
			
		||||
	private static final Logger logger = EnvoyLog.getLogger(MessageCache.class.getSimpleName());
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Adds a message to the cache.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public void accept(Message message) {
 | 
			
		||||
		logger.info(String.format("Adding message %s to cache", message));
 | 
			
		||||
		messages.add(message);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sets the processor to which messages are relayed.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param processor the processor to set
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void setProcessor(Consumer<Message> processor) { this.processor = processor; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Relays all cached messages to the processor.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void relayMessages() { messages.forEach(processor::accept); }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,37 @@
 | 
			
		||||
package envoy.client.net;
 | 
			
		||||
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import envoy.client.util.EnvoyLog;
 | 
			
		||||
import envoy.event.EventBus;
 | 
			
		||||
import envoy.event.UserStatusChangeEvent;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>UserStatusChangeProcessor.java</strong><br>
 | 
			
		||||
 * Created: <strong>2 Feb 2020</strong><br>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Leon Hofmeister
 | 
			
		||||
 * @since Envoy v0.3-alpha
 | 
			
		||||
 */
 | 
			
		||||
public class UserStatusChangeProcessor implements Consumer<UserStatusChangeEvent> {
 | 
			
		||||
 | 
			
		||||
	private Client client;
 | 
			
		||||
 | 
			
		||||
	private static final Logger logger = EnvoyLog.getLogger(UserStatusChangeProcessor.class.getSimpleName());
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param client the {@link Client} who receives an
 | 
			
		||||
	 *               {@link UserStatusChangeEvent}
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public UserStatusChangeProcessor(Client client) { this.client = client; }
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void accept(UserStatusChangeEvent evt) {
 | 
			
		||||
		logger.info("Received " + evt);
 | 
			
		||||
		client.getContacts().getContacts().stream().filter((user) -> user.getId() == evt.getId()).findFirst().get().setStatus(evt.get());
 | 
			
		||||
		EventBus.getInstance().dispatch(evt);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										103
									
								
								src/main/java/envoy/client/net/WriteProxy.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/main/java/envoy/client/net/WriteProxy.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
			
		||||
package envoy.client.net;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.logging.Level;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import envoy.client.data.LocalDb;
 | 
			
		||||
import envoy.client.util.EnvoyLog;
 | 
			
		||||
import envoy.data.Message;
 | 
			
		||||
import envoy.event.MessageStatusChangeEvent;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Implements methods to send {@link Message}s and
 | 
			
		||||
 * {@link MessageStatusChangeEvent}s to the server or cache them inside a
 | 
			
		||||
 * {@link LocalDb} depending on the online status.<br>
 | 
			
		||||
 * <br>
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>WriteProxy.java</strong><br>
 | 
			
		||||
 * Created: <strong>6 Feb 2020</strong><br>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Kai S. K. Engelbart
 | 
			
		||||
 * @since Envoy v0.3-alpha
 | 
			
		||||
 */
 | 
			
		||||
public class WriteProxy {
 | 
			
		||||
 | 
			
		||||
	private final Client	client;
 | 
			
		||||
	private final LocalDb	localDb;
 | 
			
		||||
 | 
			
		||||
	private static final Logger logger = EnvoyLog.getLogger(WriteProxy.class.getSimpleName());
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Initializes a write proxy using a client and a local database. The
 | 
			
		||||
	 * corresponding cache processors are injected into the caches.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param client  the client used to send messages and message status change
 | 
			
		||||
	 *                events
 | 
			
		||||
	 * @param localDb the local database used to cache messages and message status
 | 
			
		||||
	 *                change events
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public WriteProxy(Client client, LocalDb localDb) {
 | 
			
		||||
		this.client		= client;
 | 
			
		||||
		this.localDb	= localDb;
 | 
			
		||||
 | 
			
		||||
		// Initialize cache processors for messages and message status change events
 | 
			
		||||
		localDb.getMessageCache().setProcessor(msg -> {
 | 
			
		||||
			try {
 | 
			
		||||
				client.sendMessage(msg);
 | 
			
		||||
			} catch (IOException e) {
 | 
			
		||||
				logger.log(Level.SEVERE, "Could not send cached message", e);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		localDb.getStatusCache().setProcessor(evt -> {
 | 
			
		||||
			try {
 | 
			
		||||
				client.sendEvent(evt);
 | 
			
		||||
			} catch (IOException e) {
 | 
			
		||||
				logger.log(Level.SEVERE, "Could not send cached message status change event", e);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sends cached {@link Message}s and {@link MessageStatusChangeEvent}s to the
 | 
			
		||||
	 * server.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void flushCache() {
 | 
			
		||||
		logger.info("Sending cached messages and message status change events...");
 | 
			
		||||
 | 
			
		||||
		// Send messages
 | 
			
		||||
		localDb.getMessageCache().relay();
 | 
			
		||||
 | 
			
		||||
		// Send message status change events
 | 
			
		||||
		localDb.getStatusCache().relay();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Delivers a message to the server if online. Otherwise the message is cached
 | 
			
		||||
	 * inside the local database.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param message the message to send
 | 
			
		||||
	 * @throws IOException if the message could not be sent
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void writeMessage(Message message) throws IOException {
 | 
			
		||||
		if (client.isOnline()) client.sendMessage(message);
 | 
			
		||||
		else localDb.getMessageCache().accept(message);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Delivers a message status change event to the server if online. Otherwise the
 | 
			
		||||
	 * event is cached inside the local database.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param evt the event to send
 | 
			
		||||
	 * @throws IOException if the event could not be sent
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void writeMessageStatusChangeEvent(MessageStatusChangeEvent evt) throws IOException {
 | 
			
		||||
		if (client.isOnline()) client.sendEvent(evt);
 | 
			
		||||
		else localDb.getStatusCache().accept(evt);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -17,6 +17,7 @@ import envoy.client.data.Chat;
 | 
			
		||||
import envoy.client.data.LocalDb;
 | 
			
		||||
import envoy.client.event.*;
 | 
			
		||||
import envoy.client.net.Client;
 | 
			
		||||
import envoy.client.net.WriteProxy;
 | 
			
		||||
import envoy.client.ui.list.ComponentList;
 | 
			
		||||
import envoy.client.ui.list.ComponentListModel;
 | 
			
		||||
import envoy.client.ui.settings.SettingsScreen;
 | 
			
		||||
@@ -41,8 +42,9 @@ import envoy.event.ContactOperationEvent.Operation;
 | 
			
		||||
public class ChatWindow extends JFrame {
 | 
			
		||||
 | 
			
		||||
	// User specific objects
 | 
			
		||||
	private Client	client;
 | 
			
		||||
	private LocalDb	localDb;
 | 
			
		||||
	private Client		client;
 | 
			
		||||
	private WriteProxy	writeProxy;
 | 
			
		||||
	private LocalDb		localDb;
 | 
			
		||||
 | 
			
		||||
	// GUI components
 | 
			
		||||
	private JPanel					contentPane				= new JPanel();
 | 
			
		||||
@@ -359,6 +361,9 @@ public class ChatWindow extends JFrame {
 | 
			
		||||
		// Listen to theme changes
 | 
			
		||||
		EventBus.getInstance().register(ThemeChangeEvent.class, (evt) -> applyTheme((Theme) evt.get()));
 | 
			
		||||
 | 
			
		||||
		// Listen to user status changes
 | 
			
		||||
		EventBus.getInstance().register(UserStatusChangeEvent.class, (evt) -> { userList.revalidate(); userList.repaint(); });
 | 
			
		||||
 | 
			
		||||
		// Listen to received messages
 | 
			
		||||
		EventBus.getInstance().register(MessageCreationEvent.class, (evt) -> {
 | 
			
		||||
			Message	message	= ((MessageCreationEvent) evt).get();
 | 
			
		||||
@@ -498,8 +503,7 @@ public class ChatWindow extends JFrame {
 | 
			
		||||
				.build();
 | 
			
		||||
 | 
			
		||||
			// Send message
 | 
			
		||||
			// TODO: Store offline messages
 | 
			
		||||
			client.sendMessage(message);
 | 
			
		||||
			writeProxy.writeMessage(message);
 | 
			
		||||
 | 
			
		||||
			// Add message to PersistentLocalDb and update UI
 | 
			
		||||
			currentChat.appendMessage(message);
 | 
			
		||||
@@ -544,7 +548,7 @@ public class ChatWindow extends JFrame {
 | 
			
		||||
 | 
			
		||||
	private void readCurrentChat() {
 | 
			
		||||
		try {
 | 
			
		||||
			currentChat.read(client);
 | 
			
		||||
			currentChat.read(writeProxy);
 | 
			
		||||
			messageList.synchronizeModel();
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
@@ -571,24 +575,23 @@ public class ChatWindow extends JFrame {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sets the {@link Client} used by this {@link ChatWindow}.
 | 
			
		||||
	 * Initializes the components responsible server communication and
 | 
			
		||||
	 * persistence.<br>
 | 
			
		||||
	 * <br>
 | 
			
		||||
	 * This will trigger the display of the contact list.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param client the {@link Client} used to send and receive messages
 | 
			
		||||
	 * @since Envoy v0.2-alpha
 | 
			
		||||
	 * @param client     the client used to send and receive messages
 | 
			
		||||
	 * @param localDb    the local database used to manage stored messages
 | 
			
		||||
	 *                   and users
 | 
			
		||||
	 * @param writeProxy the write proxy used to send messages and status change
 | 
			
		||||
	 *                   events to the server or cache them inside the local
 | 
			
		||||
	 *                   database
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void setClient(Client client) { this.client = client; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sets the {@link LocalDb} used by this {@link ChatWindow}. After
 | 
			
		||||
	 * invoking this
 | 
			
		||||
	 * method, users and chats will be loaded from the database into the GUI.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param localDb the {@link LocalDb} used to manage stored messages
 | 
			
		||||
	 *                and users
 | 
			
		||||
	 * @since Envoy v0.2-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void setLocalDB(LocalDb localDb) {
 | 
			
		||||
		this.localDb = localDb;
 | 
			
		||||
	public void initContent(Client client, LocalDb localDb, WriteProxy writeProxy) {
 | 
			
		||||
		this.client		= client;
 | 
			
		||||
		this.localDb	= localDb;
 | 
			
		||||
		this.writeProxy	= writeProxy;
 | 
			
		||||
		loadUsersAndChats();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,15 +11,13 @@ import javax.swing.JFrame;
 | 
			
		||||
import javax.swing.JOptionPane;
 | 
			
		||||
import javax.swing.SwingUtilities;
 | 
			
		||||
 | 
			
		||||
import envoy.client.Config;
 | 
			
		||||
import envoy.client.Settings;
 | 
			
		||||
import envoy.client.data.LocalDb;
 | 
			
		||||
import envoy.client.data.PersistentLocalDb;
 | 
			
		||||
import envoy.client.data.TransientLocalDb;
 | 
			
		||||
import envoy.client.data.*;
 | 
			
		||||
import envoy.client.net.Client;
 | 
			
		||||
import envoy.client.net.MessageCache;
 | 
			
		||||
import envoy.client.net.WriteProxy;
 | 
			
		||||
import envoy.client.util.EnvoyLog;
 | 
			
		||||
import envoy.data.LoginCredentials;
 | 
			
		||||
import envoy.data.Message;
 | 
			
		||||
import envoy.data.User;
 | 
			
		||||
import envoy.exception.EnvoyException;
 | 
			
		||||
 | 
			
		||||
@@ -64,7 +62,7 @@ public class Startup {
 | 
			
		||||
			// Override configuration values with command line arguments
 | 
			
		||||
			if (args.length > 0) config.load(args);
 | 
			
		||||
 | 
			
		||||
			// Check if all configuration values have been initialized
 | 
			
		||||
			// Check if all mandatory configuration values have been initialized
 | 
			
		||||
			if (!config.isInitialized()) throw new EnvoyException("Server or port are not defined");
 | 
			
		||||
		} catch (Exception e) {
 | 
			
		||||
			JOptionPane
 | 
			
		||||
@@ -77,8 +75,8 @@ public class Startup {
 | 
			
		||||
		EnvoyLog.setFileLevelBarrier(config.getFileLevelBarrier());
 | 
			
		||||
		EnvoyLog.setConsoleLevelBarrier(config.getConsoleLevelBarrier());
 | 
			
		||||
 | 
			
		||||
		// Ask the user for their user name and password
 | 
			
		||||
		LoginCredentials credentials = new LoginDialog().getCredentials();
 | 
			
		||||
		// Acquire login credentials
 | 
			
		||||
		LoginCredentials credentials = config.hasLoginCredentials() ? config.getLoginCredentials() : new LoginDialog().getCredentials();
 | 
			
		||||
 | 
			
		||||
		if (credentials == null) {
 | 
			
		||||
			logger.info("The login process has been aborted by the user. Exiting...");
 | 
			
		||||
@@ -108,11 +106,11 @@ public class Startup {
 | 
			
		||||
		// Acquire the client user (with ID) either from the server or from the local
 | 
			
		||||
		// database, which triggers offline mode
 | 
			
		||||
		Client			client	= new Client();
 | 
			
		||||
		MessageCache	cache	= null;
 | 
			
		||||
		Cache<Message>	cache	= new Cache<>();
 | 
			
		||||
		try {
 | 
			
		||||
			// Try entering online mode first
 | 
			
		||||
			localDb.loadIdGenerator();
 | 
			
		||||
			cache = client.onlineInit(credentials, localDb);
 | 
			
		||||
			client.onlineInit(credentials, localDb, cache);
 | 
			
		||||
		} catch (Exception e1) {
 | 
			
		||||
			logger.warning("Could not connect to server. Trying offline mode...");
 | 
			
		||||
			e1.printStackTrace();
 | 
			
		||||
@@ -139,7 +137,7 @@ public class Startup {
 | 
			
		||||
		// Initialize chats in local database
 | 
			
		||||
		try {
 | 
			
		||||
			localDb.initializeUserStorage();
 | 
			
		||||
			localDb.loadChats();
 | 
			
		||||
			localDb.loadUserData();
 | 
			
		||||
		} catch (FileNotFoundException e) {
 | 
			
		||||
			// The local database file has not yet been created, probably first login
 | 
			
		||||
		} catch (Exception e) {
 | 
			
		||||
@@ -150,14 +148,22 @@ public class Startup {
 | 
			
		||||
					JOptionPane.WARNING_MESSAGE);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Save all users to the local database
 | 
			
		||||
		if (client.isOnline()) localDb.setUsers(client.getUsers());
 | 
			
		||||
		// Initialize write proxy
 | 
			
		||||
		final WriteProxy writeProxy = client.createWriteProxy(localDb);
 | 
			
		||||
 | 
			
		||||
		// Save all users to the local database and flush cache
 | 
			
		||||
		if (client.isOnline()) {
 | 
			
		||||
			localDb.setUsers(client.getUsers());
 | 
			
		||||
			writeProxy.flushCache();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Display ChatWindow and StatusTrayIcon
 | 
			
		||||
		EventQueue.invokeLater(() -> {
 | 
			
		||||
			try {
 | 
			
		||||
				chatWindow.setClient(client);
 | 
			
		||||
				chatWindow.setLocalDB(localDb);
 | 
			
		||||
				chatWindow.initContent(client, localDb, writeProxy);
 | 
			
		||||
 | 
			
		||||
				// Relay unread messages from cache
 | 
			
		||||
				if (cache != null) cache.relay();
 | 
			
		||||
 | 
			
		||||
				try {
 | 
			
		||||
					new StatusTrayIcon(chatWindow).show();
 | 
			
		||||
@@ -177,9 +183,6 @@ public class Startup {
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		// Relay unread messages from cache
 | 
			
		||||
		if (cache != null) cache.relayMessages();
 | 
			
		||||
 | 
			
		||||
		// Save Settings and PersistentLocalDb on shutdown
 | 
			
		||||
		Runtime.getRuntime().addShutdownHook(new Thread(() -> {
 | 
			
		||||
			try {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.logging.*;
 | 
			
		||||
 | 
			
		||||
import envoy.client.Config;
 | 
			
		||||
import envoy.client.data.Config;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user