diff --git a/.gitignore b/.gitignore index e6d18f3..d1f804e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ bin .gradle build /modules -/plugins \ No newline at end of file +/plugins +logs \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index cfdecc4..a6c1a1d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { group = "igs-landstuhl" -version = "v2.0.0-SNAPSHOT-0" +version = "v2.0.0-SNAPSHOT-1" application { mainClass.set("de.igslandstuhl.database.Application") @@ -25,6 +25,10 @@ dependencies { implementation("org.jline:jline:3.30.6") // for better console input handling implementation("org.yaml:snakeyaml:2.2") // plugin imports + // Logging + implementation("org.slf4j:slf4j-api:2.0.13") + implementation("ch.qos.logback:logback-classic:1.5.6") + testImplementation("org.junit.jupiter:junit-jupiter:5.13.4") // using JUnit 5 (latest) testRuntimeOnly("org.junit.platform:junit-platform-launcher") } diff --git a/src/main/java/de/igslandstuhl/database/Application.java b/src/main/java/de/igslandstuhl/database/Application.java index 19cd206..b1ac18e 100644 --- a/src/main/java/de/igslandstuhl/database/Application.java +++ b/src/main/java/de/igslandstuhl/database/Application.java @@ -5,6 +5,8 @@ import java.util.List; import org.jline.reader.UserInterruptException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import de.igslandstuhl.database.api.SerializationException; import de.igslandstuhl.database.api.Subject; @@ -30,6 +32,10 @@ public final class Application { public static final String TITLE_DELIMITER = "¶"; public static final String TASK_TITLE_DELIMITER = "\\|"; public static final String TASK_DELIMITER = "¤"; + + public static final Logger LOGGER = LoggerFactory.getLogger(Application.class); + public static final Logger LOGGER_API = LoggerFactory.getLogger("de.igslandstuhl.database.api"); + private static Application instance = new Application(new String[] {"--test-environment", "true"}); public static Application getInstance() { return instance; @@ -89,7 +95,6 @@ public Topic[] readFile(String file) throws SerializationException, SQLException } } } catch (Throwable t) { - t.printStackTrace(); throw new SerializationException("Failed to read file", t); } @@ -98,15 +103,20 @@ public Topic[] readFile(String file) throws SerializationException, SQLException } public static void main(String[] args) throws Exception { + LOGGER.info("Starting up student-database..."); + instance = new Application(args); PluginLoader.getInstance().preloadPlugins(); if (!getInstance().suppressCmd()) { + LOGGER.info("Setting up command line..."); Command.registerCommands(); CommandLineUtils.setup(); } - + + LOGGER.info("Setting up server..."); + Server.getInstance().getConnection().createTables(); Holiday.setupCurrentSchoolYear(); @@ -119,14 +129,17 @@ public static void main(String[] args) throws Exception { GetRequestHandler.getInstance().registerHandlers(); if (getInstance().runsWebServer()) { + LOGGER.info("Starting WebServer..."); Server.getInstance().getWebServer().start(); } PluginLoader.getInstance().enablePlugins(); + LOGGER.info("Adding shutdown hook for plugin cleanup..."); Runtime.getRuntime().addShutdownHook(new Thread(() -> PluginLoader.getInstance().unloadPlugins(),"Plugin cleanup thread")); try { + LOGGER.info("Starting main loop..."); while (true) { if (!getInstance().suppressCmd()) { CommandLineUtils.waitForCommandAndExec(); diff --git a/src/main/java/de/igslandstuhl/database/api/Admin.java b/src/main/java/de/igslandstuhl/database/api/Admin.java index cc1c69f..04b83a9 100644 --- a/src/main/java/de/igslandstuhl/database/api/Admin.java +++ b/src/main/java/de/igslandstuhl/database/api/Admin.java @@ -2,6 +2,7 @@ import java.sql.SQLException; +import de.igslandstuhl.database.Application; import de.igslandstuhl.database.server.Server; import de.igslandstuhl.database.server.sql.SQLHelper; @@ -61,7 +62,7 @@ public static Admin get(String username) { try { return Server.getInstance().processSingleRequest(Admin::fromSQL, "get_admin_by_username", SQL_FIELDS, username); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to retrieve Admin user '{}' from database", username, e); return null; } } diff --git a/src/main/java/de/igslandstuhl/database/api/SchoolClass.java b/src/main/java/de/igslandstuhl/database/api/SchoolClass.java index 90485d9..73ffd24 100644 --- a/src/main/java/de/igslandstuhl/database/api/SchoolClass.java +++ b/src/main/java/de/igslandstuhl/database/api/SchoolClass.java @@ -8,6 +8,7 @@ import java.util.Objects; import java.util.stream.Collectors; +import de.igslandstuhl.database.Application; import de.igslandstuhl.database.server.Server; import de.igslandstuhl.database.server.sql.SQLHelper; @@ -157,7 +158,7 @@ public List getStudents() { "get_students_by_class", new String[] {"id"}, String.valueOf(id) ); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to retrieve student list from database", e); } return studentIds.stream().map(Student::get).toList(); } @@ -168,7 +169,6 @@ public void delete() throws SQLException { try { s.delete(); } catch (SQLException e) { - e.printStackTrace(); throw new IllegalStateException(e); } }); @@ -214,7 +214,7 @@ public static SchoolClass get(int id) { try { return Server.getInstance().processSingleRequest(SchoolClass::fromSQL, "get_class_by_id", SQL_FIELDS, String.valueOf(id)); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get SchoolClass with id {} from database", id, e); return null; } } @@ -229,7 +229,7 @@ public static SchoolClass get(String label) { try { return Server.getInstance().processSingleRequest(SchoolClass::fromSQL, "get_class_by_label", SQL_FIELDS, label); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get SchoolClass with label '{}' from database", label, e); return null; } } @@ -254,7 +254,7 @@ public static SchoolClass getOrCreate(String label) { try { schoolClass = addClass(label, grade); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to create previously not existing class with label {} and grade {}", label, grade, e); } } return schoolClass; @@ -268,7 +268,7 @@ public static List getAll() { "get_all_classes", new String[] {"id"} ); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to retrieve a list of all classes from the database", e); } return ids.stream() .map(SchoolClass::get) diff --git a/src/main/java/de/igslandstuhl/database/api/SchoolYear.java b/src/main/java/de/igslandstuhl/database/api/SchoolYear.java index 56d1002..6901840 100644 --- a/src/main/java/de/igslandstuhl/database/api/SchoolYear.java +++ b/src/main/java/de/igslandstuhl/database/api/SchoolYear.java @@ -2,6 +2,8 @@ import java.sql.SQLException; import java.util.*; + +import de.igslandstuhl.database.Application; import de.igslandstuhl.database.server.Server; import de.igslandstuhl.database.server.sql.SQLHelper; @@ -124,7 +126,7 @@ public static SchoolYear get(int id) { if (year != null) years.put(id, year); return year; } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get SchoolYear with id {} from database", id, e); return null; } } @@ -134,7 +136,7 @@ public static SchoolYear get(String label) { return get(Integer.parseInt(fields[0])); }, "get_school_year_by_label", new String[] {"id"}, label); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get SchoolYear with label '{}' from database", label, e); return null; } } @@ -156,7 +158,7 @@ public static List getAll() { "get_all_school_years", SQL_FIELDS ); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to retrieve a list of all school years from the database", e); } return all; } @@ -176,7 +178,7 @@ public static SchoolYear getCurrentYear() { if (year != null) years.put(year.getId(), year); return year; } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to load current school year from database", e); return null; } } diff --git a/src/main/java/de/igslandstuhl/database/api/SpecialTask.java b/src/main/java/de/igslandstuhl/database/api/SpecialTask.java index c199912..709c0cf 100644 --- a/src/main/java/de/igslandstuhl/database/api/SpecialTask.java +++ b/src/main/java/de/igslandstuhl/database/api/SpecialTask.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; +import de.igslandstuhl.database.Application; import de.igslandstuhl.database.server.Server; import de.igslandstuhl.database.server.sql.SQLHelper; @@ -89,7 +90,7 @@ public static SpecialTask get(int id) { specialTasks.put(id, task); return task; } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get SpecialTask with id {} from database", id, e); return null; } } @@ -114,7 +115,7 @@ public static List getSpecialTasksByName(String name) { try { Server.getInstance().processRequest(SpecialTask::addToCache, "get_special_tasks_by_name", SQL_FIELDS, name); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get SpecialTask with name {} from database", name, e); return new ArrayList<>(); } return specialTasks.values().stream() diff --git a/src/main/java/de/igslandstuhl/database/api/Student.java b/src/main/java/de/igslandstuhl/database/api/Student.java index 933b89b..f7c336f 100644 --- a/src/main/java/de/igslandstuhl/database/api/Student.java +++ b/src/main/java/de/igslandstuhl/database/api/Student.java @@ -13,6 +13,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import de.igslandstuhl.database.Application; import de.igslandstuhl.database.api.results.StudentGenerationResult; import de.igslandstuhl.database.server.Server; import de.igslandstuhl.database.server.sql.SQLHelper; @@ -169,7 +170,7 @@ public static Student get(int id) { student.fetchTasks(); return student; } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get Student with id {} from database", id, e); return null; } } @@ -190,7 +191,7 @@ public static Student getByEmail(String email) { } catch (NullPointerException e) { return null; } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get Student with email {} from database", email, e); return null; } } @@ -210,7 +211,7 @@ public static List getAll() { "get_all_students", SQL_FIELDS ); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to retrieve student list from database", e); } return studentIDs.stream() .map(Student::get) @@ -247,7 +248,7 @@ public static String[] generatePasswords(int count, int length) { try { Thread.sleep(new Random().nextInt(1,10)); // Sleep to ensure different seeds } catch (InterruptedException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Thread sleep while generating passwords was interrupted", e); } } return passwords; @@ -581,7 +582,7 @@ private void loadCurrentTopics() { } } } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to load current topics for '{}'", this.email, e); } } diff --git a/src/main/java/de/igslandstuhl/database/api/Subject.java b/src/main/java/de/igslandstuhl/database/api/Subject.java index cffbc48..ac92e36 100644 --- a/src/main/java/de/igslandstuhl/database/api/Subject.java +++ b/src/main/java/de/igslandstuhl/database/api/Subject.java @@ -9,6 +9,7 @@ import java.util.Objects; import java.util.stream.Collectors; +import de.igslandstuhl.database.Application; import de.igslandstuhl.database.server.Server; import de.igslandstuhl.database.server.sql.SQLHelper; @@ -94,7 +95,7 @@ public static Subject get(int id) { subjects.put(id, subject); return subject; } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get Subject with id {} from database", id, e); return null; } } @@ -116,7 +117,7 @@ public static Subject get(String name) { subjects.put(subject.getId(), subject); return subject; } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get Subject with name {} from database", name, e); return null; } } @@ -137,7 +138,7 @@ public static List getAll() { SQL_FIELDS ); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to retrieve subject list from database", e); } return subjectIds.stream() .map(Subject::get) @@ -199,7 +200,7 @@ public List getTopics(int grade) { String.valueOf(id) ); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to retrieve topic list for subject '{}' in grade {} from database", this.name, grade, e); } return topicIds.stream() .map(Topic::get) @@ -218,7 +219,7 @@ public int[] getGrades() { new String[] {"grade"}, String.valueOf(id)); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to retrieve list of available grades for subject '{}' from database", this.name, e); } return grades.stream().mapToInt(Integer::intValue).toArray(); } @@ -242,8 +243,7 @@ public void delete() throws SQLException { try { t.delete(); } catch (SQLException e) { - e.printStackTrace(); - throw new IllegalStateException(e); + throw new IllegalStateException("Failed to delete topic " + t.getName() + " which is necessary to delete subject " + this.name, e); } })); } catch (IllegalStateException e) { diff --git a/src/main/java/de/igslandstuhl/database/api/Task.java b/src/main/java/de/igslandstuhl/database/api/Task.java index b4188c8..9460245 100644 --- a/src/main/java/de/igslandstuhl/database/api/Task.java +++ b/src/main/java/de/igslandstuhl/database/api/Task.java @@ -180,7 +180,7 @@ public static Task get(int id) { tasks.put(id, task); return task; } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get Task with id {} from database", id, e); return null; } } @@ -194,7 +194,7 @@ public static List getByName(String name) { String[][] table = Server.getInstance().processRequest("get_tasks_by_name", new String[] {"id"}, name); Arrays.stream(table).map(s -> s[0]).map(Integer::parseInt).map(Task::get).forEach((t) -> t.getId()); // Do something because streams are lazy } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get Task with name {} from database", name, e); return new ArrayList<>(); } return tasks.values().stream() diff --git a/src/main/java/de/igslandstuhl/database/api/Teacher.java b/src/main/java/de/igslandstuhl/database/api/Teacher.java index db39ccc..1c617b2 100644 --- a/src/main/java/de/igslandstuhl/database/api/Teacher.java +++ b/src/main/java/de/igslandstuhl/database/api/Teacher.java @@ -4,6 +4,7 @@ import java.util.*; import java.util.stream.Collectors; +import de.igslandstuhl.database.Application; import de.igslandstuhl.database.api.results.TeacherGenerationResult; import de.igslandstuhl.database.server.Server; import de.igslandstuhl.database.server.sql.SQLHelper; @@ -200,7 +201,7 @@ public static Teacher get(int id) { teachersByEmail.put(teacher.getEmail(), teacher); return teacher; } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get Teacher with id {} from database", id, e); return null; } } @@ -223,7 +224,7 @@ public static Teacher fromEmail(String email) { teachersByEmail.put(email, teacher); return teacher; } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get Teacher with email {} from database", email, e); return null; } } @@ -243,7 +244,7 @@ public static List getAll() { "get_all_teachers", SQL_FIELDS ); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to retrieve teacher list from database", e); } return teacherIDs.stream() .map(Teacher::get) @@ -259,7 +260,7 @@ public List getSubjects() { "get_subjects_by_teacher", Subject.SQL_FIELDS, String.valueOf(id) ); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to retrieve subject list for teacher '{}' from database", this.email, e); } return subjectIds.stream() .map(Subject::get) @@ -312,7 +313,7 @@ public static TeacherGenerationResult[] generateTeachersFromCSV(String csv) thro try { Thread.sleep(new Random().nextInt(100)); // Sleep to ensure unique passwords } catch (InterruptedException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Thread sleep while generating passwords was interrupted", e); } } @@ -337,7 +338,7 @@ private void loadClasses() { "get_teacher_classes", CLASS_FIELDS, String.valueOf(id) ); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to retrieve class list of teacher '{}' from database", this.email, e); } } diff --git a/src/main/java/de/igslandstuhl/database/api/Topic.java b/src/main/java/de/igslandstuhl/database/api/Topic.java index e56d764..ceb5892 100644 --- a/src/main/java/de/igslandstuhl/database/api/Topic.java +++ b/src/main/java/de/igslandstuhl/database/api/Topic.java @@ -120,7 +120,7 @@ public static Topic get(int id) { topics.put(id, topic); return topic; } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get Topic with id {} from database", id, e); return null; } } @@ -271,7 +271,7 @@ private void loadTasks() { tasksLevel2 = getTasksByLevel(tasks, TaskLevel.LEVEL2); tasksLevel3 = getTasksByLevel(tasks, TaskLevel.LEVEL3); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get task list for topic '{}' from database", this.name, e); } } private static void addToCache(String[] fields) { @@ -287,7 +287,7 @@ public static List getByName(String name) { try { Server.getInstance().processRequest(Topic::addToCache, "get_topics_by_name", SQL_FIELDS, name); } catch (SQLException e) { - e.printStackTrace(); + Application.LOGGER_API.error("Failed to get Topic with name '{}' from database", name, e); return new ArrayList<>(); } return topics.values().stream() @@ -399,6 +399,7 @@ public boolean equals(Object obj) { return true; } public static Topic fromSerialized(String serialized, Subject subject, int grade, int number) throws SerializationException, SQLException { + Application.LOGGER_API.debug("Reading topic from serialized...");; // Gathering general info (topic name and ratio) String[] parts = serialized.split(Application.TITLE_DELIMITER); String[] generalInfo = parts[0].split(Application.TASK_DELIMITER); @@ -427,7 +428,7 @@ public static Topic fromSerialized(String serialized, Subject subject, int grade } topic.loadTasks(); - System.out.println(topic.getTasks()); + Application.LOGGER_API.debug("Loaded tasks for topic {}: {}", topic.getName(), topic.getTasks()); } return topic; } diff --git a/src/main/java/de/igslandstuhl/database/client/HTMLTemplate.java b/src/main/java/de/igslandstuhl/database/client/HTMLTemplate.java index 8b20614..32516bd 100644 --- a/src/main/java/de/igslandstuhl/database/client/HTMLTemplate.java +++ b/src/main/java/de/igslandstuhl/database/client/HTMLTemplate.java @@ -3,6 +3,9 @@ import java.io.FileNotFoundException; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import de.igslandstuhl.database.Registry; import de.igslandstuhl.database.client.dynamic.DynamicFieldType; import de.igslandstuhl.database.client.dynamic.DynamicHTMLTemplate; @@ -14,12 +17,14 @@ import de.igslandstuhl.database.server.resources.ResourceLocation; public interface HTMLTemplate { + public static final Logger LOGGER = LoggerFactory.getLogger(HTMLTemplate.class); public static final ResourceLocation meta = new ResourceLocation("meta", "templates", "templates.json"); public String fill(Map args); private static void register(HTMLTemplate template, String key) { Registry.templateRegistry().register(key, template); } public static void registerAll() { + LOGGER.info("Registering HTML templates..."); NavigationElement.registerAll(); DynamicHTMLTemplate.registerDynamicElements(); Map json = Server.getInstance().getResourceManager().readJsonResourceMerged(meta); @@ -33,8 +38,7 @@ public static void registerAll() { try { register(new HTMLFileTemplate((String) template.get("path")), key); } catch (FileNotFoundException e) { - System.err.println("Failed to load html template " + key); - e.printStackTrace(); + LOGGER.error("Failed to load html template '{}'", key, e); } break; case "HTMLNavigationTemplate": diff --git a/src/main/java/de/igslandstuhl/database/client/dynamic/DynamicHTMLTemplate.java b/src/main/java/de/igslandstuhl/database/client/dynamic/DynamicHTMLTemplate.java index e425e1a..133f14c 100644 --- a/src/main/java/de/igslandstuhl/database/client/dynamic/DynamicHTMLTemplate.java +++ b/src/main/java/de/igslandstuhl/database/client/dynamic/DynamicHTMLTemplate.java @@ -22,8 +22,7 @@ public String fill(Map args) { try { return TemplatingPreprocessor.getInstance().executeTemplating(arg0); } catch (IOException e) { - System.err.println("Failed filling template " + arg0); - e.printStackTrace(); + HTMLTemplate.LOGGER.error("Failed filling template '{}'", arg0, e); return ""; } }) diff --git a/src/main/java/de/igslandstuhl/database/holidays/Holiday.java b/src/main/java/de/igslandstuhl/database/holidays/Holiday.java index 66ff791..91ff9d5 100644 --- a/src/main/java/de/igslandstuhl/database/holidays/Holiday.java +++ b/src/main/java/de/igslandstuhl/database/holidays/Holiday.java @@ -16,6 +16,9 @@ import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -25,6 +28,7 @@ public final class Holiday { private static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient(); private static final String API_URL = "https://www.mehr-schulferien.de/api/v2.1/schools/66849-integrierte-gesamtschule-am-na/periods"; private static final String SUMMER_HOLIDAY_ID = "Sommer"; + private static final Logger LOGGER = LoggerFactory.getLogger(Holiday.class); private final int id; private final String name; @@ -138,7 +142,6 @@ public static Holiday[] holidaysInterval(Instant start, Instant end) { } catch (URISyntaxException | IOException e) { throw new IllegalStateException(e); } catch (InterruptedException e) { - e.printStackTrace(); throw new IllegalStateException(e); } } @@ -167,6 +170,7 @@ public static int getActualWeek() { return (int) SchoolWeek.getAll(getLastSummerHoliday().getEnd(), Instant.now(), ZoneId.of("UTC")).stream().filter(SchoolWeek::noSchoolUTC).count(); } public static void setupCurrentSchoolYear() throws SQLException { + LOGGER.info("Trying to set up current school year..."); String name = getLastSummerHoliday().getEnd().toString().substring(0, 4) + "/" + getNextSummerHoliday().getStart().toString().substring(0, 4); int totalWeeks = getTotalWeeks(); int actualWeek = getActualWeek(); diff --git a/src/main/java/de/igslandstuhl/database/plugins/Plugin.java b/src/main/java/de/igslandstuhl/database/plugins/Plugin.java index 08c8317..0cbac36 100644 --- a/src/main/java/de/igslandstuhl/database/plugins/Plugin.java +++ b/src/main/java/de/igslandstuhl/database/plugins/Plugin.java @@ -2,6 +2,9 @@ import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import de.igslandstuhl.database.plugins.config.PluginConfig; import de.igslandstuhl.database.plugins.config.PluginSetting; @@ -86,6 +89,10 @@ void load() { onLoad(); } + public Logger getLogger() { + return LoggerFactory.getLogger(id); + } + static class DummyModule extends Plugin { private final PluginConfig config; public DummyModule(String id, String name, String description, List> settings) { diff --git a/src/main/java/de/igslandstuhl/database/plugins/PluginLoader.java b/src/main/java/de/igslandstuhl/database/plugins/PluginLoader.java index 85bf0a3..6f6a070 100644 --- a/src/main/java/de/igslandstuhl/database/plugins/PluginLoader.java +++ b/src/main/java/de/igslandstuhl/database/plugins/PluginLoader.java @@ -1,6 +1,7 @@ package de.igslandstuhl.database.plugins; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; @@ -13,11 +14,14 @@ import java.util.Map; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; import de.igslandstuhl.database.Registry; public class PluginLoader { + public static final Logger LOGGER = LoggerFactory.getLogger(PluginLoader.class); private final List pluginInfos = new ArrayList<>(); public List getPluginInfos() { return pluginInfos; @@ -35,7 +39,7 @@ private Map loadYaml(URLClassLoader classLoader) { Yaml yaml = new Yaml(); return yaml.load(is); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error("Failed loading yaml from classLoader {}", classLoader.getName()); return null; } } @@ -52,22 +56,22 @@ public PreLoadedPlugin loadPluginFromJar(File jarFile) { null ); } catch (MalformedURLException e) { - e.printStackTrace(); + LOGGER.error("URL of {} corrupted", jarFile.getName(), e); return null; } try { Map yaml = loadYaml(classLoader); if (yaml == null) { - System.err.println("No plugin.yml found in " + jarFile.getName()); - classLoader.close(); - return null; + LOGGER.error("No plugin.yml found in {}", jarFile.getName()); + throw new FileNotFoundException("No plugin.yml found"); } String mainClassName = (String) yaml.get("main"); String id = (String) yaml.get("id"); if (id == null || mainClassName == null) { - throw new IllegalStateException("Invalid plugin.yml in " + jarFile.getName() + ": you must define id and main"); + LOGGER.error("Invalid plugin.yml in {}: you must define id and main", jarFile.getName()); + throw new IllegalArgumentException("Invalid plugin.yml"); } String name = (String) yaml.getOrDefault("name", id); String description = (String) yaml.getOrDefault("description", ""); @@ -86,18 +90,18 @@ public PreLoadedPlugin loadPluginFromJar(File jarFile) { Class clazz = classLoader.loadClass(mainClassName); if (!Plugin.class.isAssignableFrom(clazz)) { - throw new IllegalStateException("Main class does not extend Plugin"); + LOGGER.error("{}, the main class of {} does not extend Plugin", clazz.getCanonicalName(), jarFile.getName()); + throw new ClassCastException("Plugin main class does not extend Plugin"); } return new PreLoadedPlugin(new PluginDescription(id, name, description, mainClassName, depends), clazz, classLoader, resourceLoader); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error("Failed to preload plugin {}", jarFile.getName(), e); try { classLoader.close(); } catch (IOException e1) { - System.out.println("FAILED to close class loader"); - e1.printStackTrace(); + LOGGER.error("Failed to close classloader for incomplete plugin {}", jarFile.getName(), e1); } return null; } @@ -110,13 +114,13 @@ public void load(PreLoadedPlugin preload) { registerPlugin(plugin); plugin.load(); if (plugin.getConfig() == null) { + LOGGER.error("Plugin '{}' does not have a config", preload.description().id()); throw new NullPointerException("Plugin must have a config"); } } catch (Exception e) { - System.err.println("Failed to load plugin: " + preload.description().id()); + LOGGER.error("Failed to load plugin '{}'", preload.description().id(), e); pluginInfos.remove(preload); if (Registry.pluginRegistry().get(preload.description().id()) != null) Registry.pluginRegistry().unregister(preload.description().id()); - e.printStackTrace(); } } public void preloadAllPlugins(File folder) { @@ -136,7 +140,8 @@ public void preloadAllPlugins(File folder) { Set ids = new HashSet<>(); for (PreLoadedPlugin p : plugins) { if (!ids.add(p.description().id())) { - throw new IllegalStateException("Duplicate module id: " + p.description().id()); + LOGGER.error("Duplicate plugin id '{}', aborting", p.description().id()); + throw new IllegalStateException("Duplicate plugin id"); } } @@ -146,6 +151,7 @@ public void loadAllPlugins(File folder) { pluginInfos.forEach(this::load); } public void enablePlugins() { + LOGGER.info("Enabling plugins..."); pluginInfos.forEach((p) -> { Plugin plugin = Registry.pluginRegistry().get(p.description().id()); if (plugin.getConfig().isEnabledOnStart()) { @@ -154,6 +160,7 @@ public void enablePlugins() { }); } public void unloadPlugins() { + LOGGER.info("Unloading plugins..."); Collections.reverse(pluginInfos); pluginInfos.forEach((p) -> { Plugin plugin = Registry.pluginRegistry().get(p.description().id()); @@ -167,7 +174,7 @@ public void unloadPlugins() { p.classLoader().close(); p.resourceLoader().close(); } catch (IOException e) { - throw new RuntimeException("Problem while unloading", e); + LOGGER.error("Failed to unload plugin '{}'", plugin.getId()); } }); pluginInfos.clear(); @@ -182,9 +189,11 @@ private void registerPlugin(Plugin plugin) { Registry.pluginRegistry().register(plugin.getId(), plugin); } public void preloadPlugins() { + LOGGER.info("Preloading plugins from directory \"plugins\"..."); preloadAllPlugins(new File("plugins")); } public void registerPlugins() { + LOGGER.info("Registering plugins from directory \"plugins\"..."); loadAllPlugins(new File("plugins")); } } diff --git a/src/main/java/de/igslandstuhl/database/plugins/PluginResourceProvider.java b/src/main/java/de/igslandstuhl/database/plugins/PluginResourceProvider.java index 77f46fc..b502ff2 100644 --- a/src/main/java/de/igslandstuhl/database/plugins/PluginResourceProvider.java +++ b/src/main/java/de/igslandstuhl/database/plugins/PluginResourceProvider.java @@ -14,6 +14,7 @@ import de.igslandstuhl.database.server.resources.CoreResourceProvider; import de.igslandstuhl.database.server.resources.ResourceLocation; +import de.igslandstuhl.database.server.resources.ResourceManager; import de.igslandstuhl.database.server.resources.ResourceProvider; public class PluginResourceProvider implements ResourceProvider { @@ -57,8 +58,8 @@ public Collection list(Pattern pattern) { // Virtual root – no real filesystem access needed final Path virtualRoot = Paths.get("").toAbsolutePath().normalize(); - for (PreLoadedPlugin module : PluginLoader.getInstance().getPluginInfos()) { - try (ZipFile zip = new ZipFile(new File(module.resourceLoader().getURLs()[0].toURI()))) { + for (PreLoadedPlugin plugin : PluginLoader.getInstance().getPluginInfos()) { + try (ZipFile zip = new ZipFile(new File(plugin.resourceLoader().getURLs()[0].toURI()))) { Enumeration entries = zip.entries(); @@ -80,7 +81,7 @@ public Collection list(Pattern pattern) { } } catch (Exception e) { - e.printStackTrace(); + ResourceManager.LOGGER.error("Failed to get resource locations of pattern {} from plugin '{}'", pattern.pattern(), plugin.description().id()); } } diff --git a/src/main/java/de/igslandstuhl/database/plugins/config/PluginConfig.java b/src/main/java/de/igslandstuhl/database/plugins/config/PluginConfig.java index 61c88c3..b0735f6 100644 --- a/src/main/java/de/igslandstuhl/database/plugins/config/PluginConfig.java +++ b/src/main/java/de/igslandstuhl/database/plugins/config/PluginConfig.java @@ -12,6 +12,7 @@ import com.google.gson.JsonObject; import de.igslandstuhl.database.plugins.Plugin; +import de.igslandstuhl.database.plugins.PluginLoader; public abstract class PluginConfig { private final T plugin; @@ -108,7 +109,7 @@ public void save() { Gson gson = new GsonBuilder().setPrettyPrinting().create(); gson.toJson(root, writer); } catch (IOException e) { - e.printStackTrace(); + PluginLoader.LOGGER.error("Failed to save plugin config for {}", plugin.getId(), e); } } public void load() { @@ -128,7 +129,7 @@ public void load() { enabledOnStart = root.get("enabled").getAsBoolean(); } catch (IOException e) { - e.printStackTrace(); + PluginLoader.LOGGER.error("Failed to load plugin config for {}", plugin.getId(), e); } } } \ No newline at end of file diff --git a/src/main/java/de/igslandstuhl/database/server/Server.java b/src/main/java/de/igslandstuhl/database/server/Server.java index df4aafc..4901f49 100644 --- a/src/main/java/de/igslandstuhl/database/server/Server.java +++ b/src/main/java/de/igslandstuhl/database/server/Server.java @@ -9,6 +9,8 @@ import java.util.function.Function; import org.apache.commons.codec.digest.DigestUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import de.igslandstuhl.database.Application; import de.igslandstuhl.database.api.User; @@ -79,6 +81,8 @@ public ResourceManager getResourceManager() { return resourceManager; } + public static final Logger LOGGER = LoggerFactory.getLogger(Server.class); + /** * Private constructor to initialize the server instance. * This constructor sets up the database connection and initializes the web server. @@ -151,15 +155,15 @@ public void processRequest(Consumer callback, String request, String[] callback.accept(results.toArray(resultArr)); } } catch (SQLException e) { - e.printStackTrace(); + LOGGER.error("Failed to process sql request {} with args {}", request, args, e); throw new IllegalStateException(e); } - }); + }, "SQL Request Subroutine"); subroutine.start(); try { subroutine.join(); } catch (InterruptedException e) { - e.printStackTrace(); + LOGGER.error("Request subroutine was interrupted", e); throw new IllegalStateException(e); } } finally { diff --git a/src/main/java/de/igslandstuhl/database/server/WebServer.java b/src/main/java/de/igslandstuhl/database/server/WebServer.java index 7091055..570f317 100644 --- a/src/main/java/de/igslandstuhl/database/server/WebServer.java +++ b/src/main/java/de/igslandstuhl/database/server/WebServer.java @@ -9,6 +9,10 @@ import java.nio.charset.StandardCharsets; import javax.net.ssl.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -40,6 +44,7 @@ public class WebServer implements Runnable { public static final int SESSION_DURATION = 21600; // six hours public static final int MAXIMUM_INACTIVITY_DURATION = 3600; // An hour public static final int RATELIMIT = 60; + public static final Logger LOGGER = LoggerFactory.getLogger(Server.class); private volatile boolean running; private final SSLServerSocket serverSocket; @@ -112,7 +117,7 @@ public void run() { } } } catch (Exception e) { - e.printStackTrace(); + LOGGER.error("Failed to handle client {}", clientIp, e); } finally { try { clientSocket.close(); } catch (IOException ignored) {} } @@ -233,7 +238,7 @@ public void start() { public void stop() { running = false; - try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } + try { serverSocket.close(); } catch (IOException e) { LOGGER.error("Failed to close server socket", e); } clientPool.shutdownNow(); } @@ -245,8 +250,7 @@ public void run() { clientPool.submit(new ClientHandler(clientSocket)); } catch (IOException e) { if (running) { - System.err.println("Error while accepting client"); - e.printStackTrace(); + LOGGER.error("Unexpected Exception while accepting client", e); } } } diff --git a/src/main/java/de/igslandstuhl/database/server/commands/Command.java b/src/main/java/de/igslandstuhl/database/server/commands/Command.java index 38469b1..6b07495 100644 --- a/src/main/java/de/igslandstuhl/database/server/commands/Command.java +++ b/src/main/java/de/igslandstuhl/database/server/commands/Command.java @@ -5,6 +5,9 @@ import java.util.List; import java.util.Random; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import de.igslandstuhl.database.Registry; import de.igslandstuhl.database.api.*; import de.igslandstuhl.database.server.Server; @@ -12,6 +15,7 @@ @FunctionalInterface public interface Command { + public static final Logger LOGGER = LoggerFactory.getLogger(Command.class); public String execute(String[] args); public default CommandDescription getDescription() { return Registry.commandDescriptionRegistry().get(Registry.commandDescriptionRegistry() @@ -25,7 +29,7 @@ public static String executeCommand(String command, String[] args) { } catch (NullPointerException e) { return "Command not found: " + command; } catch (Exception e) { - e.printStackTrace(); + LOGGER.error("Failed to execute command '{}'", command, e); return ""; } } @@ -34,8 +38,9 @@ public static void registerCommand(String name, Command command, CommandDescript Registry.commandDescriptionRegistry().register(name, description); } public static void registerCommands() { + LOGGER.info("Registering commands..."); registerCommand("exit", (args) -> { - System.out.println("Exiting..."); + LOGGER.info("Program exit through command;exiting..."); System.exit(0); return ""; }, new CommandDescription("exit", "Exits the application", "exit")); diff --git a/src/main/java/de/igslandstuhl/database/server/resources/CoreResourceProvider.java b/src/main/java/de/igslandstuhl/database/server/resources/CoreResourceProvider.java index 1c67d53..005b25c 100644 --- a/src/main/java/de/igslandstuhl/database/server/resources/CoreResourceProvider.java +++ b/src/main/java/de/igslandstuhl/database/server/resources/CoreResourceProvider.java @@ -119,7 +119,7 @@ private Collection getResourcesFromDirectory(final Path direct } }); } catch (IOException e) { - e.printStackTrace(); + ResourceManager.LOGGER.error("Failed to get resources from directory '{}'", directory.toString(), e); return retval; } return retval; diff --git a/src/main/java/de/igslandstuhl/database/server/resources/FileResourceProvider.java b/src/main/java/de/igslandstuhl/database/server/resources/FileResourceProvider.java index 7c2f9c5..53de6d3 100644 --- a/src/main/java/de/igslandstuhl/database/server/resources/FileResourceProvider.java +++ b/src/main/java/de/igslandstuhl/database/server/resources/FileResourceProvider.java @@ -58,7 +58,7 @@ public Collection list(Pattern pattern) { } }); } catch (IOException e) { - e.printStackTrace(); + ResourceManager.LOGGER.error("Failed to get resource locations of pattern {} from file root '{}'", pattern.pattern(), root, e); } return result; diff --git a/src/main/java/de/igslandstuhl/database/server/resources/ResourceManager.java b/src/main/java/de/igslandstuhl/database/server/resources/ResourceManager.java index 567495c..9f26871 100644 --- a/src/main/java/de/igslandstuhl/database/server/resources/ResourceManager.java +++ b/src/main/java/de/igslandstuhl/database/server/resources/ResourceManager.java @@ -19,6 +19,9 @@ import java.util.regex.Pattern; import java.util.stream.Stream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -29,6 +32,7 @@ * Manages Resources in the application */ public class ResourceManager { + public static final Logger LOGGER = LoggerFactory.getLogger(ResourceManager.class); private final List providers; public ResourceManager(ResourceProvider... providers) { this.providers = Arrays.asList(providers); diff --git a/src/main/java/de/igslandstuhl/database/server/sql/SQLMultipleAccessesException.java b/src/main/java/de/igslandstuhl/database/server/sql/SQLMultipleAccessesException.java index c4bdf58..97f54bb 100644 --- a/src/main/java/de/igslandstuhl/database/server/sql/SQLMultipleAccessesException.java +++ b/src/main/java/de/igslandstuhl/database/server/sql/SQLMultipleAccessesException.java @@ -2,7 +2,7 @@ public class SQLMultipleAccessesException extends RuntimeException { public SQLMultipleAccessesException() {} - public SQLMultipleAccessesException(String msg) { super(msg); System.out.println("Test");} + public SQLMultipleAccessesException(String msg) { super(msg); } public SQLMultipleAccessesException(Throwable cause) { super(cause); } public SQLMultipleAccessesException(String msg, Throwable cause) { super(msg, cause); } } diff --git a/src/main/java/de/igslandstuhl/database/server/sql/SQLiteConnection.java b/src/main/java/de/igslandstuhl/database/server/sql/SQLiteConnection.java index a39a389..7208b49 100644 --- a/src/main/java/de/igslandstuhl/database/server/sql/SQLiteConnection.java +++ b/src/main/java/de/igslandstuhl/database/server/sql/SQLiteConnection.java @@ -10,6 +10,9 @@ import java.sql.Statement; import java.util.regex.Pattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import de.igslandstuhl.database.server.Server; import de.igslandstuhl.database.utils.TrackingReadWriteLock; @@ -38,6 +41,8 @@ public Connection getSQLConnection() { private final TrackingReadWriteLock lock = new TrackingReadWriteLock(); + private final Logger LOGGER = LoggerFactory.getLogger(getClass()); + /** * Creates the necessary tables in the database by executing SQL scripts. * This method reads SQL files matching the pattern "./tables/*.sql" (regex: .*tables.+\\.sql) and executes their content. @@ -157,6 +162,7 @@ public void closePendingStatement() throws SQLException { * @throws SQLException if an SQL error occurs during table creation */ public void createTables() throws SQLException { + LOGGER.debug("Creating Database Tables..."); executeVoidProcessSecure(this::createTables); } diff --git a/src/main/java/de/igslandstuhl/database/server/webserver/AccessManager.java b/src/main/java/de/igslandstuhl/database/server/webserver/AccessManager.java index f287017..53ca04a 100644 --- a/src/main/java/de/igslandstuhl/database/server/webserver/AccessManager.java +++ b/src/main/java/de/igslandstuhl/database/server/webserver/AccessManager.java @@ -5,6 +5,9 @@ import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import de.igslandstuhl.database.Registry; import de.igslandstuhl.database.api.User; import de.igslandstuhl.database.server.Server; @@ -15,6 +18,7 @@ * It determines whether a user has access to a specific resource based on predefined rules. */ public class AccessManager { + private static final Logger LOGGER = LoggerFactory.getLogger(AccessManager.class); private static final AccessManager INSTANCE = new AccessManager(); public static AccessManager getInstance() { return INSTANCE; @@ -60,6 +64,7 @@ public static AccessManager getInstance() { @SuppressWarnings("unchecked") private AccessManager() { + LOGGER.info("Setting up AccessManager..."); ResourceLocation metaLocation = new ResourceLocation("meta", "paths", "spaces.json"); String userSpace = "user"; String teacherSpace = "teacher"; @@ -71,6 +76,7 @@ private AccessManager() { String[] teacherLocations = {}; String[] adminLocations = {"students", "teachers", "classes"}; try { + LOGGER.debug("Trying to read spaces metadata..."); Map pathData = Server.getInstance().getResourceManager().readJsonResourceAsMap(metaLocation); List publicSpacesList = (List) pathData.get("public_spaces"); List publicLocationsList = (List) pathData.get("public_locations"); @@ -86,8 +92,7 @@ private AccessManager() { teacherLocations = teacherLocationsList.toArray(new String[teacherLocationsList.size()]); adminLocations = adminLocationsList.toArray(new String[adminLocationsList.size()]); } catch (IOException e) { - System.err.println("Could not read spaces metadata!"); - e.printStackTrace(); + LOGGER.error("Could not read spaces metadata!", e); } finally { USER_SPACE = userSpace; TEACHER_SPACE = teacherSpace; diff --git a/src/main/java/de/igslandstuhl/database/server/webserver/WebPath.java b/src/main/java/de/igslandstuhl/database/server/webserver/WebPath.java index 3c11767..075401d 100644 --- a/src/main/java/de/igslandstuhl/database/server/webserver/WebPath.java +++ b/src/main/java/de/igslandstuhl/database/server/webserver/WebPath.java @@ -4,16 +4,22 @@ import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import de.igslandstuhl.database.Registry; import de.igslandstuhl.database.server.Server; import de.igslandstuhl.database.server.resources.ResourceLocation; import de.igslandstuhl.database.server.webserver.requests.RequestType; public record WebPath(RequestType type, String handlerType, List namespaces, String context, AccessLevel accessLevel) { + private static final Logger LOGGER = LoggerFactory.getLogger(WebPath.class); + public static void registerPath(String path, RequestType type, String handlerType, List namespaces, String context, AccessLevel accessLevel) { Registry.webPathRegistry().register(path, new WebPath(type, handlerType, namespaces, context, accessLevel)); } public static void registerPaths() throws IOException { + LOGGER.info("Registering get request paths..."); if (Registry.webPathRegistry().stream().count() > 0) return; // already registered ResourceLocation metaLocation = new ResourceLocation("meta", "paths", "get_paths.json"); Map pathData = Server.getInstance().getResourceManager().readJsonResourceMerged(metaLocation); diff --git a/src/main/java/de/igslandstuhl/database/server/webserver/handlers/GetRequestHandler.java b/src/main/java/de/igslandstuhl/database/server/webserver/handlers/GetRequestHandler.java index ff9e130..befe33a 100644 --- a/src/main/java/de/igslandstuhl/database/server/webserver/handlers/GetRequestHandler.java +++ b/src/main/java/de/igslandstuhl/database/server/webserver/handlers/GetRequestHandler.java @@ -2,6 +2,9 @@ import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import de.igslandstuhl.database.Registry; import de.igslandstuhl.database.api.User; import de.igslandstuhl.database.server.Server; @@ -15,6 +18,7 @@ import de.igslandstuhl.database.utils.ThrowingFunction; public class GetRequestHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(GetRequestHandler.class); private static final GetRequestHandler instance = new GetRequestHandler(); public static GetRequestHandler getInstance() { return instance; @@ -65,6 +69,7 @@ public static GetResponse handlePluginRequest(GetRequest request) { } public final void registerHandlers() { + LOGGER.info("Registering Get Request Handlers..."); if (Registry.getRequestHandlerRegistry().stream().count() > 0) return; // already registered List getPaths = Registry.webPathRegistry().keyStream().filter((p) -> Registry.webPathRegistry().get(p).type() == RequestType.GET).toList(); for (String path : getPaths) { diff --git a/src/main/java/de/igslandstuhl/database/server/webserver/handlers/HttpHandler.java b/src/main/java/de/igslandstuhl/database/server/webserver/handlers/HttpHandler.java index 2477732..d64604b 100644 --- a/src/main/java/de/igslandstuhl/database/server/webserver/handlers/HttpHandler.java +++ b/src/main/java/de/igslandstuhl/database/server/webserver/handlers/HttpHandler.java @@ -1,5 +1,8 @@ package de.igslandstuhl.database.server.webserver.handlers; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import de.igslandstuhl.database.Registry; import de.igslandstuhl.database.server.Server; import de.igslandstuhl.database.server.webserver.AccessLevel; @@ -12,6 +15,7 @@ import de.igslandstuhl.database.utils.ThrowingFunction; public class HttpHandler { + public static final Logger LOGGER = LoggerFactory.getLogger(HttpHandler.class); private final String path; private final AccessLevel accessLevel; private final ThrowingFunction handler; @@ -31,13 +35,13 @@ public HttpResponse handleHttpRequest(Rq request) { if (!accessLevel.hasAccess(sessionManager.getSessionUser(request))) { return HttpResponse.error(request, Status.UNAUTHORIZED); } else if (!path.equals(request.getPath().split("\\?")[0])) { - System.err.println("Wrong path for HTTP handler: " + handler + ", path: " + request.getPath()); + LOGGER.error("Wrong path for HTTP handler: path {} does not match handler path {}", request.getPath(), path); return HttpResponse.error(request, Status.INTERNAL_SERVER_ERROR); } else { try { return handler.apply(request); } catch (Throwable t) { - t.printStackTrace(); + LOGGER.error("Failed to apply HTTP handler", t); return HttpResponse.error(request, Status.INTERNAL_SERVER_ERROR); } } diff --git a/src/main/java/de/igslandstuhl/database/server/webserver/handlers/PostRequestHandler.java b/src/main/java/de/igslandstuhl/database/server/webserver/handlers/PostRequestHandler.java index 5bbb557..c9d5991 100644 --- a/src/main/java/de/igslandstuhl/database/server/webserver/handlers/PostRequestHandler.java +++ b/src/main/java/de/igslandstuhl/database/server/webserver/handlers/PostRequestHandler.java @@ -14,6 +14,8 @@ import org.owasp.html.PolicyFactory; import org.owasp.html.Sanitizers; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.gson.reflect.TypeToken; @@ -66,6 +68,8 @@ private PostRequestHandler() { // Private constructor to prevent instantiation } + private static final Logger LOGGER = LoggerFactory.getLogger(PostRequestHandler.class); + /** * Handles the POST request based on the path specified in the request. * It routes the request to the appropriate handler method based on the path. @@ -91,7 +95,7 @@ private static String prepare(String webInput, boolean sanitize) { try { webInput = URLDecoder.decode(webInput, StandardCharsets.UTF_8.name()); } catch (UnsupportedEncodingException e) { - e.printStackTrace(); + LOGGER.error("Encoding is not supported by URLDecoder", e); } ; if (sanitize) { @@ -166,6 +170,7 @@ public static PostResponse handleObjectAction(APIPostReque return successMessage; } public static void registerHandlers() { + LOGGER.info("Registering Post Request Handlers..."); HttpHandler.registerPostRequestHandler("/login", AccessLevel.PUBLIC, (rq) -> { String username = prepare(rq.getString("username"), false); // Do not sanitize / url-decode password to allow special characters like % diff --git a/src/main/java/de/igslandstuhl/database/server/webserver/handlers/get/SQLRequestHandler.java b/src/main/java/de/igslandstuhl/database/server/webserver/handlers/get/SQLRequestHandler.java index 391c9f4..37bd4e1 100644 --- a/src/main/java/de/igslandstuhl/database/server/webserver/handlers/get/SQLRequestHandler.java +++ b/src/main/java/de/igslandstuhl/database/server/webserver/handlers/get/SQLRequestHandler.java @@ -3,6 +3,9 @@ import java.util.HashSet; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import de.igslandstuhl.database.Registry; import de.igslandstuhl.database.api.SchoolClass; import de.igslandstuhl.database.api.Student; @@ -12,12 +15,15 @@ @FunctionalInterface public interface SQLRequestHandler { + public static final Logger LOGGER = LoggerFactory.getLogger(SQLRequestHandler.class); public String get(User user); public static String getResource(String resource, User user) { return Registry.sqlRequestHandlerRegistry().get(resource).get(user); } public static void register() { + LOGGER.info("Registering SQL request handlers..."); + Registry.sqlRequestHandlerRegistry().register("mydata", (user) -> user.toJSON()); Registry.sqlRequestHandlerRegistry().register("mysubjects", (user) -> { diff --git a/src/main/java/de/igslandstuhl/database/server/webserver/responses/GetResponse.java b/src/main/java/de/igslandstuhl/database/server/webserver/responses/GetResponse.java index 71d10da..7189ed0 100644 --- a/src/main/java/de/igslandstuhl/database/server/webserver/responses/GetResponse.java +++ b/src/main/java/de/igslandstuhl/database/server/webserver/responses/GetResponse.java @@ -11,6 +11,7 @@ import de.igslandstuhl.database.server.webserver.ContentType; import de.igslandstuhl.database.server.webserver.NoWebResourceException; import de.igslandstuhl.database.server.webserver.Status; +import de.igslandstuhl.database.server.webserver.handlers.HttpHandler; import de.igslandstuhl.database.server.webserver.handlers.get.PluginRequestHandler; import de.igslandstuhl.database.server.webserver.requests.HttpRequest; @@ -211,7 +212,7 @@ public void respond(PrintStream out) { } catch (FileNotFoundException e) { notFound(request).respond(out); } catch (Exception e) { - e.printStackTrace(); + HttpHandler.LOGGER.warn("Exception while trying to write output stream for get request {}", request, e); if (status != Status.INTERNAL_SERVER_ERROR) { internalServerError(request).respond(out); } else { diff --git a/src/main/java/de/igslandstuhl/database/server/webserver/responses/PostResponse.java b/src/main/java/de/igslandstuhl/database/server/webserver/responses/PostResponse.java index 344de23..9226521 100644 --- a/src/main/java/de/igslandstuhl/database/server/webserver/responses/PostResponse.java +++ b/src/main/java/de/igslandstuhl/database/server/webserver/responses/PostResponse.java @@ -12,6 +12,7 @@ import de.igslandstuhl.database.server.webserver.Cookie; import de.igslandstuhl.database.server.webserver.NoWebResourceException; import de.igslandstuhl.database.server.webserver.Status; +import de.igslandstuhl.database.server.webserver.handlers.HttpHandler; import de.igslandstuhl.database.server.webserver.requests.HttpRequest; import de.igslandstuhl.database.server.webserver.requests.PostRequest; @@ -165,7 +166,7 @@ public static PostResponse getResource(ResourceLocation resourceLocation, String } catch (FileNotFoundException e) { return notFound("The requested resource was not found: " + resourceLocation, request); } catch (Exception e) { - e.printStackTrace(); + HttpHandler.LOGGER.warn("Failed to get resource for request {} on resource location {}", request, resourceLocation); return internalServerError("An error occurred while processing your request.", request); } } diff --git a/src/main/java/de/igslandstuhl/database/server/webserver/sessions/SessionManager.java b/src/main/java/de/igslandstuhl/database/server/webserver/sessions/SessionManager.java index 4c226ee..dd5114c 100644 --- a/src/main/java/de/igslandstuhl/database/server/webserver/sessions/SessionManager.java +++ b/src/main/java/de/igslandstuhl/database/server/webserver/sessions/SessionManager.java @@ -5,12 +5,16 @@ import java.util.Map; import java.util.UUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import de.igslandstuhl.database.api.User; import de.igslandstuhl.database.server.webserver.Cookie; import de.igslandstuhl.database.server.webserver.handlers.SessionValidationResult; import de.igslandstuhl.database.server.webserver.requests.HttpRequest; public class SessionManager { + private static final Logger LOGGER = LoggerFactory.getLogger(SessionManager.class); private Map sessionStore = new HashMap<>(); /** * A map to store session IDs and their associated usernames. @@ -33,6 +37,7 @@ public SessionManager(int sessionExpireDuration, int maximumInactivityDuration, this.sessionExpireDuration = sessionExpireDuration; this.maximumInactivityDuration = maximumInactivityDuration; this.maxRequests = maxRequests; + LOGGER.debug("Starting session cleanup job..."); new Thread(this::cleanSecondsJob, "Session Expiring").start(); } @@ -63,7 +68,7 @@ private void cleanSecondsJob() { try { Thread.sleep(60000); } catch (InterruptedException e) { - e.printStackTrace(); + LOGGER.warn("Session manager cleanup job interrupted while sleeping", e); } } } @@ -76,18 +81,18 @@ public SessionValidationResult validateSession(HttpRequest request) { count++; requestCount.set(request, count); if (count > maxRequests && !getSessionUser(request).isAdmin()) { - System.out.println("Ratelimit!"); + LOGGER.warn("Needed to put {} under rate limit: {} requests of maximum {} allowed", getSessionUser(request).getUsername(), count, maxRequests); return SessionValidationResult.RATE_LIMITED; } String userAgent = request.getUserAgent(); if (!getSession(request).getUserAgent().equals(userAgent)) { - System.err.println("SEVERE WARNING: POTENTIAL ATTACK: faked session id (device changed), for user " + getSessionUser(request)); + LOGGER.warn("faked session id (device changed), for user {}" + getSessionUser(request)); return SessionValidationResult.INVALID_SESSION; } String ip = request.getIP(); if (!getSession(request).getIpAddress().equals(ip)) { - System.err.println("SEVERE WARNING: POTENTIAL ATTACK: faked session id (ip address changed) for user " + getSessionUser(request)); + LOGGER.warn("faked session id (ip address changed) for user {}" + getSessionUser(request)); return SessionValidationResult.INVALID_SESSION; } diff --git a/src/main/java/de/igslandstuhl/database/utils/CommandLineUtils.java b/src/main/java/de/igslandstuhl/database/utils/CommandLineUtils.java index 8f43174..7cb11b0 100644 --- a/src/main/java/de/igslandstuhl/database/utils/CommandLineUtils.java +++ b/src/main/java/de/igslandstuhl/database/utils/CommandLineUtils.java @@ -34,6 +34,6 @@ public static void waitForCommandAndExec() { String line = input(); String[] args = line.trim().split(" "); String command = args[0]; - System.out.println(Command.executeCommand(command, Arrays.copyOfRange(args, 1, args.length))); + Command.LOGGER.info("[Commands] {}", Command.executeCommand(command, Arrays.copyOfRange(args, 1, args.length))); } } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..5bd4cbc --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,48 @@ + + + + + + + + %yellow(%d{HH:mm:ss}) [%cyan(%thread)|%magenta(%logger{20})] %highlight([%level]: %msg%n%ex) + + + + + + + + logs/latest.log + + + + + logs/archive/app-%d{yyyy-MM-dd}.log + + + 30 + + + + + + %d{yyyy-MM-dd HH:mm:ss} + [%thread] + %-5level + %logger{36} + - %msg%n + + + + + + + + + + + \ No newline at end of file