From ce172c3dc476551fef462a339582185937cb6757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Dellac=C3=A0?= Date: Fri, 21 Aug 2020 16:11:32 +0200 Subject: [PATCH] Implement YAML config, start SQLite implementation A config.yml file has been added, to allow configuring server settings. It will be expanded with new settings in the future. Also, SQLite support has been added, with a "database.sqlite" file. A basic table with user/pass/userid columns has been added for testing purposes. --- .gitignore | 2 + pom.xml | 19 ++- .../net/mindoverflow/webmarker/WebMarker.java | 30 ++++- .../webmarker/runnables/StatsRunnable.java | 16 +++ .../mindoverflow/webmarker/utils/Cached.java | 8 ++ .../webmarker/utils/config/ConfigEntries.java | 27 +++++ .../webmarker/utils/config/ConfigManager.java | 90 ++++++++++++++ .../utils/messaging/MessageLevel.java | 21 ++++ .../webmarker/utils/messaging/Messenger.java | 19 +++ .../webmarker/utils/sql/FDatabaseColumn.java | 32 +++++ .../webmarker/utils/sql/FDatabaseTable.java | 26 ++++ .../webmarker/utils/sql/SQLiteManager.java | 113 ++++++++++++++++++ .../utils/sql/primitives/SQLColumn.java | 12 ++ .../utils/sql/primitives/SQLDataType.java | 18 +++ .../utils/sql/primitives/SQLTable.java | 31 +++++ .../{server => webserver}/WebApplication.java | 9 +- src/main/resources/config.yml | 1 + 17 files changed, 469 insertions(+), 5 deletions(-) create mode 100644 src/main/java/net/mindoverflow/webmarker/runnables/StatsRunnable.java create mode 100644 src/main/java/net/mindoverflow/webmarker/utils/Cached.java create mode 100644 src/main/java/net/mindoverflow/webmarker/utils/config/ConfigEntries.java create mode 100644 src/main/java/net/mindoverflow/webmarker/utils/config/ConfigManager.java create mode 100644 src/main/java/net/mindoverflow/webmarker/utils/messaging/MessageLevel.java create mode 100644 src/main/java/net/mindoverflow/webmarker/utils/messaging/Messenger.java create mode 100644 src/main/java/net/mindoverflow/webmarker/utils/sql/FDatabaseColumn.java create mode 100644 src/main/java/net/mindoverflow/webmarker/utils/sql/FDatabaseTable.java create mode 100644 src/main/java/net/mindoverflow/webmarker/utils/sql/SQLiteManager.java create mode 100644 src/main/java/net/mindoverflow/webmarker/utils/sql/primitives/SQLColumn.java create mode 100644 src/main/java/net/mindoverflow/webmarker/utils/sql/primitives/SQLDataType.java create mode 100644 src/main/java/net/mindoverflow/webmarker/utils/sql/primitives/SQLTable.java rename src/main/java/net/mindoverflow/webmarker/{server => webserver}/WebApplication.java (84%) create mode 100644 src/main/resources/config.yml diff --git a/.gitignore b/.gitignore index 92322c4..f3adb52 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .idea/ target/ +run/ +*.ignore diff --git a/pom.xml b/pom.xml index bfd99ad..7cd7ea1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ net.mindoverflow.webmarker WebMarker - 1.0-SNAPSHOT + 0.0.1-alpha jar @@ -16,12 +16,27 @@ 1.13.1 pom - ro.pippo pippo-controller 1.13.1 + + org.slf4j + slf4j-simple + 2.0.0-alpha1 + + + org.xerial + sqlite-jdbc + 3.32.3.2 + + + + org.yaml + snakeyaml + 1.21 + diff --git a/src/main/java/net/mindoverflow/webmarker/WebMarker.java b/src/main/java/net/mindoverflow/webmarker/WebMarker.java index 590a597..be10a16 100644 --- a/src/main/java/net/mindoverflow/webmarker/WebMarker.java +++ b/src/main/java/net/mindoverflow/webmarker/WebMarker.java @@ -1,13 +1,39 @@ package net.mindoverflow.webmarker; -import net.mindoverflow.webmarker.server.WebApplication; +import net.mindoverflow.webmarker.runnables.StatsRunnable; +import net.mindoverflow.webmarker.utils.Cached; +import net.mindoverflow.webmarker.utils.sql.SQLiteManager; +import net.mindoverflow.webmarker.webserver.WebApplication; +import net.mindoverflow.webmarker.utils.config.ConfigEntries; +import net.mindoverflow.webmarker.utils.config.ConfigManager; +import net.mindoverflow.webmarker.utils.messaging.Messenger; import ro.pippo.core.Pippo; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + public class WebMarker { + private static final Messenger msg = new Messenger(); + public static void main(String[] args) { + ConfigManager.checkFiles(); + ConfigManager.loadFiles(); + Cached.sqlManager = new SQLiteManager(); + Cached.sqlManager.initialize(); + + msg.info("Loading Pippo framework..."); final Pippo pippo = new Pippo(new WebApplication()); - pippo.start(); + msg.info("Loaded Pippo framework."); + + msg.info("Starting webserver..."); + int port = (int) ConfigEntries.WEBSERVER_PORT.getValue(); + pippo.start(port); + msg.info("Started webserver."); + + ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor(); + exec.scheduleAtFixedRate(new StatsRunnable(), 0, 5, TimeUnit.SECONDS); } } diff --git a/src/main/java/net/mindoverflow/webmarker/runnables/StatsRunnable.java b/src/main/java/net/mindoverflow/webmarker/runnables/StatsRunnable.java new file mode 100644 index 0000000..822ab5e --- /dev/null +++ b/src/main/java/net/mindoverflow/webmarker/runnables/StatsRunnable.java @@ -0,0 +1,16 @@ +package net.mindoverflow.webmarker.runnables; + +import net.mindoverflow.webmarker.utils.messaging.Messenger; + +public class StatsRunnable implements Runnable { + + Messenger msg = new Messenger(); + + @Override + public void run() + { + double usedRam = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1000000.0; + msg.info("Used RAM: " + usedRam + "MB"); + + } +} diff --git a/src/main/java/net/mindoverflow/webmarker/utils/Cached.java b/src/main/java/net/mindoverflow/webmarker/utils/Cached.java new file mode 100644 index 0000000..82d3611 --- /dev/null +++ b/src/main/java/net/mindoverflow/webmarker/utils/Cached.java @@ -0,0 +1,8 @@ +package net.mindoverflow.webmarker.utils; + +import net.mindoverflow.webmarker.utils.sql.SQLiteManager; + +public class Cached { + + public static SQLiteManager sqlManager; +} diff --git a/src/main/java/net/mindoverflow/webmarker/utils/config/ConfigEntries.java b/src/main/java/net/mindoverflow/webmarker/utils/config/ConfigEntries.java new file mode 100644 index 0000000..7a7346f --- /dev/null +++ b/src/main/java/net/mindoverflow/webmarker/utils/config/ConfigEntries.java @@ -0,0 +1,27 @@ +package net.mindoverflow.webmarker.utils.config; + +public enum ConfigEntries +{ + WEBSERVER_PORT("port", 7344); + + private final String path; + private Object value; + + ConfigEntries(String path, Object value) + { + this.path = path; + this.value = value; + } + + public String getPath() { + return path; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } +} diff --git a/src/main/java/net/mindoverflow/webmarker/utils/config/ConfigManager.java b/src/main/java/net/mindoverflow/webmarker/utils/config/ConfigManager.java new file mode 100644 index 0000000..2850968 --- /dev/null +++ b/src/main/java/net/mindoverflow/webmarker/utils/config/ConfigManager.java @@ -0,0 +1,90 @@ +package net.mindoverflow.webmarker.utils.config; + +import net.mindoverflow.webmarker.utils.messaging.MessageLevel; +import net.mindoverflow.webmarker.utils.messaging.Messenger; +import org.yaml.snakeyaml.Yaml; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; + +public class ConfigManager +{ + + private static final Messenger msg = new Messenger(); + + public static void checkFiles() + { + msg.send(MessageLevel.INFO, "Checking configuration files: "); + + File configFile = FileType.CONFIG_YAML.file; + + if(!configFile.exists()) + { + try { + InputStream stream = ConfigManager.class.getResourceAsStream("/" + configFile.getName()); + Files.copy(stream, Paths.get(configFile.getAbsolutePath())); + stream.close(); + } + catch (IOException e) + { + msg.sendLine(MessageLevel.NONE, "FATAL"); + e.printStackTrace(); + msg.critical("Error creating missing file!"); + System.exit(1); + } + } + + msg.sendLine(MessageLevel.NONE, "OK"); + } + + public static void loadFiles() + { + msg.send(MessageLevel.INFO, "Loading configuration files: "); + + try { + InputStream stream = new FileInputStream(FileType.CONFIG_YAML.file); + Yaml yaml = new Yaml(); + FileType.CONFIG_YAML.yaml = yaml.load(stream); + + } catch (FileNotFoundException e) { + e.printStackTrace(); + msg.critical("Error loading config file!"); + } + + for(ConfigEntries entry : ConfigEntries.values()) + { + String path = entry.getPath(); + Object value = FileType.CONFIG_YAML.yaml.get(path); + entry.setValue(value); + } + + msg.sendLine(MessageLevel.NONE, "OK"); + + for(ConfigEntries entry : ConfigEntries.values()) + { msg.info(entry.name() + ": " + entry.getValue()); } + } + + public static String getJarAbsolutePath() + { + + Path currentPath = Paths.get(""); + return currentPath.toAbsolutePath().toString(); + } + + public enum FileType + { + DATABASE_FILE(new File(getJarAbsolutePath() + File.separator + "database.sqlite"), null), + CONFIG_YAML(new File(getJarAbsolutePath() + File.separator + "config.yml"), new HashMap<>()); + + public File file; + public HashMap yaml; + FileType(File givenFile, HashMap yamlConfig) + { + file = givenFile; + yaml = yamlConfig; + } + } +} diff --git a/src/main/java/net/mindoverflow/webmarker/utils/messaging/MessageLevel.java b/src/main/java/net/mindoverflow/webmarker/utils/messaging/MessageLevel.java new file mode 100644 index 0000000..f5b8645 --- /dev/null +++ b/src/main/java/net/mindoverflow/webmarker/utils/messaging/MessageLevel.java @@ -0,0 +1,21 @@ +package net.mindoverflow.webmarker.utils.messaging; + +public enum MessageLevel { + + INFO("[INFO] "), + WARNING("[WARNING] "), + CRITICAL("[CRITICAL] "), + NONE(""); + + private String prefix; + + MessageLevel(String prefix) + { + this.prefix = prefix; + } + + public String getPrefix() + { + return prefix; + } +} diff --git a/src/main/java/net/mindoverflow/webmarker/utils/messaging/Messenger.java b/src/main/java/net/mindoverflow/webmarker/utils/messaging/Messenger.java new file mode 100644 index 0000000..43da264 --- /dev/null +++ b/src/main/java/net/mindoverflow/webmarker/utils/messaging/Messenger.java @@ -0,0 +1,19 @@ +package net.mindoverflow.webmarker.utils.messaging; + +public class Messenger { + + public void info(String message) + { sendLine(MessageLevel.INFO, message); } + + public void warn(String message) + { sendLine(MessageLevel.WARNING, message); } + + public void critical(String message) + { sendLine(MessageLevel.CRITICAL, message); } + + public void sendLine(MessageLevel level, String message) + { System.out.println(level.getPrefix() + message); } + + public void send(MessageLevel level, String message) + { System.out.print(level.getPrefix() + message); } +} diff --git a/src/main/java/net/mindoverflow/webmarker/utils/sql/FDatabaseColumn.java b/src/main/java/net/mindoverflow/webmarker/utils/sql/FDatabaseColumn.java new file mode 100644 index 0000000..b0fedee --- /dev/null +++ b/src/main/java/net/mindoverflow/webmarker/utils/sql/FDatabaseColumn.java @@ -0,0 +1,32 @@ +package net.mindoverflow.webmarker.utils.sql; + +import net.mindoverflow.webmarker.utils.sql.primitives.SQLColumn; +import net.mindoverflow.webmarker.utils.sql.primitives.SQLDataType; + +public enum FDatabaseColumn +{ + + ALL(new SQLColumn("*"), null), + USERNAME(new SQLColumn("username"), SQLDataType.VARCHAR_128), + PASSWORD(new SQLColumn("password"), SQLDataType.VARCHAR_128), + USERID(new SQLColumn("userid"), SQLDataType.VARCHAR_128), + + + + ; + + private final SQLColumn column; + private final SQLDataType type; + + FDatabaseColumn(SQLColumn column, SQLDataType type) + { + this.column = column; + this.type = type; + } + + public SQLColumn getColumn() + { return column; } + + public SQLDataType getDataType() + { return type; } +} diff --git a/src/main/java/net/mindoverflow/webmarker/utils/sql/FDatabaseTable.java b/src/main/java/net/mindoverflow/webmarker/utils/sql/FDatabaseTable.java new file mode 100644 index 0000000..3d5b182 --- /dev/null +++ b/src/main/java/net/mindoverflow/webmarker/utils/sql/FDatabaseTable.java @@ -0,0 +1,26 @@ +package net.mindoverflow.webmarker.utils.sql; + + +import net.mindoverflow.webmarker.utils.sql.primitives.SQLTable; + +import java.util.ArrayList; + +public enum FDatabaseTable +{ + USERS(new SQLTable("users", // table name + new ArrayList<>(){{ // columns + add(FDatabaseColumn.USERID); + add(FDatabaseColumn.USERNAME); + add(FDatabaseColumn.PASSWORD); + }})), + + ; + + private final SQLTable table; + + FDatabaseTable(SQLTable table) + { this.table = table; } + + public SQLTable getTable() + { return table; } +} diff --git a/src/main/java/net/mindoverflow/webmarker/utils/sql/SQLiteManager.java b/src/main/java/net/mindoverflow/webmarker/utils/sql/SQLiteManager.java new file mode 100644 index 0000000..8dac86e --- /dev/null +++ b/src/main/java/net/mindoverflow/webmarker/utils/sql/SQLiteManager.java @@ -0,0 +1,113 @@ +package net.mindoverflow.webmarker.utils.sql; + +import net.mindoverflow.webmarker.utils.config.ConfigManager; +import net.mindoverflow.webmarker.utils.messaging.MessageLevel; +import net.mindoverflow.webmarker.utils.messaging.Messenger; +import net.mindoverflow.webmarker.utils.sql.primitives.SQLDataType; +import net.mindoverflow.webmarker.utils.sql.primitives.SQLTable; + +import java.sql.*; +import java.util.List; + +public class SQLiteManager { + + private final Messenger msg = new Messenger(); + private Connection connection; + + public void initialize() + { + msg.send(MessageLevel.INFO, "Connecting to SQLite database: "); + + String url = "jdbc:sqlite:" + ConfigManager.FileType.DATABASE_FILE.file.getAbsolutePath(); + try { + connection = DriverManager.getConnection(url); + if(connection != null && !connection.isClosed()) + { + msg.sendLine(MessageLevel.NONE, "OK"); + doInitialSetup(); + } + + } catch (SQLException throwables) { + msg.sendLine(MessageLevel.NONE, "FATAL"); + throwables.printStackTrace(); + msg.critical("Error connecting to SQLite database!"); + System.exit(1); + } + } + + private void doInitialSetup() + { + for(FDatabaseTable currentTable : FDatabaseTable.values()) + { + if(!tableExists(currentTable)) + { + msg.info("Creating SQLite table `" + currentTable.getTable().getTableSQLName() + "`"); + createTable(currentTable); + } + } + } + + private boolean tableExists(FDatabaseTable tableEnum) + { + String name = tableEnum.getTable().getTableSQLName(); + + try + { + DatabaseMetaData meta = connection.getMetaData(); + ResultSet result = meta.getTables(null, null, name, null); + while(result.next()) + { + String tableName = result.getString("TABLE_NAME"); + if(tableName != null && tableName.equals(name.toLowerCase())) return true; + } + + } catch (SQLException e) + { + e.printStackTrace(); + msg.critical("Error checking SQLite table " + name + "!"); + System.exit(1); + } + return false; + } + + private void createTable(FDatabaseTable tableEnum) + { + SQLTable table = tableEnum.getTable(); + List columns = table.getColumns(); + List dataTypes = table.getDataTypes(); + + StringBuilder query = new StringBuilder("CREATE TABLE IF NOT EXISTS ").append(table.getTableSQLName()).append(" ("); + + int pos = 0; + for(FDatabaseColumn column : columns) + { + query.append(column.getColumn().getColumnSQLName()).append(" ").append(dataTypes.get(pos).getSQLName()); + pos++; + + if(pos < columns.size()) // we don't want to append a colon to the last entry + { query.append(", "); } + } + query.append(");"); + executeUpdate(query.toString()); + } + + private void executeUpdate(String query) + { + try + { + if(connection == null || connection.isClosed()) + { + msg.critical("Lost connection to SQLite database!"); + System.exit(1); + } + + Statement statement = connection.createStatement(); + statement.executeUpdate(query); + } catch (SQLException e) + { + e.printStackTrace(); + msg.critical("Error executing SQLite update!"); + System.exit(1); + } + } +} diff --git a/src/main/java/net/mindoverflow/webmarker/utils/sql/primitives/SQLColumn.java b/src/main/java/net/mindoverflow/webmarker/utils/sql/primitives/SQLColumn.java new file mode 100644 index 0000000..8d6bd68 --- /dev/null +++ b/src/main/java/net/mindoverflow/webmarker/utils/sql/primitives/SQLColumn.java @@ -0,0 +1,12 @@ +package net.mindoverflow.webmarker.utils.sql.primitives; + +public class SQLColumn { + + private final String columnName; + + public SQLColumn(String name) + { columnName = name; } + + public String getColumnSQLName() + { return columnName; } +} diff --git a/src/main/java/net/mindoverflow/webmarker/utils/sql/primitives/SQLDataType.java b/src/main/java/net/mindoverflow/webmarker/utils/sql/primitives/SQLDataType.java new file mode 100644 index 0000000..bd9ad9d --- /dev/null +++ b/src/main/java/net/mindoverflow/webmarker/utils/sql/primitives/SQLDataType.java @@ -0,0 +1,18 @@ +package net.mindoverflow.webmarker.utils.sql.primitives; + +public enum SQLDataType +{ + INTEGER("INTEGER NOT NULL"), + VARCHAR_128("VARCHAR(128)"), + TEXT("TEXT"), + DATETIME("DATETIME"), + + ; + + private String sqlName; + SQLDataType(String sqlName) + { this.sqlName = sqlName; } + + public String getSQLName() + { return this.sqlName; } +} diff --git a/src/main/java/net/mindoverflow/webmarker/utils/sql/primitives/SQLTable.java b/src/main/java/net/mindoverflow/webmarker/utils/sql/primitives/SQLTable.java new file mode 100644 index 0000000..aa856d7 --- /dev/null +++ b/src/main/java/net/mindoverflow/webmarker/utils/sql/primitives/SQLTable.java @@ -0,0 +1,31 @@ +package net.mindoverflow.webmarker.utils.sql.primitives; + +import net.mindoverflow.webmarker.utils.sql.FDatabaseColumn; + +import java.util.ArrayList; +import java.util.List; + +public class SQLTable +{ + private final String tableName; + private final List columns; + private final List columnTypes = new ArrayList<>(); + + public SQLTable(String tableName, List columns) + { + this.tableName = tableName; + this.columns = columns; + + for(FDatabaseColumn column : columns) + { columnTypes.add(column.getDataType()); } + } + + public String getTableSQLName() + { return tableName; } + + public List getColumns() + { return columns; } + + public List getDataTypes() + { return columnTypes; } +} diff --git a/src/main/java/net/mindoverflow/webmarker/server/WebApplication.java b/src/main/java/net/mindoverflow/webmarker/webserver/WebApplication.java similarity index 84% rename from src/main/java/net/mindoverflow/webmarker/server/WebApplication.java rename to src/main/java/net/mindoverflow/webmarker/webserver/WebApplication.java index a3a75c1..188975a 100644 --- a/src/main/java/net/mindoverflow/webmarker/server/WebApplication.java +++ b/src/main/java/net/mindoverflow/webmarker/webserver/WebApplication.java @@ -1,4 +1,4 @@ -package net.mindoverflow.webmarker.server; +package net.mindoverflow.webmarker.webserver; import net.mindoverflow.webmarker.utils.URLMap; import ro.pippo.controller.ControllerApplication; @@ -46,6 +46,13 @@ public class WebApplication extends ControllerApplication { }); + POST("/post", routeContext -> { + int userId = routeContext.getParameter("id").toInt(); + String url = routeContext.getParameter("url").toString(); + + routeContext.send("AAAAAAAAAAAAA " + url); + }); + ANY("/.*", new TrailingSlashHandler(false)); // remove trailing slash } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..20a8781 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1 @@ +port: 7344 \ No newline at end of file