Merge branch 'lab7-dev' into develop

This commit is contained in:
Ivan Zinchenko 2024-06-08 00:20:55 +03:00
commit c23b171b5f
Signed by: zinch
GPG Key ID: 6D45AA2C8FD6A37E
15 changed files with 203 additions and 68 deletions

View File

@ -1,13 +1,23 @@
package me.zinch.Lab7.Server;
import me.zinch.Lab7.Server.console.Console;
import me.zinch.Lab7.Server.utils.SHA256;
import me.zinch.Lab7.Server.exceptions.DbConnectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
/**
* Main class for running the application.
*/
public class App {
public static void main(String[] args) throws Exception {
Console.run();
private static final Logger log = LoggerFactory.getLogger(App.class);
public static void main(String[] args) {
try {
Console.run();
} catch (IOException | DbConnectionException e) {
log.error(e.getMessage());
}
}
}

View File

@ -37,13 +37,6 @@ public class CommandManager {
commandList.add(command);
}
public static String getRegisteredCommand() {
return String.join("\n", commandList
.stream()
.map(command -> String.format("%s - %s", command.getName(), command.getDescription()))
.toList());
}
public static Optional<Command> getCommandByInput(String input) {
return commandList.stream().filter(command -> command.checkPattern(input)).findFirst();
}

View File

@ -9,13 +9,13 @@ import me.zinch.Lab7.Server.wrapper.IStorage;
import java.sql.SQLException;
public class Login extends PostCommand {
public class Login extends UnauthorizedCommand {
public Login() {
super("login", "");
}
@Override
public String action(IStorage productCollection, Object obj, User user) throws CommandActionException {
public String action( Object obj) throws CommandActionException {
try {
var loginUser = (User) obj;
if (DbWrapper.isUserExists(loginUser.getLogin(), SHA256.hash(loginUser.getPassword()), DbController.getConnection()))

View File

@ -9,13 +9,12 @@ import me.zinch.Lab7.Server.wrapper.IStorage;
import java.sql.SQLException;
public class Register extends PostCommand {
public class Register extends UnauthorizedCommand {
public Register() {
super("register", "");
}
@Override
public String action(IStorage productCollection, Object obj, User user) throws CommandActionException {
public String action(Object obj) throws CommandActionException {
try {
var newUser = (User) obj;
newUser.setPassword(SHA256.hash(newUser.getPassword()));

View File

@ -0,0 +1,19 @@
package me.zinch.Lab7.Server.commands;
import me.zinch.Lab7.Domain.net.User;
import me.zinch.Lab7.Server.exceptions.CommandActionException;
import me.zinch.Lab7.Server.wrapper.IStorage;
import java.util.regex.Pattern;
public abstract class UnauthorizedCommand extends Command {
public UnauthorizedCommand(String name, String description, Pattern pattern) {
super(name, description, pattern);
}
public UnauthorizedCommand(String name, String description) {
super(name, description);
}
public abstract String action(Object obj) throws CommandActionException;
}

View File

@ -2,7 +2,7 @@ package me.zinch.Lab7.Server.console;
import me.zinch.Lab7.Domain.exceptions.ValidationException;
import me.zinch.Lab7.Server.wrapper.IStorage;
import me.zinch.Lab7.Server.Server.ServerBuilder;
import me.zinch.Lab7.Server.server.ServerBuilder;
import me.zinch.Lab7.Server.commands.GetCommand;
import me.zinch.Lab7.Server.commands.SystemCommandManager;
import me.zinch.Lab7.Server.configs.ServerConfig;

View File

@ -0,0 +1,21 @@
package me.zinch.Lab7.Server.exceptions;
import java.io.IOException;
public class NonExistentCommand extends IOException {
public NonExistentCommand() {
super("This command doesn't exist");
}
public NonExistentCommand(String message) {
super(message);
}
public NonExistentCommand(String message, Throwable cause) {
super(message, cause);
}
public NonExistentCommand(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,21 @@
package me.zinch.Lab7.Server.exceptions;
import java.io.IOException;
public class UnauthorizedException extends IOException {
public UnauthorizedException() {
super("Error during user validation");
}
public UnauthorizedException(String message) {
super(message);
}
public UnauthorizedException(String message, Throwable cause) {
super(message, cause);
}
public UnauthorizedException(Throwable cause) {
super(cause);
}
}

View File

@ -1,16 +1,22 @@
package me.zinch.Lab7.Server.Server;
package me.zinch.Lab7.Server.server;
import me.zinch.Lab7.Domain.net.BodylessMessage;
import me.zinch.Lab7.Domain.net.Message;
import me.zinch.Lab7.Domain.net.MessageBody;
import me.zinch.Lab7.Domain.net.MessageType;
import me.zinch.Lab7.Domain.net.User;
import me.zinch.Lab7.Server.commands.UnauthorizedCommand;
import me.zinch.Lab7.Server.db.DbController;
import me.zinch.Lab7.Server.exceptions.CommandActionException;
import me.zinch.Lab7.Server.exceptions.DbExecuteException;
import me.zinch.Lab7.Server.exceptions.NonExistentCommand;
import me.zinch.Lab7.Server.exceptions.UnauthorizedException;
import me.zinch.Lab7.Server.utils.SHA256;
import me.zinch.Lab7.Server.wrapper.AuthGuard;
import me.zinch.Lab7.Server.wrapper.IStorage;
import me.zinch.Lab7.Server.commands.CommandManager;
import me.zinch.Lab7.Server.commands.GetCommand;
import me.zinch.Lab7.Server.commands.PostCommand;
import me.zinch.Lab7.Server.exceptions.CommandActionException;
import me.zinch.Lab7.Server.wrapper.ProductCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -24,7 +30,6 @@ import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -42,7 +47,6 @@ public class Server {
serverSocket = new ServerSocket(port);
isRunning = true;
if (collection == null) collection = new ProductCollection(new ArrayList<>());
storage = collection;
log.info("The server has been assigned to port {}", port);
@ -112,31 +116,32 @@ public class Server {
}
private byte[] createResponse(Message message) throws IOException {
if (message.getType() == MessageType.HELLO) return serialize(new BodylessMessage(MessageType.HELLO));
var user = (User) message.getUser();
if (user != null) user.setPassword(SHA256.hash(user.getPassword()));
return switch (message.getType()) {
case HELLO -> serialize(new BodylessMessage(MessageType.HELLO));
case GET -> handleGetCommand((String) message.getBody(), user);
case POST -> handlePostCommand((MessageBody) message.getBody(), user);
default -> {
log.error("Message type not supported");
yield null;
}
};
if (user == null) return handleUnauthorizedCommand(message);
user.setPassword(SHA256.hash(user.getPassword()));
try {
AuthGuard.throwIfUserNotValid(user);
return switch (message.getType()) {
case GET -> handleGetCommand((String) message.getBody(), user);
case POST -> handlePostCommand((MessageBody) message.getBody(), user);
default -> {
log.error("Message type not supported");
yield null;
}
};
} catch (IOException | DbExecuteException | CommandActionException e) {
return serialize(new BodylessMessage(MessageType.ERROR, e.getMessage()));
}
}
private byte[] handleGetCommand(String inputCommand, User user) throws IOException {
var command = CommandManager.getCommandByInput(inputCommand);
if (command.isEmpty()) {
log.error("Command {} not found", inputCommand);
return serialize(new BodylessMessage(MessageType.ERROR, String.format("Command %s not found", inputCommand)));
}
try {
var action = (GetCommand) command.get();
return serialize(new BodylessMessage(MessageType.OK, action.action(storage, user)));
} catch (CommandActionException e) {
return serialize(new BodylessMessage(MessageType.ERROR, e.getMessage()));
}
if (command.isEmpty()) throw new NonExistentCommand("Такой команды не существует");
var action = (GetCommand) command.get();
return serialize(new BodylessMessage(MessageType.OK, action.action(storage, user)));
}
private byte[] handlePostCommand(MessageBody request, User user) throws IOException {
@ -144,15 +149,25 @@ public class Server {
var body = request.getBody();
var command = CommandManager.getCommandByInput(inputCommand);
if (command.isEmpty()) {
log.error("Command {} not found", inputCommand);
return serialize(new BodylessMessage(MessageType.ERROR, String.format("Command %s not found", inputCommand)));
}
if (command.isEmpty()) throw new NonExistentCommand("Такой команды не существует");
var action = (PostCommand) command.get();
return serialize(new BodylessMessage(MessageType.OK, action.action(storage, body, user)));
}
// Only get commands or else exception
private byte[] handleUnauthorizedCommand(Message message) throws IOException {
try {
var action = (PostCommand) command.get();
return serialize(new BodylessMessage(MessageType.OK, action.action(storage, body, user)));
} catch (CommandActionException e) {
var messageBody = (MessageBody) message.getBody();
var inputCommand = messageBody.getCommand();
var body = messageBody.getBody();
var command = CommandManager.getCommandByInput(inputCommand);
if (command.isEmpty()) throw new NonExistentCommand("Такой команды не существует");
var action = (UnauthorizedCommand) command.get();
return serialize(new BodylessMessage(MessageType.OK, action.action(body)));
} catch (ClassCastException | NonExistentCommand | CommandActionException e) {
return serialize(new BodylessMessage(MessageType.ERROR, e.getMessage()));
}
}

View File

@ -1,15 +1,17 @@
package me.zinch.Lab7.Server.Server;
package me.zinch.Lab7.Server.server;
import me.zinch.Lab7.Server.wrapper.IStorage;
import me.zinch.Lab7.Server.wrapper.ProductCollection;
import java.io.IOException;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Set;
public class ServerBuilder {
private static class Config {
private int port = 3000;
private IStorage collection = new ProductCollection();
private IStorage collection;
public int getPort() {
return port;
@ -32,7 +34,6 @@ public class ServerBuilder {
public ServerBuilder() {
config.setPort(3000);
config.setCollection(new ProductCollection(new ArrayList<>()));
}
private ServerBuilder(Config config) {

View File

@ -0,0 +1,19 @@
package me.zinch.Lab7.Server.wrapper;
import me.zinch.Lab7.Domain.net.User;
import me.zinch.Lab7.Server.db.DbController;
import me.zinch.Lab7.Server.exceptions.CommandActionException;
import me.zinch.Lab7.Server.exceptions.DbExecuteException;
import me.zinch.Lab7.Server.exceptions.UnauthorizedException;
import java.sql.SQLException;
public class AuthGuard {
public static void throwIfUserNotValid(User user) throws UnauthorizedException, DbExecuteException {
try {
if (!DbWrapper.isUserExists(user, DbController.getConnection())) throw new UnauthorizedException("Ошибка при валидации пользователя");
} catch (SQLException e) {
throw new DbExecuteException();
}
}
}

View File

@ -15,7 +15,9 @@ import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DbWrapper {
private static final Logger log = LoggerFactory.getLogger(DbWrapper.class);
@ -196,6 +198,7 @@ public class DbWrapper {
public static void clearProducts(User user, Connection connection) throws SQLException {
try (var st = connection.prepareStatement(FileReader.readFromResource("migrations/clearProducts.sql"))) {
st.setLong(1, getUserId(user, connection));
st.setLong(2, getUserId(user, connection));
st.executeUpdate();
} catch (SQLException | FileNotFoundException e) {
log.error(e.getMessage());
@ -280,4 +283,20 @@ public class DbWrapper {
log.error(e.getMessage());
}
}
public static Map<Long, String> getProductsOwners(Connection connection) throws SQLException {
try (var st = connection.prepareStatement(FileReader.readFromResource("migrations/getProductsOwners.sql"))) {
var productsOwners = new HashMap<Long, String>();
var rs = st.executeQuery();
while (rs.next()) {
productsOwners.put(rs.getLong(1), rs.getString(2));
}
return productsOwners;
} catch (SQLException | FileNotFoundException e) {
log.error(e.getMessage());
throw new SQLException(e);
}
}
}

View File

@ -4,6 +4,7 @@ import me.zinch.Lab7.Domain.models.Product;
import me.zinch.Lab7.Domain.models.ProductDTO;
import me.zinch.Lab7.Domain.net.User;
import me.zinch.Lab7.Server.db.DbController;
import me.zinch.Lab7.Server.exceptions.DbConnectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -15,7 +16,9 @@ import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@ -26,9 +29,10 @@ import java.util.concurrent.locks.Lock;
public class ProductCollection implements IStorage {
private static final Logger log = LoggerFactory.getLogger(ProductCollection.class);
private TreeSet<Product> productList;
private Map<Long, String> productsOwners;
private final Lock lock = new ReentrantLock();
private List<Product> fetchProductFromDb(Connection connection) {
private List<Product> fetchProducts(Connection connection) {
try {
return DbWrapper.getProducts(connection);
} catch (SQLException | FileNotFoundException e) {
@ -37,25 +41,32 @@ public class ProductCollection implements IStorage {
}
}
private void Initialize(List<Product> list) {
private Map<Long, String> fetchProductsOwners(Connection connection) {
try {
return DbWrapper.getProductsOwners(connection);
} catch (SQLException e) {
log.error(e.getMessage());
return new HashMap<>();
}
}
private void fetchDb() throws DbConnectionException {
productList.clear();
productList.addAll(fetchProducts(DbController.getConnection()));
productsOwners.clear();
productsOwners.putAll(fetchProductsOwners(DbController.getConnection()));
}
public ProductCollection(Connection connection) {
productList = new TreeSet<>((o1, o2) -> {
var o1Length = Math.sqrt(o1.getCoordinates().getX() * o1.getCoordinates().getX() + o1.getCoordinates().getY() * o1.getCoordinates().getY());
var o2Length = Math.sqrt(o2.getCoordinates().getX() * o2.getCoordinates().getX() + o2.getCoordinates().getY() * o2.getCoordinates().getY());
return Math.toIntExact((long) (o1Length - o2Length));
});
productList.addAll(list);
}
productsOwners = new HashMap<>();
public ProductCollection(List<Product> list) {
Initialize(list);
}
public ProductCollection() {
Initialize(new ArrayList<>());
}
public ProductCollection(Connection connection) {
Initialize(fetchProductFromDb(connection));
productList.addAll(fetchProducts(connection));
productsOwners.putAll(fetchProductsOwners(connection));
}
public Product getProductById(Long id) {
@ -83,7 +94,7 @@ public class ProductCollection implements IStorage {
lock.lock();
try {
var product = DbWrapper.updateProduct(id, productDTO, DbController.getConnection(), user);
fetchProductFromDb(DbController.getConnection());
fetchDb();
return productDTO.buildProduct(product.getId(), product.getCreationDate());
} finally {
lock.unlock();
@ -119,7 +130,7 @@ public class ProductCollection implements IStorage {
lock.lock();
try {
DbWrapper.clearProducts(user, DbController.getConnection());
fetchProductFromDb(DbController.getConnection());
fetchDb();
} finally {
lock.unlock();
}
@ -157,7 +168,7 @@ public class ProductCollection implements IStorage {
try {
var size = productList.size();
DbWrapper.removeLower(id, DbController.getConnection(), user);
fetchProductFromDb(DbController.getConnection());
fetchDb();
return size - productList.size();
} finally {
lock.unlock();
@ -203,7 +214,9 @@ public class ProductCollection implements IStorage {
public String toString() {
lock.lock();
try {
return String.join("\n", productList.stream().map(Product::toString).toList());
return String.join("\n", productList.stream()
.map(product -> String.format("%s by %s", product.toString(), productsOwners.getOrDefault(product.getId(), "Unknown")))
.toList());
} finally {
lock.unlock();
}

View File

@ -1,3 +1,5 @@
DELETE FROM products_owners WHERE "user" = ?;
DELETE FROM products
WHERE id IN (
SELECT product FROM products_owners

View File

@ -0,0 +1,3 @@
SELECT product, login
FROM products_owners
LEFT JOIN users on products_owners."user" = users.id;