Made local database persistence optional
* Split LocalDB into abstract class LocalDb and PersistentLocalDb and TransientLocalDb * Moved LocalDb to database package * Added ignoreLocalDb option to Config
This commit is contained in:
		| @@ -9,6 +9,7 @@ import java.util.logging.Logger; | |||||||
|  |  | ||||||
| import javax.naming.TimeLimitExceededException; | import javax.naming.TimeLimitExceededException; | ||||||
|  |  | ||||||
|  | import envoy.client.database.LocalDb; | ||||||
| import envoy.client.net.ReceivedMessageProcessor; | import envoy.client.net.ReceivedMessageProcessor; | ||||||
| import envoy.client.net.Receiver; | import envoy.client.net.Receiver; | ||||||
| import envoy.client.util.EnvoyLog; | import envoy.client.util.EnvoyLog; | ||||||
| @@ -48,13 +49,13 @@ public class Client implements Closeable { | |||||||
| 	 * an exception is thrown. | 	 * an exception is thrown. | ||||||
| 	 * | 	 * | ||||||
| 	 * @param credentials       the login credentials of the user | 	 * @param credentials       the login credentials of the user | ||||||
| 	 * @param localDB     the local database used to persist the current | 	 * @param localDb the local database used to persist the current | ||||||
| 	 *                          {@link IdGenerator} | 	 *                          {@link IdGenerator} | ||||||
| 	 * @throws Exception if the online mode could not be entered or the request | 	 * @throws Exception if the online mode could not be entered or the request | ||||||
| 	 *                   failed for some other reason | 	 *                   failed for some other reason | ||||||
| 	 * @since Envoy v0.2-alpha | 	 * @since Envoy v0.2-alpha | ||||||
| 	 */ | 	 */ | ||||||
| 	public void onlineInit(LoginCredentials credentials, LocalDB localDB) throws Exception { | 	public void onlineInit(LoginCredentials credentials, LocalDb localDb) throws Exception { | ||||||
| 		// Establish TCP connection | 		// Establish TCP connection | ||||||
| 		logger.info(String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort())); | 		logger.info(String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort())); | ||||||
| 		socket = new Socket(config.getServer(), config.getPort()); | 		socket = new Socket(config.getServer(), config.getPort()); | ||||||
| @@ -92,10 +93,10 @@ public class Client implements Closeable { | |||||||
| 		// TODO: Status handling | 		// TODO: Status handling | ||||||
|  |  | ||||||
| 		// Process message ID generation | 		// Process message ID generation | ||||||
| 		receiver.registerProcessor(IdGenerator.class, localDB::setIdGenerator); | 		receiver.registerProcessor(IdGenerator.class, localDb::setIdGenerator); | ||||||
|  |  | ||||||
| 		// Request a generator if none is present | 		// Request a generator if none is present | ||||||
| 		if (!localDB.hasIdGenerator() || !localDB.getIdGenerator().hasNext()) requestIdGenerator(); | 		if (!localDb.hasIdGenerator() || !localDb.getIdGenerator().hasNext()) requestIdGenerator(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
|   | |||||||
| @@ -1,9 +1,7 @@ | |||||||
| package envoy.client; | package envoy.client; | ||||||
|  |  | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.util.HashMap; | import java.util.*; | ||||||
| import java.util.Map; |  | ||||||
| import java.util.Properties; |  | ||||||
| import java.util.logging.Level; | import java.util.logging.Level; | ||||||
|  |  | ||||||
| import envoy.exception.EnvoyException; | import envoy.exception.EnvoyException; | ||||||
| @@ -31,6 +29,7 @@ public class Config { | |||||||
| 		items.put("server", new ConfigItem<>("server", "s", (input) -> input, null)); | 		items.put("server", new ConfigItem<>("server", "s", (input) -> input, null)); | ||||||
| 		items.put("port", new ConfigItem<>("port", "p", (input) -> Integer.parseInt(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("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", | 		items.put("homeDirectory", | ||||||
| 				new ConfigItem<>("homeDirectory", "h", (input) -> new File(input), new File(System.getProperty("user.home"), ".envoy"))); | 				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("fileLevelBarrier", new ConfigItem<>("fileLevelBarrier", "fb", (input) -> Level.parse(input), Level.CONFIG)); | ||||||
| @@ -99,7 +98,7 @@ public class Config { | |||||||
| 	 * @return {@code true} if server, port and localDB directory are known. | 	 * @return {@code true} if server, port and localDB directory are known. | ||||||
| 	 * @since Envoy v0.1-alpha | 	 * @since Envoy v0.1-alpha | ||||||
| 	 */ | 	 */ | ||||||
| 	public boolean isInitialized() { return items.values().stream().noneMatch(item -> item.get() == null); } | 	public boolean isInitialized() { return items.values().stream().map(ConfigItem::get).noneMatch(Objects::isNull); } | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * @return the host name of the Envoy server | 	 * @return the host name of the Envoy server | ||||||
| @@ -119,6 +118,12 @@ public class Config { | |||||||
| 	 */ | 	 */ | ||||||
| 	public File getLocalDB() { return (File) items.get("localDB").get(); } | 	public File getLocalDB() { return (File) items.get("localDB").get(); } | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * @return {@code true} if the local database is to be ignored | ||||||
|  | 	 * @since Envoy v0.3-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public Boolean isIgnoreLocalDB() { return (Boolean) items.get("ignoreLocalDB").get(); } | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * @return the directory in which all local files are saves | 	 * @return the directory in which all local files are saves | ||||||
| 	 * @since Envoy v0.2-alpha | 	 * @since Envoy v0.2-alpha | ||||||
|   | |||||||
| @@ -1,163 +0,0 @@ | |||||||
| package envoy.client; |  | ||||||
|  |  | ||||||
| import java.io.File; |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.util.*; |  | ||||||
|  |  | ||||||
| import envoy.data.IdGenerator; |  | ||||||
| import envoy.data.User; |  | ||||||
| import envoy.util.SerializationUtils; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Stored information about the current {@link User} and their {@link Chat}s. |  | ||||||
|  * For message ID generation a {@link IdGenerator} is stored as well. |  | ||||||
|  * These object are persisted inside a folder of the local file system.<br> |  | ||||||
|  * <br> |  | ||||||
|  * Project: <strong>envoy-client</strong><br> |  | ||||||
|  * File: <strong>LocalDB.java</strong><br> |  | ||||||
|  * Created: <strong>27.10.2019</strong><br> |  | ||||||
|  * |  | ||||||
|  * @author Kai S. K. Engelbart |  | ||||||
|  * @author Maximilian Käfer |  | ||||||
|  * @since Envoy v0.1-alpha |  | ||||||
|  */ |  | ||||||
| public class LocalDB { |  | ||||||
|  |  | ||||||
| 	private File				localDBDir, localDBFile, usersFile, idGeneratorFile; |  | ||||||
| 	private User				user; |  | ||||||
| 	private Map<String, User>	users	= new HashMap<>(); |  | ||||||
| 	private List<Chat>			chats	= new ArrayList<>(); |  | ||||||
| 	private IdGenerator			idGenerator; |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Constructs an empty local database. To serialize any chats to the file |  | ||||||
| 	 * system, call {@link LocalDB#initializeDBFile()}. |  | ||||||
| 	 * |  | ||||||
| 	 * @param localDBDir the directory in which to store users and chats |  | ||||||
| 	 * @throws IOException if the LocalDB could not be initialized |  | ||||||
| 	 * @since Envoy v0.1-alpha |  | ||||||
| 	 */ |  | ||||||
| 	public LocalDB(File localDBDir) throws IOException { |  | ||||||
| 		this.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"); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Creates a database file for a user-specific list of chats. |  | ||||||
| 	 * |  | ||||||
| 	 * @throws NullPointerException if the client user is not yet specified |  | ||||||
| 	 * @since Envoy v0.1-alpha |  | ||||||
| 	 */ |  | ||||||
| 	public void initializeDBFile() { |  | ||||||
| 		if (user == null) throw new NullPointerException("Client user is null"); |  | ||||||
| 		localDBFile = new File(localDBDir, user.getId() + ".db"); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Stores all users to the local database. If the client user is specified, the |  | ||||||
| 	 * chats related to this user are stored as well. The message id generator will |  | ||||||
| 	 * also be saved if present. |  | ||||||
| 	 * |  | ||||||
| 	 * @throws IOException if something went wrong during saving |  | ||||||
| 	 * @since Envoy v0.1-alpha |  | ||||||
| 	 */ |  | ||||||
| 	public void save() throws IOException { |  | ||||||
| 		// Save users |  | ||||||
| 		SerializationUtils.write(usersFile, users); |  | ||||||
|  |  | ||||||
| 		// Save chats |  | ||||||
| 		if (user != null) SerializationUtils.write(localDBFile, chats); |  | ||||||
|  |  | ||||||
| 		// Save id generator |  | ||||||
| 		if (hasIdGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Loads all users that are stored in the local database. |  | ||||||
| 	 * |  | ||||||
| 	 * @throws IOException            if the loading process failed |  | ||||||
| 	 * @throws ClassNotFoundException if the loading process failed |  | ||||||
| 	 * @since Envoy v0.2-alpha |  | ||||||
| 	 */ |  | ||||||
| 	public void loadUsers() throws ClassNotFoundException, IOException { users = SerializationUtils.read(usersFile, HashMap.class); } |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Loads all chats saved by Envoy for the client user. |  | ||||||
| 	 * |  | ||||||
| 	 * @throws IOException            if the loading process failed |  | ||||||
| 	 * @throws ClassNotFoundException if the loading process failed |  | ||||||
| 	 * @since Envoy v0.1-alpha |  | ||||||
| 	 */ |  | ||||||
| 	public void loadChats() throws ClassNotFoundException, IOException { chats = SerializationUtils.read(localDBFile, ArrayList.class); } |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Loads the message ID generator that is stored in the local database. If the |  | ||||||
| 	 * file is not found, the exception is ignored. |  | ||||||
| 	 * |  | ||||||
| 	 * @since Envoy v0.3-alpha |  | ||||||
| 	 */ |  | ||||||
| 	public void loadIdGenerator() { |  | ||||||
| 		try { |  | ||||||
| 			idGenerator = SerializationUtils.read(idGeneratorFile, IdGenerator.class); |  | ||||||
| 		} catch (ClassNotFoundException | IOException e) {} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * @return a {@code Map<String, User>} of all users stored locally with their |  | ||||||
| 	 *         user names as keys |  | ||||||
| 	 * @since Envoy v0.2-alpha |  | ||||||
| 	 */ |  | ||||||
| 	public Map<String, User> getUsers() { return users; } |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * @param users the users to set |  | ||||||
| 	 */ |  | ||||||
| 	public void setUsers(Map<String, User> users) { this.users = users; } |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * @return all saved {@link Chat} objects that list the client user as the |  | ||||||
| 	 *         sender |  | ||||||
| 	 * @since Envoy v0.1-alpha |  | ||||||
| 	 **/ |  | ||||||
| 	public List<Chat> getChats() { return chats; } |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * @param chats the chats to set |  | ||||||
| 	 */ |  | ||||||
| 	public void setChats(List<Chat> chats) { this.chats = chats; } |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * @return the {@link User} who initialized the local database |  | ||||||
| 	 * @since Envoy v0.2-alpha |  | ||||||
| 	 */ |  | ||||||
| 	public User getUser() { return user; } |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * @param user the user to set |  | ||||||
| 	 * @since Envoy v0.2-alpha |  | ||||||
| 	 */ |  | ||||||
| 	public void setUser(User user) { this.user = user; } |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * @return the message ID generator |  | ||||||
| 	 * @since Envoy v0.3-alpha |  | ||||||
| 	 */ |  | ||||||
| 	public IdGenerator getIdGenerator() { return idGenerator; } |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * @param idGenerator the message ID generator to set |  | ||||||
| 	 * @since Envoy v0.3-alpha |  | ||||||
| 	 */ |  | ||||||
| 	public void setIdGenerator(IdGenerator idGenerator) { this.idGenerator = idGenerator; } |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * @return {@code true} if an {@link IdGenerator} is present |  | ||||||
| 	 * @since Envoy v0.3-alpha |  | ||||||
| 	 */ |  | ||||||
| 	public boolean hasIdGenerator() { return idGenerator != null; } |  | ||||||
| } |  | ||||||
							
								
								
									
										119
									
								
								src/main/java/envoy/client/database/LocalDb.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/main/java/envoy/client/database/LocalDb.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | |||||||
|  | package envoy.client.database; | ||||||
|  |  | ||||||
|  | import java.util.*; | ||||||
|  |  | ||||||
|  | import envoy.client.Chat; | ||||||
|  | import envoy.data.IdGenerator; | ||||||
|  | import envoy.data.User; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Stores information about the current {@link User} and their {@link Chat}s. | ||||||
|  |  * For message ID generation a {@link IdGenerator} is stored as well.<br> | ||||||
|  |  * <br> | ||||||
|  |  * Project: <strong>envoy-client</strong><br> | ||||||
|  |  * File: <strong>LocalDb.java</strong><br> | ||||||
|  |  * Created: <strong>3 Feb 2020</strong><br> | ||||||
|  |  * | ||||||
|  |  * @author Kai S. K. Engelbart | ||||||
|  |  * @since Envoy v0.3-alpha | ||||||
|  |  */ | ||||||
|  | public abstract class LocalDb { | ||||||
|  |  | ||||||
|  | 	protected User				user; | ||||||
|  | 	protected Map<String, User>	users	= new HashMap<>(); | ||||||
|  | 	protected List<Chat>		chats	= new ArrayList<>(); | ||||||
|  | 	protected IdGenerator		idGenerator; | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Initializes a storage space for a user-specific list of chats. | ||||||
|  | 	 * | ||||||
|  | 	 * @since Envoy v0.3-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public void initializeUserStorage() {} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Stores all users. If the client user is specified, their chats will be stored | ||||||
|  | 	 * as well. The message id generator will also be saved if present. | ||||||
|  | 	 * | ||||||
|  | 	 * @throws Exception if the saving process failed | ||||||
|  | 	 * @since Envoy v0.3-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public void save() throws Exception {} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Loads all user data. | ||||||
|  | 	 * | ||||||
|  | 	 * @throws Exception if the loading process failed | ||||||
|  | 	 * @since Envoy v0.3-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public void loadUsers() throws Exception {} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Loads all chat data of the client user. | ||||||
|  | 	 * | ||||||
|  | 	 * @throws Exception if the loading process failed | ||||||
|  | 	 * @since Envoy v0.3-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public void loadChats() throws Exception {} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Loads the ID generator. Any exception thrown during this process is ignored. | ||||||
|  | 	 * | ||||||
|  | 	 * @since Envoy v0.3-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public void loadIdGenerator() {} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * @return a {@code Map<String, User>} of all users stored locally with their | ||||||
|  | 	 *         user names as keys | ||||||
|  | 	 * @since Envoy v0.2-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public Map<String, User> getUsers() { return users; } | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * @param users the users to set | ||||||
|  | 	 */ | ||||||
|  | 	public void setUsers(Map<String, User> users) { this.users = users; } | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * @return all saved {@link Chat} objects that list the client user as the | ||||||
|  | 	 *         sender | ||||||
|  | 	 * @since Envoy v0.1-alpha | ||||||
|  | 	 **/ | ||||||
|  | 	public List<Chat> getChats() { return chats; } | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * @param chats the chats to set | ||||||
|  | 	 */ | ||||||
|  | 	public void setChats(List<Chat> chats) { this.chats = chats; } | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * @return the {@link User} who initialized the local database | ||||||
|  | 	 * @since Envoy v0.2-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public User getUser() { return user; } | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * @param user the user to set | ||||||
|  | 	 * @since Envoy v0.2-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public void setUser(User user) { this.user = user; } | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * @return the message ID generator | ||||||
|  | 	 * @since Envoy v0.3-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public IdGenerator getIdGenerator() { return idGenerator; } | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * @param idGenerator the message ID generator to set | ||||||
|  | 	 * @since Envoy v0.3-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public void setIdGenerator(IdGenerator idGenerator) { this.idGenerator = idGenerator; } | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * @return {@code true} if an {@link IdGenerator} is present | ||||||
|  | 	 * @since Envoy v0.3-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public boolean hasIdGenerator() { return idGenerator != null; } | ||||||
|  | } | ||||||
							
								
								
									
										93
									
								
								src/main/java/envoy/client/database/PersistentLocalDb.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/main/java/envoy/client/database/PersistentLocalDb.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | |||||||
|  | package envoy.client.database; | ||||||
|  |  | ||||||
|  | import java.io.File; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.HashMap; | ||||||
|  |  | ||||||
|  | import envoy.client.ConfigItem; | ||||||
|  | import envoy.data.IdGenerator; | ||||||
|  | import envoy.util.SerializationUtils; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Implements a {@link LocalDb} in a way that stores all information inside a | ||||||
|  |  * folder on the local file system.<br> | ||||||
|  |  * <br> | ||||||
|  |  * Project: <strong>envoy-client</strong><br> | ||||||
|  |  * File: <strong>PersistentLocalDb.java</strong><br> | ||||||
|  |  * Created: <strong>27.10.2019</strong><br> | ||||||
|  |  * | ||||||
|  |  * @author Kai S. K. Engelbart | ||||||
|  |  * @author Maximilian Käfer | ||||||
|  |  * @since Envoy v0.1-alpha | ||||||
|  |  */ | ||||||
|  | public class PersistentLocalDb extends LocalDb { | ||||||
|  |  | ||||||
|  | 	private File localDBDir, localDBFile, usersFile, idGeneratorFile; | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Initializes an empty local database without a directory. All changes made to | ||||||
|  | 	 * this instance cannot be saved to the file system.<br> | ||||||
|  | 	 * <br> | ||||||
|  | 	 * This constructor shall be used in conjunction with the {@code ignoreLocalDB} | ||||||
|  | 	 * {@link ConfigItem}. | ||||||
|  | 	 * | ||||||
|  | 	 * @since Envoy v0.3-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public PersistentLocalDb() {} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 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 | ||||||
|  | 	 * @throws IOException if the PersistentLocalDb could not be initialized | ||||||
|  | 	 * @since Envoy v0.1-alpha | ||||||
|  | 	 */ | ||||||
|  | 	public PersistentLocalDb(File localDBDir) throws IOException { | ||||||
|  | 		this.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"); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * Creates a database file for a user-specific list of chats. | ||||||
|  | 	 * | ||||||
|  | 	 * @throws NullPointerException if the client user is not yet specified | ||||||
|  | 	 * @since Envoy v0.1-alpha | ||||||
|  | 	 */ | ||||||
|  | 	@Override | ||||||
|  | 	public void initializeUserStorage() { | ||||||
|  | 		if (user == null) throw new NullPointerException("Client user is null"); | ||||||
|  | 		localDBFile = new File(localDBDir, user.getId() + ".db"); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public void save() throws IOException { | ||||||
|  | 		// Save users | ||||||
|  | 		SerializationUtils.write(usersFile, users); | ||||||
|  |  | ||||||
|  | 		// Save chats | ||||||
|  | 		if (user != null) SerializationUtils.write(localDBFile, chats); | ||||||
|  |  | ||||||
|  | 		// Save id generator | ||||||
|  | 		if (hasIdGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	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); } | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public void loadIdGenerator() { | ||||||
|  | 		try { | ||||||
|  | 			idGenerator = SerializationUtils.read(idGeneratorFile, IdGenerator.class); | ||||||
|  | 		} catch (ClassNotFoundException | IOException e) {} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								src/main/java/envoy/client/database/TransientLocalDb.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/main/java/envoy/client/database/TransientLocalDb.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | package envoy.client.database; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Implements a {@link LocalDb} in a way that does not persist any information | ||||||
|  |  * after application shutdown.<br> | ||||||
|  |  * <br> | ||||||
|  |  * Project: <strong>envoy-client</strong><br> | ||||||
|  |  * File: <strong>TransientLocalDb.java</strong><br> | ||||||
|  |  * Created: <strong>3 Feb 2020</strong><br> | ||||||
|  |  * | ||||||
|  |  * @author Kai S. K. Engelbart | ||||||
|  |  * @since Envoy v0.3-alpha | ||||||
|  |  */ | ||||||
|  | public class TransientLocalDb extends LocalDb { | ||||||
|  | } | ||||||
| @@ -9,7 +9,10 @@ import java.util.logging.Logger; | |||||||
| import javax.swing.*; | import javax.swing.*; | ||||||
| import javax.swing.border.EmptyBorder; | import javax.swing.border.EmptyBorder; | ||||||
|  |  | ||||||
| import envoy.client.*; | import envoy.client.Chat; | ||||||
|  | import envoy.client.Client; | ||||||
|  | import envoy.client.Settings; | ||||||
|  | import envoy.client.database.LocalDb; | ||||||
| import envoy.client.event.MessageCreationEvent; | import envoy.client.event.MessageCreationEvent; | ||||||
| import envoy.client.event.ThemeChangeEvent; | import envoy.client.event.ThemeChangeEvent; | ||||||
| import envoy.client.ui.list.ComponentList; | import envoy.client.ui.list.ComponentList; | ||||||
| @@ -34,7 +37,7 @@ public class ChatWindow extends JFrame { | |||||||
|  |  | ||||||
| 	// User specific objects | 	// User specific objects | ||||||
| 	private Client	client; | 	private Client	client; | ||||||
| 	private LocalDB	localDB; | 	private LocalDb	localDb; | ||||||
|  |  | ||||||
| 	// GUI components | 	// GUI components | ||||||
| 	private JPanel					contentPane				= new JPanel(); | 	private JPanel					contentPane				= new JPanel(); | ||||||
| @@ -161,13 +164,13 @@ public class ChatWindow extends JFrame { | |||||||
| 		userList.setCellRenderer(new UserListRenderer()); | 		userList.setCellRenderer(new UserListRenderer()); | ||||||
| 		userList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); | 		userList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); | ||||||
| 		userList.addListSelectionListener((listSelectionEvent) -> { | 		userList.addListSelectionListener((listSelectionEvent) -> { | ||||||
| 			if (client != null && localDB != null && !listSelectionEvent.getValueIsAdjusting()) { | 			if (client != null && localDb != null && !listSelectionEvent.getValueIsAdjusting()) { | ||||||
| 				@SuppressWarnings("unchecked") | 				@SuppressWarnings("unchecked") | ||||||
| 				final JList<User>	selectedUserList	= (JList<User>) listSelectionEvent.getSource(); | 				final JList<User>	selectedUserList	= (JList<User>) listSelectionEvent.getSource(); | ||||||
| 				final User			user				= selectedUserList.getSelectedValue(); | 				final User			user				= selectedUserList.getSelectedValue(); | ||||||
|  |  | ||||||
| 				// Select current chat | 				// Select current chat | ||||||
| 				currentChat = localDB.getChats().stream().filter(chat -> chat.getRecipient().getId() == user.getId()).findFirst().get(); | 				currentChat = localDb.getChats().stream().filter(chat -> chat.getRecipient().getId() == user.getId()).findFirst().get(); | ||||||
|  |  | ||||||
| 				// Read current Chat | 				// Read current Chat | ||||||
| 				currentChat.read(); | 				currentChat.read(); | ||||||
| @@ -206,7 +209,7 @@ public class ChatWindow extends JFrame { | |||||||
| 		// Listen to received messages | 		// Listen to received messages | ||||||
| 		EventBus.getInstance().register(MessageCreationEvent.class, (evt) -> { | 		EventBus.getInstance().register(MessageCreationEvent.class, (evt) -> { | ||||||
| 			Message message = ((MessageCreationEvent) evt).get(); | 			Message message = ((MessageCreationEvent) evt).get(); | ||||||
| 			localDB.getChats().stream().filter(c -> c.getRecipient().getId() == message.getSenderId()).findFirst().get().appendMessage(message); | 			localDb.getChats().stream().filter(c -> c.getRecipient().getId() == message.getSenderId()).findFirst().get().appendMessage(message); | ||||||
| 			revalidate(); | 			revalidate(); | ||||||
| 			repaint(); | 			repaint(); | ||||||
| 		}); | 		}); | ||||||
| @@ -262,7 +265,7 @@ public class ChatWindow extends JFrame { | |||||||
| 		if (!messageEnterTextArea.getText().isEmpty()) try { | 		if (!messageEnterTextArea.getText().isEmpty()) try { | ||||||
|  |  | ||||||
| 			// Create message | 			// Create message | ||||||
| 			final Message message = new MessageBuilder(localDB.getUser().getId(), currentChat.getRecipient().getId(), localDB.getIdGenerator()) | 			final Message message = new MessageBuilder(localDb.getUser().getId(), currentChat.getRecipient().getId(), localDb.getIdGenerator()) | ||||||
| 				.setText(messageEnterTextArea.getText()) | 				.setText(messageEnterTextArea.getText()) | ||||||
| 				.build(); | 				.build(); | ||||||
|  |  | ||||||
| @@ -270,7 +273,7 @@ public class ChatWindow extends JFrame { | |||||||
| 			// TODO: Store offline messages | 			// TODO: Store offline messages | ||||||
| 			client.sendMessage(message); | 			client.sendMessage(message); | ||||||
|  |  | ||||||
| 			// Add message to LocalDB and update UI | 			// Add message to PersistentLocalDb and update UI | ||||||
| 			currentChat.appendMessage(message); | 			currentChat.appendMessage(message); | ||||||
| 			// messageList.setModel(currentChat.getModel()); | 			// messageList.setModel(currentChat.getModel()); | ||||||
|  |  | ||||||
| @@ -281,8 +284,8 @@ public class ChatWindow extends JFrame { | |||||||
| 			revalidate(); | 			revalidate(); | ||||||
| 			repaint(); | 			repaint(); | ||||||
|  |  | ||||||
| 			// Request a new id generator if all ids were used | 			// Request a new id generator if all IDs were used | ||||||
| 			if (!localDB.getIdGenerator().hasNext()) client.requestIdGenerator(); | 			if (!localDb.getIdGenerator().hasNext()) client.requestIdGenerator(); | ||||||
|  |  | ||||||
| 		} catch (Exception e) { | 		} catch (Exception e) { | ||||||
| 			JOptionPane.showMessageDialog(this, | 			JOptionPane.showMessageDialog(this, | ||||||
| @@ -302,12 +305,12 @@ public class ChatWindow extends JFrame { | |||||||
| 	private void loadUsersAndChats() { | 	private void loadUsersAndChats() { | ||||||
| 		new Thread(() -> { | 		new Thread(() -> { | ||||||
| 			DefaultListModel<User> userListModel = new DefaultListModel<>(); | 			DefaultListModel<User> userListModel = new DefaultListModel<>(); | ||||||
| 			localDB.getUsers().values().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 | ||||||
| 				if (localDB.getChats().stream().filter(c -> c.getRecipient().getId() == user.getId()).count() == 0) | 				if (localDb.getChats().stream().filter(c -> c.getRecipient().getId() == user.getId()).count() == 0) | ||||||
| 					localDB.getChats().add(new Chat(user)); | 					localDb.getChats().add(new Chat(user)); | ||||||
| 			}); | 			}); | ||||||
| 			SwingUtilities.invokeLater(() -> userList.setModel(userListModel)); | 			SwingUtilities.invokeLater(() -> userList.setModel(userListModel)); | ||||||
| 		}).start(); | 		}).start(); | ||||||
| @@ -324,14 +327,16 @@ public class ChatWindow extends JFrame { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Sets the {@link LocalDB} used by this {@link ChatWindow}. After invoking this | 	 * 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. | 	 * 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 | 	 * @param localDb the {@link LocalDb} used to manage stored messages | ||||||
|  | 	 *                and users | ||||||
| 	 * @since Envoy v0.2-alpha | 	 * @since Envoy v0.2-alpha | ||||||
| 	 */ | 	 */ | ||||||
| 	public void setLocalDB(LocalDB localDB) { | 	public void setLocalDB(LocalDb localDb) { | ||||||
| 		this.localDB = localDB; | 		this.localDb = localDb; | ||||||
| 		loadUsersAndChats(); | 		loadUsersAndChats(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -11,7 +11,12 @@ import javax.swing.JFrame; | |||||||
| import javax.swing.JOptionPane; | import javax.swing.JOptionPane; | ||||||
| import javax.swing.SwingUtilities; | import javax.swing.SwingUtilities; | ||||||
|  |  | ||||||
| import envoy.client.*; | import envoy.client.Client; | ||||||
|  | import envoy.client.Config; | ||||||
|  | import envoy.client.Settings; | ||||||
|  | import envoy.client.database.LocalDb; | ||||||
|  | import envoy.client.database.PersistentLocalDb; | ||||||
|  | import envoy.client.database.TransientLocalDb; | ||||||
| import envoy.client.util.EnvoyLog; | import envoy.client.util.EnvoyLog; | ||||||
| import envoy.data.LoginCredentials; | import envoy.data.LoginCredentials; | ||||||
| import envoy.data.User; | import envoy.data.User; | ||||||
| @@ -63,8 +68,8 @@ public class Startup { | |||||||
| 		} catch (Exception e) { | 		} catch (Exception e) { | ||||||
| 			JOptionPane | 			JOptionPane | ||||||
| 				.showMessageDialog(null, "Error loading configuration values:\n" + e.toString(), "Configuration error", JOptionPane.ERROR_MESSAGE); | 				.showMessageDialog(null, "Error loading configuration values:\n" + e.toString(), "Configuration error", JOptionPane.ERROR_MESSAGE); | ||||||
| 			System.exit(1); |  | ||||||
| 			e.printStackTrace(); | 			e.printStackTrace(); | ||||||
|  | 			System.exit(1); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Set new logger levels loaded from config | 		// Set new logger levels loaded from config | ||||||
| @@ -80,12 +85,19 @@ public class Startup { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Initialize the local database | 		// Initialize the local database | ||||||
| 		LocalDB localDB; | 		LocalDb localDb; | ||||||
| 		try { | 		if (config.isIgnoreLocalDB()) { | ||||||
| 			localDB = new LocalDB(new File(config.getHomeDirectory(), config.getLocalDB().getPath())); | 			localDb = new TransientLocalDb(); | ||||||
|  | 			JOptionPane.showMessageDialog(null, | ||||||
|  | 					"Ignoring local database.\nMessages will not be saved!", | ||||||
|  | 					"Local database warning", | ||||||
|  | 					JOptionPane.WARNING_MESSAGE); | ||||||
|  | 		} else try { | ||||||
|  | 			localDb = new PersistentLocalDb(new File(config.getHomeDirectory(), config.getLocalDB().getPath())); | ||||||
| 		} catch (IOException e3) { | 		} catch (IOException e3) { | ||||||
| 			logger.log(Level.SEVERE, "Could not initialize local database", e3); | 			logger.log(Level.SEVERE, "Could not initialize local database", e3); | ||||||
| 			JOptionPane.showMessageDialog(null, "Could not initialize local database!\n" + e3.toString()); | 			JOptionPane | ||||||
|  | 				.showMessageDialog(null, "Could not initialize local database!\n" + e3.toString(), "Local database error", JOptionPane.ERROR_MESSAGE); | ||||||
| 			System.exit(1); | 			System.exit(1); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| @@ -97,15 +109,15 @@ public class Startup { | |||||||
| 		Client client = new Client(); | 		Client client = new Client(); | ||||||
| 		try { | 		try { | ||||||
| 			// Try entering online mode first | 			// Try entering online mode first | ||||||
| 			localDB.loadIdGenerator(); | 			localDb.loadIdGenerator(); | ||||||
| 			client.onlineInit(credentials, localDB); | 			client.onlineInit(credentials, localDb); | ||||||
| 		} catch (Exception e1) { | 		} catch (Exception e1) { | ||||||
| 			logger.warning("Could not connect to server. Trying offline mode..."); | 			logger.warning("Could not connect to server. Trying offline mode..."); | ||||||
| 			e1.printStackTrace(); | 			e1.printStackTrace(); | ||||||
| 			try { | 			try { | ||||||
| 				// Try entering offline mode | 				// Try entering offline mode | ||||||
| 				localDB.loadUsers(); | 				localDb.loadUsers(); | ||||||
| 				User clientUser = localDB.getUsers().get(credentials.getName()); | 				User clientUser = localDb.getUsers().get(credentials.getName()); | ||||||
| 				if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown"); | 				if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown"); | ||||||
| 				client.setSender(clientUser); | 				client.setSender(clientUser); | ||||||
| 				JOptionPane.showMessageDialog(null, | 				JOptionPane.showMessageDialog(null, | ||||||
| @@ -120,12 +132,12 @@ public class Startup { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Set client user in local database | 		// Set client user in local database | ||||||
| 		localDB.setUser(client.getSender()); | 		localDb.setUser(client.getSender()); | ||||||
|  |  | ||||||
| 		// Initialize chats in local database | 		// Initialize chats in local database | ||||||
| 		try { | 		try { | ||||||
| 			localDB.initializeDBFile(); | 			localDb.initializeUserStorage(); | ||||||
| 			localDB.loadChats(); | 			localDb.loadChats(); | ||||||
| 		} catch (FileNotFoundException e) { | 		} catch (FileNotFoundException e) { | ||||||
| 			// The local database file has not yet been created, probably first login | 			// The local database file has not yet been created, probably first login | ||||||
| 		} catch (Exception e) { | 		} catch (Exception e) { | ||||||
| @@ -137,12 +149,12 @@ public class Startup { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Save all users to the local database | 		// Save all users to the local database | ||||||
| 		if (client.isOnline()) localDB.setUsers(client.getUsers()); | 		if (client.isOnline()) localDb.setUsers(client.getUsers()); | ||||||
|  |  | ||||||
| 		EventQueue.invokeLater(() -> { | 		EventQueue.invokeLater(() -> { | ||||||
| 			try { | 			try { | ||||||
| 				chatWindow.setClient(client); | 				chatWindow.setClient(client); | ||||||
| 				chatWindow.setLocalDB(localDB); | 				chatWindow.setLocalDB(localDb); | ||||||
|  |  | ||||||
| 				try { | 				try { | ||||||
| 					new StatusTrayIcon(chatWindow).show(); | 					new StatusTrayIcon(chatWindow).show(); | ||||||
| @@ -162,16 +174,16 @@ public class Startup { | |||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
| 		// Save Settings and LocalDB on shutdown | 		// Save Settings and PersistentLocalDb on shutdown | ||||||
| 		Runtime.getRuntime().addShutdownHook(new Thread(() -> { | 		Runtime.getRuntime().addShutdownHook(new Thread(() -> { | ||||||
| 			try { | 			try { | ||||||
| 				logger.info("Closing connection..."); | 				logger.info("Closing connection..."); | ||||||
| 				client.close(); | 				client.close(); | ||||||
|  |  | ||||||
| 				logger.info("Saving local database and settings..."); | 				logger.info("Saving local database and settings..."); | ||||||
| 				localDB.save(); | 				localDb.save(); | ||||||
| 				Settings.getInstance().save(); | 				Settings.getInstance().save(); | ||||||
| 			} catch (IOException e) { | 			} catch (Exception e) { | ||||||
| 				logger.log(Level.SEVERE, "Unable to save local files", e); | 				logger.log(Level.SEVERE, "Unable to save local files", e); | ||||||
| 			} | 			} | ||||||
| 		})); | 		})); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user