From f14191b166c311a065c39448115d2fa5e7e1ca28 Mon Sep 17 00:00:00 2001 From: Kacper Witek Date: Sat, 24 Feb 2024 18:50:47 +0100 Subject: [PATCH] Add abstraction for reading and writing to account files with different formats --- application/accounts.json | 25 +++ application/users.ftprx | 11 -- .../main/java/com/ftprx/server/Server.java | 3 +- .../server/account/AccountFileFormat.java | 5 + .../server/account/AccountFileManager.java | 15 ++ .../server/account/FileAccountRepository.java | 158 ------------------ .../account/JsonAccountFileManager.java | 26 +++ .../repository/FileAccountRepository.java | 46 ++--- .../repository/InMemoryAccountRepository.java | 2 +- 9 files changed, 97 insertions(+), 194 deletions(-) create mode 100644 application/accounts.json delete mode 100644 application/users.ftprx create mode 100644 server/src/main/java/com/ftprx/server/account/AccountFileFormat.java create mode 100644 server/src/main/java/com/ftprx/server/account/AccountFileManager.java delete mode 100644 server/src/main/java/com/ftprx/server/account/FileAccountRepository.java create mode 100644 server/src/main/java/com/ftprx/server/account/JsonAccountFileManager.java diff --git a/application/accounts.json b/application/accounts.json new file mode 100644 index 0000000..4817b34 --- /dev/null +++ b/application/accounts.json @@ -0,0 +1,25 @@ +[ + { + "username": "test2", + "homeDirectory": "C:\\Users\\wkacp\\Desktop" + }, + { + "username": "admin", + "homeDirectory": "test", + "hashedPassword": "7ee737c83ee689c96ef37d3a029068c390ebc8f8" + }, + { + "username": "admin2", + "homeDirectory": "test", + "hashedPassword": "7ee737c83ee689c96ef37d3a029068c390ebc8f8" + }, + { + "username": "admin3", + "homeDirectory": "test", + "hashedPassword": "7ee737c83ee689c96ef37d3a029068c390ebc8f8" + }, + { + "username": "sd", + "homeDirectory": "" + } +] \ No newline at end of file diff --git a/application/users.ftprx b/application/users.ftprx deleted file mode 100644 index 6ca8de6..0000000 --- a/application/users.ftprx +++ /dev/null @@ -1,11 +0,0 @@ -[ - { - "username": "test2", - "homeDirectory": "C:\\Users\\wkacp\\Desktop" - }, - { - "username": "admin", - "homeDirectory": "C:\\Users\\wkacp\\Desktop\\home", - "hashedPassword": "d033e22ae348aeb5660fc2140aec35850c4da997" - } -] \ No newline at end of file diff --git a/server/src/main/java/com/ftprx/server/Server.java b/server/src/main/java/com/ftprx/server/Server.java index c40de95..d07e6ac 100644 --- a/server/src/main/java/com/ftprx/server/Server.java +++ b/server/src/main/java/com/ftprx/server/Server.java @@ -16,6 +16,7 @@ package com.ftprx.server; +import com.ftprx.server.account.AccountFileFormat; import com.ftprx.server.account.ObservableAccountRepository; import com.ftprx.server.channel.Client; import com.ftprx.server.repository.FileAccountRepository; @@ -51,7 +52,7 @@ public class Server { private Server() { this.clients = new CopyOnWriteArrayList<>(); - this.accountRepository = new FileAccountRepository("users.ftprx"); + this.accountRepository = new FileAccountRepository("accounts.json", AccountFileFormat.JSON); this.status = ServerStatus.STOPPED; this.config = ConfigFactory.create(ServerConfig.class); } diff --git a/server/src/main/java/com/ftprx/server/account/AccountFileFormat.java b/server/src/main/java/com/ftprx/server/account/AccountFileFormat.java new file mode 100644 index 0000000..e1bb939 --- /dev/null +++ b/server/src/main/java/com/ftprx/server/account/AccountFileFormat.java @@ -0,0 +1,5 @@ +package com.ftprx.server.account; + +public enum AccountFileFormat { + JSON +} diff --git a/server/src/main/java/com/ftprx/server/account/AccountFileManager.java b/server/src/main/java/com/ftprx/server/account/AccountFileManager.java new file mode 100644 index 0000000..4e6877a --- /dev/null +++ b/server/src/main/java/com/ftprx/server/account/AccountFileManager.java @@ -0,0 +1,15 @@ +package com.ftprx.server.account; + +import com.ftprx.server.account.Account; + +import java.io.Reader; +import java.io.Writer; +import java.util.List; + +/** + * An interface for reading from and writing to a file containing account list. + */ +public interface AccountFileManager { + void write(Writer writer, List accounts); + List read(Reader reader); +} diff --git a/server/src/main/java/com/ftprx/server/account/FileAccountRepository.java b/server/src/main/java/com/ftprx/server/account/FileAccountRepository.java deleted file mode 100644 index ad4879f..0000000 --- a/server/src/main/java/com/ftprx/server/account/FileAccountRepository.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2019, FtpRx Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.ftprx.server.account; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.tinylog.Logger; - -import java.io.Reader; -import java.io.Writer; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -import static com.ftprx.server.account.AccountInsertException.ACCOUNT_ALREADY_EXISTS; -import static java.nio.file.Files.*; -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; - -public class FileAccountRepository implements ObservableAccountRepository { - private final Gson gson; - private final Path accountsFile; - private final Set listeners; - - public FileAccountRepository(String filename) { - this.accountsFile = Paths.get(filename); - this.gson = new GsonBuilder().setPrettyPrinting().create(); - - listeners = Collections.newSetFromMap(new ConcurrentHashMap<>()); - - // Only development purpose (delete in production) - try { - insert(new Account("admin", "test", "dir")); - insert(new Account("admin2", "test", "dir")); - insert(new Account("admin3", "test", "dir")); - } catch (AccountInsertException | AccountCreateException ignore) {} - } - - @Override - public void update(@NotNull Account account) { - Objects.requireNonNull(account, "Account must not be null"); - } - - @Override - public Account findByUsername(@NotNull String username) { - return findAll() - .stream() - .filter(account -> username.equals(account.getUsername())) - .findFirst() - .orElse(null); - } - - @Override - public List findAll() { - return readFile(); - } - - @Override - public void insert(@NotNull Account account) throws AccountInsertException { - Objects.requireNonNull(account, "Account must not be null"); - if (isAccountExists(account)) { - throw new AccountInsertException(ACCOUNT_ALREADY_EXISTS); - } - List toSave = new ArrayList<>(findAll()); - toSave.add(account); - saveFile(toSave); - notifyInsertAccount(account); - } - - private void notifyInsertAccount(Account account) { - for (AccountRepositoryChangeListener listener : listeners) { - listener.onInsertAccount(account); - } - } - - public boolean isAccountExists(Account account) { - return findByUsername(account.getUsername()) != null; - } - - @Override - public void delete(@NotNull Account account) { - Objects.requireNonNull(account, "Account must not be null"); - delete(account.getUsername()); - notifyDeleteAccount(account); - } - - private void notifyDeleteAccount(Account account) { - for (AccountRepositoryChangeListener listener : listeners) { - listener.onDeleteEvent(account); - } - } - - private void delete(String username) { - List toSave = new ArrayList<>(findAll()); - List toRemove = new ArrayList<>(); - toSave.stream() - .filter(account -> username.equals(account.getUsername())) - .forEach(toRemove::add); - toSave.removeAll(toRemove); - saveFile(toSave); - } - - private void createAccountsFileIfNotExists() { - if (!exists(accountsFile)) { - Logger.debug("Account file not found!"); - try { - createFile(accountsFile); - } catch (Exception e) { - Logger.error(e.getMessage()); - } - } - } - - private void saveFile(List accounts) { - createAccountsFileIfNotExists(); - try (Writer writer = newBufferedWriter(accountsFile)) { - gson.toJson(accounts, writer); - } catch (Exception e) { - Logger.error(e.getMessage()); - } - } - - public List readFile() { - createAccountsFileIfNotExists(); - try (Reader reader = newBufferedReader(accountsFile)) { - return asList(gson.fromJson(reader, Account[].class)); - } catch (Exception e) { - return emptyList(); - } - } - - @Override - public void addListener(@Nullable AccountRepositoryChangeListener listener) { - Optional.ofNullable(listener).ifPresent(listeners::add); - } - - @Override - public void removeListener(@Nullable AccountRepositoryChangeListener listener) { - Optional.ofNullable(listener).ifPresent(listeners::remove); - } -} diff --git a/server/src/main/java/com/ftprx/server/account/JsonAccountFileManager.java b/server/src/main/java/com/ftprx/server/account/JsonAccountFileManager.java new file mode 100644 index 0000000..5b0d140 --- /dev/null +++ b/server/src/main/java/com/ftprx/server/account/JsonAccountFileManager.java @@ -0,0 +1,26 @@ +package com.ftprx.server.account; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.io.Reader; +import java.io.Writer; +import java.util.List; + +public class JsonAccountFileManager implements AccountFileManager { + private final Gson gson; + + public JsonAccountFileManager() { + this.gson = new GsonBuilder().setPrettyPrinting().create(); + } + + @Override + public void write(Writer writer, List accounts) { + gson.toJson(accounts, writer); + } + + @Override + public List read(Reader reader) { + return List.of(gson.fromJson(reader, Account[].class)); + } +} diff --git a/server/src/main/java/com/ftprx/server/repository/FileAccountRepository.java b/server/src/main/java/com/ftprx/server/repository/FileAccountRepository.java index 5bbd518..2a38128 100644 --- a/server/src/main/java/com/ftprx/server/repository/FileAccountRepository.java +++ b/server/src/main/java/com/ftprx/server/repository/FileAccountRepository.java @@ -17,13 +17,12 @@ package com.ftprx.server.repository; import com.ftprx.server.account.*; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.tinylog.Logger; -import java.io.*; +import java.io.Reader; +import java.io.Writer; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; @@ -31,17 +30,20 @@ import static com.ftprx.server.account.AccountInsertException.ACCOUNT_ALREADY_EXISTS; import static java.nio.file.Files.*; +import static java.util.Collections.emptyList; public class FileAccountRepository implements ObservableAccountRepository { - private final Gson gson; - private final Path repositoryFile; + private final Path accountsFile; private final Set listeners; + private final AccountFileManager fileManager; - public FileAccountRepository(String filename) { - this.repositoryFile = Paths.get(filename); - this.gson = new GsonBuilder() - .setPrettyPrinting() - .create(); + public FileAccountRepository(String filename, AccountFileFormat fileFormat) { + this.accountsFile = Paths.get(filename); + + switch (fileFormat) { + case JSON -> fileManager = new JsonAccountFileManager(); + default -> throw new IllegalStateException("Unexpected value: " + fileFormat); + } listeners = Collections.newSetFromMap(new ConcurrentHashMap<>()); @@ -50,9 +52,7 @@ public FileAccountRepository(String filename) { insert(new Account("admin", "test", "dir")); insert(new Account("admin2", "test", "dir")); insert(new Account("admin3", "test", "dir")); - } catch (AccountInsertException ignore) {} catch (AccountCreateException e) { - e.printStackTrace(); - } + } catch (AccountInsertException | AccountCreateException ignore) {} } @Override @@ -119,11 +119,11 @@ private void delete(String username) { saveFile(toSave); } - private void createRepositoryFileIfNotExists() { - if (!exists(repositoryFile)) { + private void createAccountsFileIfNotExists() { + if (!exists(accountsFile)) { Logger.debug("Account file not found!"); try { - createFile(repositoryFile); + createFile(accountsFile); } catch (Exception e) { Logger.error(e.getMessage()); } @@ -131,20 +131,20 @@ private void createRepositoryFileIfNotExists() { } private void saveFile(List accounts) { - createRepositoryFileIfNotExists(); - try (Writer writer = newBufferedWriter(repositoryFile)) { - gson.toJson(accounts, writer); + createAccountsFileIfNotExists(); + try (Writer writer = newBufferedWriter(accountsFile)) { + fileManager.write(writer, accounts); } catch (Exception e) { Logger.error(e.getMessage()); } } public List readFile() { - createRepositoryFileIfNotExists(); - try (Reader reader = newBufferedReader(repositoryFile)) { - return Arrays.asList(gson.fromJson(reader, Account[].class)); + createAccountsFileIfNotExists(); + try (Reader reader = newBufferedReader(accountsFile)) { + return fileManager.read(reader); } catch (Exception e) { - return Collections.emptyList(); + return emptyList(); } } diff --git a/server/src/main/java/com/ftprx/server/repository/InMemoryAccountRepository.java b/server/src/main/java/com/ftprx/server/repository/InMemoryAccountRepository.java index 63b06c0..baeafe5 100644 --- a/server/src/main/java/com/ftprx/server/repository/InMemoryAccountRepository.java +++ b/server/src/main/java/com/ftprx/server/repository/InMemoryAccountRepository.java @@ -9,7 +9,7 @@ import java.util.List; public class InMemoryAccountRepository implements AccountRepository { - private List accounts = new ArrayList<>(); + private final List accounts = new ArrayList<>(); public InMemoryAccountRepository() { try {