From 478a8f22eeddb007bf84f692d22a7f8d74331359 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Fri, 28 Nov 2025 16:06:25 +0000 Subject: [PATCH 01/61] add deadline --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d20aaf9..7071577 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/339Lr3BJ) ### How the tests work (and Docker requirement) This project ships with an end‑to‑end CLI integration test suite that uses Testcontainers to spin up a temporary MySQL database. From eca520cb99c6e3dd50ea1dbf09fd098cacfbbdbf Mon Sep 17 00:00:00 2001 From: Kristina M Date: Fri, 28 Nov 2025 19:33:25 +0100 Subject: [PATCH 02/61] test first commit --- src/main/java/com/example/DevDatabaseInitializer.java | 2 +- src/main/java/com/example/Main.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/example/DevDatabaseInitializer.java b/src/main/java/com/example/DevDatabaseInitializer.java index e8a45fe..5526da9 100644 --- a/src/main/java/com/example/DevDatabaseInitializer.java +++ b/src/main/java/com/example/DevDatabaseInitializer.java @@ -21,4 +21,4 @@ public static void start() { System.setProperty("APP_DB_PASS", mysql.getPassword()); } } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 6dc6fbd..c45b9ce 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -59,4 +59,4 @@ private static String resolveConfig(String propertyKey, String envKey) { } return (v == null || v.trim().isEmpty()) ? null : v.trim(); } -} +} \ No newline at end of file From 2760978aed9094b891892dd4938823fdbf311566 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Fri, 28 Nov 2025 20:30:37 +0100 Subject: [PATCH 03/61] Moved test file to correct structure --- dependency-reduced-pom.xml | 117 ++++++++++++++++++ src/test/{ => java}/com/example/CliAppIT.java | 0 2 files changed, 117 insertions(+) create mode 100644 dependency-reduced-pom.xml rename src/test/{ => java}/com/example/CliAppIT.java (100%) diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml new file mode 100644 index 0000000..0c555fe --- /dev/null +++ b/dependency-reduced-pom.xml @@ -0,0 +1,117 @@ + + + 4.0.0 + com.example + jdbc + 1.0-SNAPSHOT + + + + maven-compiler-plugin + 3.13.0 + + ${maven.compiler.release} + + + + maven-surefire-plugin + 3.2.5 + + + maven-failsafe-plugin + 3.5.4 + + + integration-test + + integration-test + verify + + + + + + maven-shade-plugin + 3.5.0 + + + package + + shade + + + + + com.example.Main + + + + + + + + + + + org.junit.jupiter + junit-jupiter + 6.0.1 + test + + + junit-jupiter-api + org.junit.jupiter + + + junit-jupiter-params + org.junit.jupiter + + + junit-jupiter-engine + org.junit.jupiter + + + + + org.assertj + assertj-core + 3.27.6 + test + + + byte-buddy + net.bytebuddy + + + + + org.mockito + mockito-junit-jupiter + 5.20.0 + test + + + mockito-core + org.mockito + + + junit-jupiter-api + org.junit.jupiter + + + + + org.testcontainers + junit-jupiter + 1.21.3 + test + + + + 25 + UTF-8 + 5.20.0 + 6.0.1 + 3.27.6 + + diff --git a/src/test/com/example/CliAppIT.java b/src/test/java/com/example/CliAppIT.java similarity index 100% rename from src/test/com/example/CliAppIT.java rename to src/test/java/com/example/CliAppIT.java From 4c2fa50ab445b9fc0d856a80cef5b789c7f314da Mon Sep 17 00:00:00 2001 From: Kristina M Date: Fri, 28 Nov 2025 21:04:15 +0100 Subject: [PATCH 04/61] Enable dev mode and add test logging in Main --- src/main/java/com/example/Main.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index c45b9ce..9ffbdd5 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -7,7 +7,7 @@ public class Main { - static void main(String[] args) { + public static void main(String[] args) { if (isDevMode(args)) { DevDatabaseInitializer.start(); } @@ -20,6 +20,13 @@ public void run() { String dbUser = resolveConfig("APP_DB_USER", "APP_DB_USER"); String dbPass = resolveConfig("APP_DB_PASS", "APP_DB_PASS"); + // Kontrollera om dev-mode fungerar + System.out.println("Dev Mode Check"); + System.out.println("JDBC URL: " + jdbcUrl); + System.out.println("DB User: " + dbUser); + System.out.println("DB Password: " + dbPass); + System.out.println("======================"); + if (jdbcUrl == null || dbUser == null || dbPass == null) { throw new IllegalStateException( "Missing DB configuration. Provide APP_JDBC_URL, APP_DB_USER, APP_DB_PASS " + From 1815cd222a8c0b963de4c67c4cda03f3750d5b48 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Fri, 28 Nov 2025 21:55:42 +0100 Subject: [PATCH 05/61] Implement login loop in Main using System.in for input - validate username and password against the database - Fixed CLI so that the invalid login test passes --- src/main/java/com/example/Main.java | 61 +++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 9ffbdd5..2f1b7cd 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -1,8 +1,9 @@ package com.example; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; +import java.io.Console; +import java.io.IOException; +import java.io.InputStream; +import java.sql.*; import java.util.Arrays; public class Main { @@ -20,12 +21,13 @@ public void run() { String dbUser = resolveConfig("APP_DB_USER", "APP_DB_USER"); String dbPass = resolveConfig("APP_DB_PASS", "APP_DB_PASS"); - // Kontrollera om dev-mode fungerar + /* System.out.println("Dev Mode Check"); System.out.println("JDBC URL: " + jdbcUrl); System.out.println("DB User: " + dbUser); System.out.println("DB Password: " + dbPass); System.out.println("======================"); + */ if (jdbcUrl == null || dbUser == null || dbPass == null) { throw new IllegalStateException( @@ -38,8 +40,59 @@ public void run() { throw new RuntimeException(e); } //Todo: Starting point for your code + + boolean loggedIn = false; + InputStream in = System.in; + + while (!loggedIn) { + try { + System.out.print("Username: "); + String username = readLine(in); + + System.out.print("Password: "); + String password = readLine(in); + + try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass); + PreparedStatement ps = connection.prepareStatement( + "SELECT 1 FROM account WHERE name = ? AND password = ?")) { + + ps.setString(1, username); + ps.setString(2, password); + + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + loggedIn = true; + System.out.println("Login successful!"); + } else { + System.out.println("Invalid username or password"); + System.out.print("Press 0 to exit or enter to retry: "); + String exit = readLine(in); + if ("0".equals(exit)) return; + } + } + } + + } catch (SQLException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException("I/O error reading input", e); + } + } + } + + // Reads a line from System.in using Java 25 IO + private static String readLine(InputStream in) throws IOException { + byte[] buffer = new byte[1024]; + int len = 0; + int b; + while ((b = in.read()) != -1) { + if (b == '\n') break; + buffer[len++] = (byte) b; + } + return new String(buffer, 0, len).trim(); } + /** * Determines if the application is running in development mode based on system properties, * environment variables, or command-line arguments. From 66cca47c4f61e171e08bc606a6d2e4ac2e09cb94 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Fri, 28 Nov 2025 22:42:34 +0100 Subject: [PATCH 06/61] Add menu options to run method --- src/main/java/com/example/Main.java | 55 ++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 2f1b7cd..cd09a6c 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -1,6 +1,5 @@ package com.example; -import java.io.Console; import java.io.IOException; import java.io.InputStream; import java.sql.*; @@ -35,26 +34,21 @@ public void run() { "as system properties (-Dkey=value) or environment variables."); } - try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) { - } catch (SQLException e) { - throw new RuntimeException(e); - } - //Todo: Starting point for your code - - boolean loggedIn = false; InputStream in = System.in; - while (!loggedIn) { - try { + try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) { + + // LOGIN LOOP + boolean loggedIn = false; + while (!loggedIn) { System.out.print("Username: "); String username = readLine(in); System.out.print("Password: "); String password = readLine(in); - try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass); - PreparedStatement ps = connection.prepareStatement( - "SELECT 1 FROM account WHERE name = ? AND password = ?")) { + try (PreparedStatement ps = connection.prepareStatement( + "SELECT 1 FROM account WHERE name = ? AND password = ?")) { ps.setString(1, username); ps.setString(2, password); @@ -71,15 +65,42 @@ public void run() { } } } + } - } catch (SQLException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException("I/O error reading input", e); + boolean exit = false; + while (!exit) { + System.out.println("\nMenu:"); + System.out.println("1) List moon missions"); + System.out.println("2) Get a moon mission by mission_id"); + System.out.println("3) Count missions for a given year"); + System.out.println("0) Exit"); + System.out.print("Choose an option: "); + String choice = readLine(in); + + switch (choice) { + case "1": + listMoonMissions(connection); + break; + case "2": + getMoonMissionById(connection, in); + break; + case "3": + countMissionsByYear(connection, in); + break; + case "0": + exit = true; + break; + default: + System.out.println("Invalid option"); + } } + + } catch (SQLException | IOException e) { + throw new RuntimeException(e); } } + // Reads a line from System.in using Java 25 IO private static String readLine(InputStream in) throws IOException { byte[] buffer = new byte[1024]; From d0e7c92b778f4f993ec987d69c2a99f14784fd48 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Fri, 28 Nov 2025 22:54:29 +0100 Subject: [PATCH 07/61] add listMoonMissions method --- src/main/java/com/example/Main.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index cd09a6c..45c0793 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -100,6 +100,20 @@ public void run() { } } + // LIST MOON MISSIONS + private void listMoonMissions(Connection connection) throws SQLException { + String sql = "SELECT spacecraft FROM moon_mission"; + try (Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery(sql)) { + System.out.println("Moon Missions:"); + while (rs.next()) { + System.out.println(rs.getString("spacecraft")); + } + } + } + + + // Reads a line from System.in using Java 25 IO private static String readLine(InputStream in) throws IOException { From 008acb8e4a97f79056d116786f0c5a493eb8834c Mon Sep 17 00:00:00 2001 From: Kristina M Date: Fri, 28 Nov 2025 22:58:42 +0100 Subject: [PATCH 08/61] add getMoonMissionById method --- src/main/java/com/example/Main.java | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 45c0793..67ba311 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -112,6 +112,30 @@ private void listMoonMissions(Connection connection) throws SQLException { } } + // GET MOON MISSION BY ID + private void getMoonMissionById(Connection connection, InputStream in) throws SQLException, IOException { + System.out.print("Enter mission_id: "); + int id = Integer.parseInt(readLine(in)); + + String sql = "SELECT * FROM moon_mission WHERE mission_id = ?"; + try (PreparedStatement ps = connection.prepareStatement(sql)) { + ps.setInt(1, id); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + System.out.println("Spacecraft: " + rs.getString("spacecraft")); + System.out.println("Launch Date: " + rs.getDate("launch_date")); + System.out.println("Carrier Rocket: " + rs.getString("carrier_rocket")); + System.out.println("Operator: " + rs.getString("operator")); + System.out.println("Mission Type: " + rs.getString("mission_type")); + System.out.println("Outcome: " + rs.getString("outcome")); + } else { + System.out.println("No mission found with id " + id); + } + } + } + } + + From ce95e823d3db90ae6448c01e841d69bbcf7b268e Mon Sep 17 00:00:00 2001 From: Kristina M Date: Fri, 28 Nov 2025 23:02:33 +0100 Subject: [PATCH 09/61] add countMissionsByYear method --- src/main/java/com/example/Main.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 67ba311..55892ab 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -135,6 +135,21 @@ private void getMoonMissionById(Connection connection, InputStream in) throws SQ } } + // COUNT MISSIONS BY YEAR + private void countMissionsByYear(Connection connection, InputStream in) throws SQLException, IOException { + System.out.print("Enter year: "); + int year = Integer.parseInt(readLine(in)); + + String sql = "SELECT COUNT(*) FROM moon_mission WHERE YEAR(launch_date) = ?"; + try (PreparedStatement ps = connection.prepareStatement(sql)) { + ps.setInt(1, year); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + System.out.println("Number of missions in " + year + ": " + rs.getInt(1)); + } + } + } + } From 687784b58d55bb96de9bde8e64783b7b0b739015 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Fri, 28 Nov 2025 23:04:09 +0100 Subject: [PATCH 10/61] Implement more method cases in switch --- src/main/java/com/example/Main.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 55892ab..61c8fe5 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -87,6 +87,16 @@ public void run() { case "3": countMissionsByYear(connection, in); break; + case "4": + createAccount(connection, in); + break; + case "5": + updateAccountPassword(connection, in); + break; + case "6": + deleteAccount(connection, in); + break; + case "0": exit = true; break; From 7493b37f0f0597d8ce10eb677610e7e5147c88f6 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Fri, 28 Nov 2025 23:15:36 +0100 Subject: [PATCH 11/61] Implement visible id to method listMoonMissions when printing menu option 2 --- src/main/java/com/example/Main.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 61c8fe5..5015c32 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -67,12 +67,16 @@ public void run() { } } + // MENU LOOP boolean exit = false; while (!exit) { System.out.println("\nMenu:"); System.out.println("1) List moon missions"); System.out.println("2) Get a moon mission by mission_id"); System.out.println("3) Count missions for a given year"); + System.out.println("4) Create an account"); + System.out.println("5) Update an account password"); + System.out.println("6) Delete an account"); System.out.println("0) Exit"); System.out.print("Choose an option: "); String choice = readLine(in); @@ -96,12 +100,11 @@ public void run() { case "6": deleteAccount(connection, in); break; - case "0": exit = true; break; default: - System.out.println("Invalid option"); + System.out.println("Invalid option."); } } @@ -112,16 +115,17 @@ public void run() { // LIST MOON MISSIONS private void listMoonMissions(Connection connection) throws SQLException { - String sql = "SELECT spacecraft FROM moon_mission"; + String sql = "SELECT mission_id, spacecraft FROM moon_mission"; try (Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { System.out.println("Moon Missions:"); while (rs.next()) { - System.out.println(rs.getString("spacecraft")); + System.out.println(rs.getInt("mission_id") + ": " + rs.getString("spacecraft")); } } } + // GET MOON MISSION BY ID private void getMoonMissionById(Connection connection, InputStream in) throws SQLException, IOException { System.out.print("Enter mission_id: "); @@ -163,7 +167,6 @@ private void countMissionsByYear(Connection connection, InputStream in) throws S - // Reads a line from System.in using Java 25 IO private static String readLine(InputStream in) throws IOException { byte[] buffer = new byte[1024]; From 08cc1b80cf9a0bf42135e9db6a6f3968117d08cb Mon Sep 17 00:00:00 2001 From: Kristina M Date: Mon, 1 Dec 2025 22:26:12 +0100 Subject: [PATCH 12/61] Implement createAccount method, passes integration test --- src/main/java/com/example/Main.java | 35 ++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 5015c32..83af005 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -94,12 +94,6 @@ public void run() { case "4": createAccount(connection, in); break; - case "5": - updateAccountPassword(connection, in); - break; - case "6": - deleteAccount(connection, in); - break; case "0": exit = true; break; @@ -165,6 +159,35 @@ private void countMissionsByYear(Connection connection, InputStream in) throws S } } + // CREATE ACCOUNT + private void createAccount(Connection connection, InputStream in) throws SQLException, IOException { + System.out.print("First name: "); + String firstName = readLine(in); + + System.out.print("Last name: "); + String lastName = readLine(in); + + System.out.print("SSN: "); + String ssn = readLine(in); + + System.out.print("Password: "); + String password = readLine(in); + + String username = (firstName.length() >= 3 ? firstName.substring(0, 3) : firstName) + + (lastName.length() >= 3 ? lastName.substring(0, 3) : lastName); + + String sql = "INSERT INTO account (name, first_name, last_name, ssn, password) VALUES (?, ?, ?, ?, ?)"; + try (PreparedStatement ps = connection.prepareStatement(sql)) { + ps.setString(1, username); + ps.setString(2, firstName); + ps.setString(3, lastName); + ps.setString(4, ssn); + ps.setString(5, password); + ps.executeUpdate(); + System.out.println("Account " + username + " created!"); + } + } + // Reads a line from System.in using Java 25 IO From 97ff845cef4bcd77342d71b4c3e6f9620391265b Mon Sep 17 00:00:00 2001 From: Kristina M Date: Mon, 1 Dec 2025 22:30:28 +0100 Subject: [PATCH 13/61] implement createAccount, updateAccountPassword, and listAccounts helper - passes login, moon mission, and account tests (except delete) --- src/main/java/com/example/Main.java | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 83af005..7491a6f 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -94,6 +94,9 @@ public void run() { case "4": createAccount(connection, in); break; + case "5": + updateAccountPassword(connection, in); + break; case "0": exit = true; break; @@ -188,6 +191,52 @@ private void createAccount(Connection connection, InputStream in) throws SQLExce } } + // UPDATE ACCOUNT PASSWORD + private void updateAccountPassword(Connection connection, InputStream in) throws SQLException, IOException { + // List all accounts first + listAccounts(connection); + + long userId = -1; + while (true) { + System.out.print("Enter the User ID to update: "); + String input = readLine(in); + try { + userId = Long.parseLong(input); + break; + } catch (NumberFormatException e) { + System.out.println("Invalid input. Please enter a numeric user ID."); + } + } + + System.out.print("New password: "); + String newPassword = readLine(in); + + String sql = "UPDATE account SET password = ? WHERE user_id = ?"; + try (PreparedStatement ps = connection.prepareStatement(sql)) { + ps.setString(1, newPassword); + ps.setLong(2, userId); + int updated = ps.executeUpdate(); + if (updated > 0) { + System.out.println("Account password updated successfully!"); + } else { + System.out.println("No account found with user_id " + userId); + } + } + } + + + // Helper to list accounts + private void listAccounts(Connection connection) throws SQLException { + String sql = "SELECT user_id, first_name, last_name FROM account"; + try (Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery(sql)) { + System.out.println("Accounts:"); + while (rs.next()) { + System.out.printf("%d: %s %s%n", rs.getLong("user_id"), rs.getString("first_name"), rs.getString("last_name")); + } + } + } + // Reads a line from System.in using Java 25 IO From c376082994ee8c23535c178143bce94b4a51c4c0 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Mon, 1 Dec 2025 22:33:48 +0100 Subject: [PATCH 14/61] add deleteAccount method with user ID input and confirmation - passes integration test --- src/main/java/com/example/Main.java | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 7491a6f..897c1f0 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -97,6 +97,9 @@ public void run() { case "5": updateAccountPassword(connection, in); break; + case "6": + deleteAccount(connection, in); + break; case "0": exit = true; break; @@ -237,7 +240,34 @@ private void listAccounts(Connection connection) throws SQLException { } } + // DELETE ACCOUNT + private void deleteAccount(Connection connection, InputStream in) throws SQLException, IOException { + // List all accounts first + listAccounts(connection); + + long userId = -1; + while (true) { + System.out.print("Enter the User ID to delete: "); + String input = readLine(in); + try { + userId = Long.parseLong(input); + break; + } catch (NumberFormatException e) { + System.out.println("Invalid input. Please enter a numeric user ID."); + } + } + String sql = "DELETE FROM account WHERE user_id = ?"; + try (PreparedStatement ps = connection.prepareStatement(sql)) { + ps.setLong(1, userId); + int deleted = ps.executeUpdate(); + if (deleted > 0) { + System.out.println("Account deleted successfully!"); + } else { + System.out.println("No account found with user_id " + userId); + } + } + } // Reads a line from System.in using Java 25 IO private static String readLine(InputStream in) throws IOException { From 3b4145a944824ca1542c9ebeac6d3eeab4d9a3ed Mon Sep 17 00:00:00 2001 From: Kristina M Date: Mon, 1 Dec 2025 22:41:04 +0100 Subject: [PATCH 15/61] Create class SimpleDriverManagerDataSource --- src/main/java/com/example/SimpleDriverManagerDataSource.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/com/example/SimpleDriverManagerDataSource.java diff --git a/src/main/java/com/example/SimpleDriverManagerDataSource.java b/src/main/java/com/example/SimpleDriverManagerDataSource.java new file mode 100644 index 0000000..245872c --- /dev/null +++ b/src/main/java/com/example/SimpleDriverManagerDataSource.java @@ -0,0 +1,4 @@ +package com.example; + +public class SimpleDriverManagerDataSource { +} From aa41e0dcd5ddb52ceb6dc8fc2891496d5c8d3fd2 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Mon, 1 Dec 2025 22:42:07 +0100 Subject: [PATCH 16/61] Create interface AccountRepository --- src/main/java/com/example/AccountRepository.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/com/example/AccountRepository.java diff --git a/src/main/java/com/example/AccountRepository.java b/src/main/java/com/example/AccountRepository.java new file mode 100644 index 0000000..a0f1383 --- /dev/null +++ b/src/main/java/com/example/AccountRepository.java @@ -0,0 +1,4 @@ +package com.example; + +public interface AccountRepository { +} From 98a30f14da3dcf9e19b24bdea43c9970bddacc0e Mon Sep 17 00:00:00 2001 From: Kristina M Date: Mon, 1 Dec 2025 22:42:38 +0100 Subject: [PATCH 17/61] Create interface MoonMissionRepository --- src/main/java/com/example/MoonMissionRepository.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/com/example/MoonMissionRepository.java diff --git a/src/main/java/com/example/MoonMissionRepository.java b/src/main/java/com/example/MoonMissionRepository.java new file mode 100644 index 0000000..11b57e5 --- /dev/null +++ b/src/main/java/com/example/MoonMissionRepository.java @@ -0,0 +1,4 @@ +package com.example; + +public interface MoonMissionRepository { +} From f40b9f223e6b9933f4effb00d780a061b2f50b0c Mon Sep 17 00:00:00 2001 From: Kristina M Date: Mon, 1 Dec 2025 22:56:08 +0100 Subject: [PATCH 18/61] Create record Accont and MoonMission --- src/main/java/com/example/Account.java | 4 ++++ src/main/java/com/example/MoonMission.java | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 src/main/java/com/example/Account.java create mode 100644 src/main/java/com/example/MoonMission.java diff --git a/src/main/java/com/example/Account.java b/src/main/java/com/example/Account.java new file mode 100644 index 0000000..82ed11f --- /dev/null +++ b/src/main/java/com/example/Account.java @@ -0,0 +1,4 @@ +package com.example; + +public record Account() { +} diff --git a/src/main/java/com/example/MoonMission.java b/src/main/java/com/example/MoonMission.java new file mode 100644 index 0000000..b9c5e9f --- /dev/null +++ b/src/main/java/com/example/MoonMission.java @@ -0,0 +1,4 @@ +package com.example; + +public record MoonMission() { +} From be0f8b56bc86baaaf3d25f3cf76a32557d5aecea Mon Sep 17 00:00:00 2001 From: Kristina M Date: Mon, 1 Dec 2025 22:59:04 +0100 Subject: [PATCH 19/61] Implemented immutable data (records) for Account and MoonMission to represent database entities --- src/main/java/com/example/Account.java | 3 +-- src/main/java/com/example/MoonMission.java | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/Account.java b/src/main/java/com/example/Account.java index 82ed11f..4dcdfc1 100644 --- a/src/main/java/com/example/Account.java +++ b/src/main/java/com/example/Account.java @@ -1,4 +1,3 @@ package com.example; -public record Account() { -} +public record Account(long userId, String name, String firstName, String lastName, String ssn, String password) {} \ No newline at end of file diff --git a/src/main/java/com/example/MoonMission.java b/src/main/java/com/example/MoonMission.java index b9c5e9f..fd0e0e7 100644 --- a/src/main/java/com/example/MoonMission.java +++ b/src/main/java/com/example/MoonMission.java @@ -1,4 +1,6 @@ package com.example; -public record MoonMission() { -} +import java.util.Date; + +public record MoonMission(int missionId, String spacecraft, Date launchDate, String carrierRocket, + String operator, String missionType, String outcome) {} \ No newline at end of file From fc8ac6ecd346b23bf4633c917db635e49e681356 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 4 Dec 2025 11:25:08 +0100 Subject: [PATCH 20/61] Impl AccountRepositoryJdbcImpl class --- src/main/java/com/example/AccountRepositoryJdbcImpl.java | 4 ++++ src/main/java/com/example/SimpleDriverManagerDataSource.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/example/AccountRepositoryJdbcImpl.java diff --git a/src/main/java/com/example/AccountRepositoryJdbcImpl.java b/src/main/java/com/example/AccountRepositoryJdbcImpl.java new file mode 100644 index 0000000..6d0d49e --- /dev/null +++ b/src/main/java/com/example/AccountRepositoryJdbcImpl.java @@ -0,0 +1,4 @@ +package com.example; + +public class AccountRepositoryJdbcImpl { +} diff --git a/src/main/java/com/example/SimpleDriverManagerDataSource.java b/src/main/java/com/example/SimpleDriverManagerDataSource.java index 245872c..4cf2145 100644 --- a/src/main/java/com/example/SimpleDriverManagerDataSource.java +++ b/src/main/java/com/example/SimpleDriverManagerDataSource.java @@ -1,4 +1,4 @@ package com.example; public class SimpleDriverManagerDataSource { -} +} \ No newline at end of file From 63a1f717928c7e3306410887e21c9ed3e4bd6049 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 4 Dec 2025 11:25:36 +0100 Subject: [PATCH 21/61] Impl MoonMissionJdbcImpl class --- src/main/java/com/example/MoonMissionJdbcImpl.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/com/example/MoonMissionJdbcImpl.java diff --git a/src/main/java/com/example/MoonMissionJdbcImpl.java b/src/main/java/com/example/MoonMissionJdbcImpl.java new file mode 100644 index 0000000..45224fe --- /dev/null +++ b/src/main/java/com/example/MoonMissionJdbcImpl.java @@ -0,0 +1,4 @@ +package com.example; + +public class MoonMissionJdbcImpl { +} From 46735bdd652f32b2fa7c6baf256533d08c29ba54 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 4 Dec 2025 11:26:02 +0100 Subject: [PATCH 22/61] Impl AppFlow class --- src/main/java/com/example/AppFlow.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/com/example/AppFlow.java diff --git a/src/main/java/com/example/AppFlow.java b/src/main/java/com/example/AppFlow.java new file mode 100644 index 0000000..794c6fd --- /dev/null +++ b/src/main/java/com/example/AppFlow.java @@ -0,0 +1,4 @@ +package com.example; + +public class AppFlow { +} From ccf957da1f498a6afbd6d1e346d0edbe0b613962 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 4 Dec 2025 14:17:31 +0100 Subject: [PATCH 23/61] Impl AccountRepository interface --- src/main/java/com/example/AccountImpl.java | 4 ++++ src/main/java/com/example/AccountRepository.java | 8 ++++++++ src/main/java/com/example/AccountRepositoryJdbcImpl.java | 4 ---- src/main/java/com/example/{AppFlow.java => CLIFlow.java} | 2 +- src/main/java/com/example/MoonMissionImpl.java | 4 ++++ src/main/java/com/example/MoonMissionJdbcImpl.java | 4 ---- 6 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/example/AccountImpl.java delete mode 100644 src/main/java/com/example/AccountRepositoryJdbcImpl.java rename src/main/java/com/example/{AppFlow.java => CLIFlow.java} (51%) create mode 100644 src/main/java/com/example/MoonMissionImpl.java delete mode 100644 src/main/java/com/example/MoonMissionJdbcImpl.java diff --git a/src/main/java/com/example/AccountImpl.java b/src/main/java/com/example/AccountImpl.java new file mode 100644 index 0000000..0b7be29 --- /dev/null +++ b/src/main/java/com/example/AccountImpl.java @@ -0,0 +1,4 @@ +package com.example; + +public class AccountImpl { +} diff --git a/src/main/java/com/example/AccountRepository.java b/src/main/java/com/example/AccountRepository.java index a0f1383..ff645db 100644 --- a/src/main/java/com/example/AccountRepository.java +++ b/src/main/java/com/example/AccountRepository.java @@ -1,4 +1,12 @@ package com.example; +import java.util.List; +import java.util.Optional; + public interface AccountRepository { + Optional findNameAndPassword(String name, String password); + List findAll(); + long create(Account account); + boolean updatePassword(long userId, String newPassword); + boolean delete(long userId); } diff --git a/src/main/java/com/example/AccountRepositoryJdbcImpl.java b/src/main/java/com/example/AccountRepositoryJdbcImpl.java deleted file mode 100644 index 6d0d49e..0000000 --- a/src/main/java/com/example/AccountRepositoryJdbcImpl.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example; - -public class AccountRepositoryJdbcImpl { -} diff --git a/src/main/java/com/example/AppFlow.java b/src/main/java/com/example/CLIFlow.java similarity index 51% rename from src/main/java/com/example/AppFlow.java rename to src/main/java/com/example/CLIFlow.java index 794c6fd..97236df 100644 --- a/src/main/java/com/example/AppFlow.java +++ b/src/main/java/com/example/CLIFlow.java @@ -1,4 +1,4 @@ package com.example; -public class AppFlow { +public class CLIFlow { } diff --git a/src/main/java/com/example/MoonMissionImpl.java b/src/main/java/com/example/MoonMissionImpl.java new file mode 100644 index 0000000..af03081 --- /dev/null +++ b/src/main/java/com/example/MoonMissionImpl.java @@ -0,0 +1,4 @@ +package com.example; + +public class MoonMissionImpl { +} diff --git a/src/main/java/com/example/MoonMissionJdbcImpl.java b/src/main/java/com/example/MoonMissionJdbcImpl.java deleted file mode 100644 index 45224fe..0000000 --- a/src/main/java/com/example/MoonMissionJdbcImpl.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example; - -public class MoonMissionJdbcImpl { -} From ebb32093dc2a3ea684af08a180dc16af55c98937 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 4 Dec 2025 14:17:52 +0100 Subject: [PATCH 24/61] Impl MoonMissionRepository interface --- src/main/java/com/example/MoonMissionRepository.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/com/example/MoonMissionRepository.java b/src/main/java/com/example/MoonMissionRepository.java index 11b57e5..851ef82 100644 --- a/src/main/java/com/example/MoonMissionRepository.java +++ b/src/main/java/com/example/MoonMissionRepository.java @@ -1,4 +1,12 @@ package com.example; +import java.util.List; +import java.util.Optional; + public interface MoonMissionRepository { + List findAll(); + Optional findById(long missionId); + long create(MoonMission mission); + boolean updateSpacecraft(long missionId, String newSpacecraft); + boolean delete(long missionId); } From 4929580b17c84b3b93a1159c26f532d2a493012d Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 4 Dec 2025 14:21:01 +0100 Subject: [PATCH 25/61] Impl AccountImpl class --- src/main/java/com/example/AccountImpl.java | 81 +++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/AccountImpl.java b/src/main/java/com/example/AccountImpl.java index 0b7be29..9804389 100644 --- a/src/main/java/com/example/AccountImpl.java +++ b/src/main/java/com/example/AccountImpl.java @@ -1,4 +1,83 @@ package com.example; -public class AccountImpl { +import javax.sql.DataSource; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class AccountImpl implements AccountRepository { + private final DataSource ds; + + public AccountImpl(DataSource ds) { this.ds = ds; } + + @Override + public Optional findNameAndPassword(String name, String password) { + try (Connection c = ds.getConnection(); + PreparedStatement ps = c.prepareStatement( + "SELECT * FROM account WHERE name=? AND password=?")) { + ps.setString(1, name); + ps.setString(2, password); + ResultSet rs = ps.executeQuery(); + if (rs.next()) return Optional.of(mapAccount(rs)); + else return Optional.empty(); + } catch (SQLException e) { throw new RuntimeException(e); } + } + + @Override + public List findAll() { + try (Connection c = ds.getConnection(); + PreparedStatement ps = c.prepareStatement("SELECT * FROM account")) { + ResultSet rs = ps.executeQuery(); + List list = new ArrayList<>(); + while (rs.next()) list.add(mapAccount(rs)); + return list; + } catch (SQLException e) { throw new RuntimeException(e); } + } + + @Override + public long create(Account a) { + try (Connection c = ds.getConnection(); + PreparedStatement ps = c.prepareStatement( + "INSERT INTO account(name, first_name, last_name, ssn, password) VALUES (?,?,?,?,?)", + Statement.RETURN_GENERATED_KEYS)) { + ps.setString(1, a.name()); + ps.setString(2, a.firstName()); + ps.setString(3, a.lastName()); + ps.setString(4, a.ssn()); + ps.setString(5, a.password()); + ps.executeUpdate(); + try (ResultSet keys = ps.getGeneratedKeys()) { keys.next(); return keys.getLong(1); } + } catch (SQLException e) { throw new RuntimeException(e); } + } + + @Override + public boolean updatePassword(long id, String pw) { + try (Connection c = ds.getConnection(); + PreparedStatement ps = c.prepareStatement("UPDATE account SET password=? WHERE user_id=?")) { + ps.setString(1, pw); + ps.setLong(2, id); + return ps.executeUpdate() > 0; + } catch (SQLException e) { throw new RuntimeException(e); } + } + + @Override + public boolean delete(long id) { + try (Connection c = ds.getConnection(); + PreparedStatement ps = c.prepareStatement("DELETE FROM account WHERE user_id=?")) { + ps.setLong(1, id); + return ps.executeUpdate() > 0; + } catch (SQLException e) { throw new RuntimeException(e); } + } + + private Account mapAccount(ResultSet rs) throws SQLException { + return new Account( + rs.getLong("user_id"), + rs.getString("name"), + rs.getString("first_name"), + rs.getString("last_name"), + rs.getString("ssn"), + rs.getString("password") + ); + } } From 27fe08dad5657cfcc508a61fbdc8cf173a790121 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:07:05 +0100 Subject: [PATCH 26/61] Move MoonMission and Account records to model package, update code --- src/main/java/com/example/Account.java | 3 --- src/main/java/com/example/AccountImpl.java | 2 ++ .../java/com/example/AccountRepository.java | 2 ++ src/main/java/com/example/MoonMission.java | 6 ----- .../com/example/MoonMissionRepository.java | 2 ++ src/main/java/com/example/model/Account.java | 22 ++++++++++++++++ .../java/com/example/model/MoonMission.java | 26 +++++++++++++++++++ 7 files changed, 54 insertions(+), 9 deletions(-) delete mode 100644 src/main/java/com/example/Account.java delete mode 100644 src/main/java/com/example/MoonMission.java create mode 100644 src/main/java/com/example/model/Account.java create mode 100644 src/main/java/com/example/model/MoonMission.java diff --git a/src/main/java/com/example/Account.java b/src/main/java/com/example/Account.java deleted file mode 100644 index 4dcdfc1..0000000 --- a/src/main/java/com/example/Account.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.example; - -public record Account(long userId, String name, String firstName, String lastName, String ssn, String password) {} \ No newline at end of file diff --git a/src/main/java/com/example/AccountImpl.java b/src/main/java/com/example/AccountImpl.java index 9804389..f1a1d5f 100644 --- a/src/main/java/com/example/AccountImpl.java +++ b/src/main/java/com/example/AccountImpl.java @@ -1,5 +1,7 @@ package com.example; +import com.example.model.Account; + import javax.sql.DataSource; import java.sql.*; import java.util.ArrayList; diff --git a/src/main/java/com/example/AccountRepository.java b/src/main/java/com/example/AccountRepository.java index ff645db..590a1cd 100644 --- a/src/main/java/com/example/AccountRepository.java +++ b/src/main/java/com/example/AccountRepository.java @@ -1,5 +1,7 @@ package com.example; +import com.example.model.Account; + import java.util.List; import java.util.Optional; diff --git a/src/main/java/com/example/MoonMission.java b/src/main/java/com/example/MoonMission.java deleted file mode 100644 index fd0e0e7..0000000 --- a/src/main/java/com/example/MoonMission.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.example; - -import java.util.Date; - -public record MoonMission(int missionId, String spacecraft, Date launchDate, String carrierRocket, - String operator, String missionType, String outcome) {} \ No newline at end of file diff --git a/src/main/java/com/example/MoonMissionRepository.java b/src/main/java/com/example/MoonMissionRepository.java index 851ef82..463f820 100644 --- a/src/main/java/com/example/MoonMissionRepository.java +++ b/src/main/java/com/example/MoonMissionRepository.java @@ -1,5 +1,7 @@ package com.example; +import com.example.model.MoonMission; + import java.util.List; import java.util.Optional; diff --git a/src/main/java/com/example/model/Account.java b/src/main/java/com/example/model/Account.java new file mode 100644 index 0000000..a690a6b --- /dev/null +++ b/src/main/java/com/example/model/Account.java @@ -0,0 +1,22 @@ +package com.example.model; + +public record Account( + long userId, + String firstName, + String lastName, + String ssn, + String password, + String name +) { + + @Override + public String toString() { + return "Account: " + + "ID: " + userId + + ", First: " + firstName + + ", Last: " + lastName + + ", SSN: " + ssn + + ", Name: " + name; + } + // fix: skriv ut visuell record-account vid delete eller update password? +} \ No newline at end of file diff --git a/src/main/java/com/example/model/MoonMission.java b/src/main/java/com/example/model/MoonMission.java new file mode 100644 index 0000000..977e3ce --- /dev/null +++ b/src/main/java/com/example/model/MoonMission.java @@ -0,0 +1,26 @@ +package com.example.model; + +import java.sql.Date; + +public record MoonMission( + int missionId, + String spacecraft, + Date launchDate, + String carrierRocket, + String operator, + String missionType, + String outcome +) { + + @Override + public String toString() { + return "MoonMission: " + + "missionId: " + missionId + + ", spacecraft: " + spacecraft + + ", launchDate: " + launchDate + + ", carrierRocket: " + carrierRocket + + ", operator: " + operator + + ", missionType: " + missionType + + ", outcome: " + outcome; + } +} From 15310fb8240b297af4800ccd2f90ec40a4710333 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:09:10 +0100 Subject: [PATCH 27/61] Move AccountRepository and MoonMissionRepository to repository package and update interfaces --- src/main/java/com/example/AccountImpl.java | 1 + src/main/java/com/example/AccountRepository.java | 14 -------------- .../java/com/example/MoonMissionRepository.java | 14 -------------- .../example/repository/AccountRepository.java | 16 ++++++++++++++++ .../repository/MoonMissionRepository.java | 13 +++++++++++++ 5 files changed, 30 insertions(+), 28 deletions(-) delete mode 100644 src/main/java/com/example/AccountRepository.java delete mode 100644 src/main/java/com/example/MoonMissionRepository.java create mode 100644 src/main/java/com/example/repository/AccountRepository.java create mode 100644 src/main/java/com/example/repository/MoonMissionRepository.java diff --git a/src/main/java/com/example/AccountImpl.java b/src/main/java/com/example/AccountImpl.java index f1a1d5f..1d5ddb4 100644 --- a/src/main/java/com/example/AccountImpl.java +++ b/src/main/java/com/example/AccountImpl.java @@ -1,6 +1,7 @@ package com.example; import com.example.model.Account; +import com.example.repository.AccountRepository; import javax.sql.DataSource; import java.sql.*; diff --git a/src/main/java/com/example/AccountRepository.java b/src/main/java/com/example/AccountRepository.java deleted file mode 100644 index 590a1cd..0000000 --- a/src/main/java/com/example/AccountRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.example; - -import com.example.model.Account; - -import java.util.List; -import java.util.Optional; - -public interface AccountRepository { - Optional findNameAndPassword(String name, String password); - List findAll(); - long create(Account account); - boolean updatePassword(long userId, String newPassword); - boolean delete(long userId); -} diff --git a/src/main/java/com/example/MoonMissionRepository.java b/src/main/java/com/example/MoonMissionRepository.java deleted file mode 100644 index 463f820..0000000 --- a/src/main/java/com/example/MoonMissionRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.example; - -import com.example.model.MoonMission; - -import java.util.List; -import java.util.Optional; - -public interface MoonMissionRepository { - List findAll(); - Optional findById(long missionId); - long create(MoonMission mission); - boolean updateSpacecraft(long missionId, String newSpacecraft); - boolean delete(long missionId); -} diff --git a/src/main/java/com/example/repository/AccountRepository.java b/src/main/java/com/example/repository/AccountRepository.java new file mode 100644 index 0000000..71a0646 --- /dev/null +++ b/src/main/java/com/example/repository/AccountRepository.java @@ -0,0 +1,16 @@ +package com.example.repository; + +import com.example.model.Account; + +import java.sql.SQLException; +import java.util.List; +import java.util.Optional; + +public interface AccountRepository { + boolean validateLogin(String username, String password) throws SQLException; + long createAccount(String firstName, String lastName, String ssn, String password) throws SQLException; + void updatePassword(long userId, String newPassword) throws SQLException; + void deleteAccount(long userId) throws SQLException; + List listAccounts() throws SQLException; + Optional getById(long userId) throws SQLException; +} diff --git a/src/main/java/com/example/repository/MoonMissionRepository.java b/src/main/java/com/example/repository/MoonMissionRepository.java new file mode 100644 index 0000000..39f7ba2 --- /dev/null +++ b/src/main/java/com/example/repository/MoonMissionRepository.java @@ -0,0 +1,13 @@ +package com.example.repository; + +import com.example.model.MoonMission; + +import java.sql.SQLException; +import java.util.List; +import java.util.Optional; + +public interface MoonMissionRepository { + List listMissions() throws SQLException; + Optional getMissionById(int missionId) throws SQLException; + int countMissionsByYear(int year) throws SQLException; +} \ No newline at end of file From 4dbc5b023fb72b0fd111ab26dc390db938aec016 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:10:33 +0100 Subject: [PATCH 28/61] update class name CLIFlow to BaseRepository, move to repository package --- src/main/java/com/example/BaseRepository.java | 4 ++++ src/main/java/com/example/CLIFlow.java | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/example/BaseRepository.java delete mode 100644 src/main/java/com/example/CLIFlow.java diff --git a/src/main/java/com/example/BaseRepository.java b/src/main/java/com/example/BaseRepository.java new file mode 100644 index 0000000..54bcb68 --- /dev/null +++ b/src/main/java/com/example/BaseRepository.java @@ -0,0 +1,4 @@ +package com.example; + +public class BaseRepository { +} diff --git a/src/main/java/com/example/CLIFlow.java b/src/main/java/com/example/CLIFlow.java deleted file mode 100644 index 97236df..0000000 --- a/src/main/java/com/example/CLIFlow.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example; - -public class CLIFlow { -} From b3e3ced2b68e214f8d0365bef5b34ca7cb357d55 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:12:00 +0100 Subject: [PATCH 29/61] update class name AccountImpl to AccountRepositoryJdbc, move to repository package --- .../AccountRepositoryJdbc.java} | 4 ++-- .../java/com/example/{ => repository}/BaseRepository.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/main/java/com/example/{AccountImpl.java => repository/AccountRepositoryJdbc.java} (95%) rename src/main/java/com/example/{ => repository}/BaseRepository.java (50%) diff --git a/src/main/java/com/example/AccountImpl.java b/src/main/java/com/example/repository/AccountRepositoryJdbc.java similarity index 95% rename from src/main/java/com/example/AccountImpl.java rename to src/main/java/com/example/repository/AccountRepositoryJdbc.java index 1d5ddb4..de19759 100644 --- a/src/main/java/com/example/AccountImpl.java +++ b/src/main/java/com/example/repository/AccountRepositoryJdbc.java @@ -9,10 +9,10 @@ import java.util.List; import java.util.Optional; -public class AccountImpl implements AccountRepository { +public class AccountRepositoryJdbc implements AccountRepository { private final DataSource ds; - public AccountImpl(DataSource ds) { this.ds = ds; } + public AccountRepositoryJdbc(DataSource ds) { this.ds = ds; } @Override public Optional findNameAndPassword(String name, String password) { diff --git a/src/main/java/com/example/BaseRepository.java b/src/main/java/com/example/repository/BaseRepository.java similarity index 50% rename from src/main/java/com/example/BaseRepository.java rename to src/main/java/com/example/repository/BaseRepository.java index 54bcb68..a3335a0 100644 --- a/src/main/java/com/example/BaseRepository.java +++ b/src/main/java/com/example/repository/BaseRepository.java @@ -1,4 +1,4 @@ -package com.example; +package com.example.repository; public class BaseRepository { } From d761c6d81643c14a522b3bf630b93954176e9baf Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:13:00 +0100 Subject: [PATCH 30/61] update clas name MoonMissionImpl to MoonMissionRepositoryJdbc, move to repository package --- .../java/com/example/repository/AccountRepositoryJdbc.java | 3 +-- .../MoonMissionRepositoryJdbc.java} | 0 2 files changed, 1 insertion(+), 2 deletions(-) rename src/main/java/com/example/{MoonMissionImpl.java => repository/MoonMissionRepositoryJdbc.java} (100%) diff --git a/src/main/java/com/example/repository/AccountRepositoryJdbc.java b/src/main/java/com/example/repository/AccountRepositoryJdbc.java index de19759..d3a8e57 100644 --- a/src/main/java/com/example/repository/AccountRepositoryJdbc.java +++ b/src/main/java/com/example/repository/AccountRepositoryJdbc.java @@ -1,7 +1,6 @@ -package com.example; +package com.example.repository; import com.example.model.Account; -import com.example.repository.AccountRepository; import javax.sql.DataSource; import java.sql.*; diff --git a/src/main/java/com/example/MoonMissionImpl.java b/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java similarity index 100% rename from src/main/java/com/example/MoonMissionImpl.java rename to src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java From 87184b463fd06216b3d35dfa1504634c5e4c43e6 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:15:58 +0100 Subject: [PATCH 31/61] move methods from main: isDevMode and resolveConfig to new class ConfigUtils --- src/main/java/com/example/ConfigUtils.java | 17 +++++++++++ src/main/java/com/example/Main.java | 28 ------------------- .../repository/MoonMissionRepositoryJdbc.java | 4 +-- 3 files changed, 19 insertions(+), 30 deletions(-) create mode 100644 src/main/java/com/example/ConfigUtils.java diff --git a/src/main/java/com/example/ConfigUtils.java b/src/main/java/com/example/ConfigUtils.java new file mode 100644 index 0000000..ae129e4 --- /dev/null +++ b/src/main/java/com/example/ConfigUtils.java @@ -0,0 +1,17 @@ +package com.example; + +import java.util.Arrays; + +public class ConfigUtils { + public static boolean isDevMode(String[] args) { + if (Boolean.getBoolean("devMode")) return true; + if ("true".equalsIgnoreCase(System.getenv("DEV_MODE"))) return true; + return Arrays.asList(args).contains("--dev"); + } + + public static String resolveConfig(String propertyKey, String envKey) { + String v = System.getProperty(propertyKey); + if (v == null || v.trim().isEmpty()) v = System.getenv(envKey); + return (v == null || v.trim().isEmpty()) ? null : v.trim(); + } +} diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 897c1f0..13e007d 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -280,32 +280,4 @@ private static String readLine(InputStream in) throws IOException { } return new String(buffer, 0, len).trim(); } - - - /** - * Determines if the application is running in development mode based on system properties, - * environment variables, or command-line arguments. - * - * @param args an array of command-line arguments - * @return {@code true} if the application is in development mode; {@code false} otherwise - */ - private static boolean isDevMode(String[] args) { - if (Boolean.getBoolean("devMode")) //Add VM option -DdevMode=true - return true; - if ("true".equalsIgnoreCase(System.getenv("DEV_MODE"))) //Environment variable DEV_MODE=true - return true; - return Arrays.asList(args).contains("--dev"); //Argument --dev - } - - /** - * Reads configuration with precedence: Java system property first, then environment variable. - * Returns trimmed value or null if neither source provides a non-empty value. - */ - private static String resolveConfig(String propertyKey, String envKey) { - String v = System.getProperty(propertyKey); - if (v == null || v.trim().isEmpty()) { - v = System.getenv(envKey); - } - return (v == null || v.trim().isEmpty()) ? null : v.trim(); - } } \ No newline at end of file diff --git a/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java b/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java index af03081..483bca0 100644 --- a/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java +++ b/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java @@ -1,4 +1,4 @@ -package com.example; +package com.example.repository; -public class MoonMissionImpl { +public class MoonMissionRepositoryJdbc { } From 05866a66492647464092e109daaf8dd3422f19b8 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:18:34 +0100 Subject: [PATCH 32/61] move menu loop from main: to package cli and new class menuCLI --- src/main/java/com/example/Main.java | 46 ------------------ src/main/java/com/example/cli/MenuCLI.java | 55 ++++++++++++++++++++++ 2 files changed, 55 insertions(+), 46 deletions(-) create mode 100644 src/main/java/com/example/cli/MenuCLI.java diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 13e007d..d02e8b7 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -67,52 +67,6 @@ public void run() { } } - // MENU LOOP - boolean exit = false; - while (!exit) { - System.out.println("\nMenu:"); - System.out.println("1) List moon missions"); - System.out.println("2) Get a moon mission by mission_id"); - System.out.println("3) Count missions for a given year"); - System.out.println("4) Create an account"); - System.out.println("5) Update an account password"); - System.out.println("6) Delete an account"); - System.out.println("0) Exit"); - System.out.print("Choose an option: "); - String choice = readLine(in); - - switch (choice) { - case "1": - listMoonMissions(connection); - break; - case "2": - getMoonMissionById(connection, in); - break; - case "3": - countMissionsByYear(connection, in); - break; - case "4": - createAccount(connection, in); - break; - case "5": - updateAccountPassword(connection, in); - break; - case "6": - deleteAccount(connection, in); - break; - case "0": - exit = true; - break; - default: - System.out.println("Invalid option."); - } - } - - } catch (SQLException | IOException e) { - throw new RuntimeException(e); - } - } - // LIST MOON MISSIONS private void listMoonMissions(Connection connection) throws SQLException { String sql = "SELECT mission_id, spacecraft FROM moon_mission"; diff --git a/src/main/java/com/example/cli/MenuCLI.java b/src/main/java/com/example/cli/MenuCLI.java new file mode 100644 index 0000000..8d8192c --- /dev/null +++ b/src/main/java/com/example/cli/MenuCLI.java @@ -0,0 +1,55 @@ +package com.example.cli; + +import java.io.IOException; +import java.sql.SQLException; + +public class MenuCLI { + + + // MENU LOOP + boolean exit = false; + while (!exit) { + System.out.println("\nMenu:"); + System.out.println("1) List moon missions"); + System.out.println("2) Get a moon mission by mission_id"); + System.out.println("3) Count missions for a given year"); + System.out.println("4) Create an account"); + System.out.println("5) Update an account password"); + System.out.println("6) Delete an account"); + System.out.println("0) Exit"); + System.out.print("Choose an option: "); + String choice = readLine(in); + + switch (choice) { + case "1": + listMoonMissions(connection); + break; + case "2": + getMoonMissionById(connection, in); + break; + case "3": + countMissionsByYear(connection, in); + break; + case "4": + createAccount(connection, in); + break; + case "5": + updateAccountPassword(connection, in); + break; + case "6": + deleteAccount(connection, in); + break; + case "0": + exit = true; + break; + default: + System.out.println("Invalid option."); + } + } + +} catch (SQLException | IOException e) { + throw new RuntimeException(e); + } + } + + } From 7687e250a8c6e07a66c41eab73f93b34ea672a09 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:20:13 +0100 Subject: [PATCH 33/61] move login from main: to package cli and new class LoginManager --- src/main/java/com/example/Main.java | 28 -------------- .../java/com/example/cli/LoginManager.java | 37 +++++++++++++++++++ 2 files changed, 37 insertions(+), 28 deletions(-) create mode 100644 src/main/java/com/example/cli/LoginManager.java diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index d02e8b7..aa25586 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -38,34 +38,6 @@ public void run() { try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) { - // LOGIN LOOP - boolean loggedIn = false; - while (!loggedIn) { - System.out.print("Username: "); - String username = readLine(in); - - System.out.print("Password: "); - String password = readLine(in); - - try (PreparedStatement ps = connection.prepareStatement( - "SELECT 1 FROM account WHERE name = ? AND password = ?")) { - - ps.setString(1, username); - ps.setString(2, password); - - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - loggedIn = true; - System.out.println("Login successful!"); - } else { - System.out.println("Invalid username or password"); - System.out.print("Press 0 to exit or enter to retry: "); - String exit = readLine(in); - if ("0".equals(exit)) return; - } - } - } - } // LIST MOON MISSIONS private void listMoonMissions(Connection connection) throws SQLException { diff --git a/src/main/java/com/example/cli/LoginManager.java b/src/main/java/com/example/cli/LoginManager.java new file mode 100644 index 0000000..b64cabe --- /dev/null +++ b/src/main/java/com/example/cli/LoginManager.java @@ -0,0 +1,37 @@ +package com.example.cli; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; + +public class LoginManager { + + + // LOGIN LOOP + boolean loggedIn = false; + while (!loggedIn) { + System.out.print("Username: "); + String username = readLine(in); + + System.out.print("Password: "); + String password = readLine(in); + + try (PreparedStatement ps = connection.prepareStatement( + "SELECT 1 FROM account WHERE name = ? AND password = ?")) { + + ps.setString(1, username); + ps.setString(2, password); + + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + loggedIn = true; + System.out.println("Login successful!"); + } else { + System.out.println("Invalid username or password"); + System.out.print("Press 0 to exit or enter to retry: "); + String exit = readLine(in); + if ("0".equals(exit)) return; + } + } + } + } +} From fab16bfe83f9a3be9a4149862df6baf5666455c6 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:21:04 +0100 Subject: [PATCH 34/61] create class InputReader in package cli --- src/main/java/com/example/cli/InputReader.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/com/example/cli/InputReader.java diff --git a/src/main/java/com/example/cli/InputReader.java b/src/main/java/com/example/cli/InputReader.java new file mode 100644 index 0000000..5508634 --- /dev/null +++ b/src/main/java/com/example/cli/InputReader.java @@ -0,0 +1,4 @@ +package com.example.cli; + +public class InputReader { +} From c72e43418c2546dec3a151668740a00dc6f944c5 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:21:32 +0100 Subject: [PATCH 35/61] create class AccountCLI in package cli --- src/main/java/com/example/cli/AccountCLI.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/com/example/cli/AccountCLI.java diff --git a/src/main/java/com/example/cli/AccountCLI.java b/src/main/java/com/example/cli/AccountCLI.java new file mode 100644 index 0000000..53918d7 --- /dev/null +++ b/src/main/java/com/example/cli/AccountCLI.java @@ -0,0 +1,4 @@ +package com.example.cli; + +public class AccountCLI { +} From aaa0145c188edbd90d3eb0c72d9c0f61af9fab35 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:22:14 +0100 Subject: [PATCH 36/61] create class MoonMissionCLI in package cli --- src/main/java/com/example/cli/MoonMissionCLI.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/com/example/cli/MoonMissionCLI.java diff --git a/src/main/java/com/example/cli/MoonMissionCLI.java b/src/main/java/com/example/cli/MoonMissionCLI.java new file mode 100644 index 0000000..c9bc527 --- /dev/null +++ b/src/main/java/com/example/cli/MoonMissionCLI.java @@ -0,0 +1,4 @@ +package com.example.cli; + +public class MoonMissionCLI { +} From 75105b4e9c7e6cfddf2cd86520d26a8723886fc8 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:24:40 +0100 Subject: [PATCH 37/61] move method readline from main: to input reader class --- src/main/java/com/example/Main.java | 12 ------------ src/main/java/com/example/cli/InputReader.java | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index aa25586..c955398 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -194,16 +194,4 @@ private void deleteAccount(Connection connection, InputStream in) throws SQLExce } } } - - // Reads a line from System.in using Java 25 IO - private static String readLine(InputStream in) throws IOException { - byte[] buffer = new byte[1024]; - int len = 0; - int b; - while ((b = in.read()) != -1) { - if (b == '\n') break; - buffer[len++] = (byte) b; - } - return new String(buffer, 0, len).trim(); - } } \ No newline at end of file diff --git a/src/main/java/com/example/cli/InputReader.java b/src/main/java/com/example/cli/InputReader.java index 5508634..b794862 100644 --- a/src/main/java/com/example/cli/InputReader.java +++ b/src/main/java/com/example/cli/InputReader.java @@ -1,4 +1,19 @@ package com.example.cli; public class InputReader { + + + // Reads a line from System.in using Java 25 IO + private static String readLine(InputStream in) throws IOException { + byte[] buffer = new byte[1024]; + int len = 0; + int b; + while ((b = in.read()) != -1) { + if (b == '\n') break; + buffer[len++] = (byte) b; + } + return new String(buffer, 0, len).trim(); + } + + } From bfbe2cd068e1362c792611e0a71932874cedcf27 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:25:52 +0100 Subject: [PATCH 38/61] move methods listmoonmissions, getmoonmissionbyid, countmissionsbyyear from main: to MoonMissionRepositoryJdbc class --- src/main/java/com/example/Main.java | 50 ---------------- .../repository/MoonMissionRepositoryJdbc.java | 57 +++++++++++++++++++ 2 files changed, 57 insertions(+), 50 deletions(-) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index c955398..bf591b2 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -39,57 +39,7 @@ public void run() { try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) { - // LIST MOON MISSIONS - private void listMoonMissions(Connection connection) throws SQLException { - String sql = "SELECT mission_id, spacecraft FROM moon_mission"; - try (Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery(sql)) { - System.out.println("Moon Missions:"); - while (rs.next()) { - System.out.println(rs.getInt("mission_id") + ": " + rs.getString("spacecraft")); - } - } - } - - - // GET MOON MISSION BY ID - private void getMoonMissionById(Connection connection, InputStream in) throws SQLException, IOException { - System.out.print("Enter mission_id: "); - int id = Integer.parseInt(readLine(in)); - - String sql = "SELECT * FROM moon_mission WHERE mission_id = ?"; - try (PreparedStatement ps = connection.prepareStatement(sql)) { - ps.setInt(1, id); - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - System.out.println("Spacecraft: " + rs.getString("spacecraft")); - System.out.println("Launch Date: " + rs.getDate("launch_date")); - System.out.println("Carrier Rocket: " + rs.getString("carrier_rocket")); - System.out.println("Operator: " + rs.getString("operator")); - System.out.println("Mission Type: " + rs.getString("mission_type")); - System.out.println("Outcome: " + rs.getString("outcome")); - } else { - System.out.println("No mission found with id " + id); - } - } - } - } - - // COUNT MISSIONS BY YEAR - private void countMissionsByYear(Connection connection, InputStream in) throws SQLException, IOException { - System.out.print("Enter year: "); - int year = Integer.parseInt(readLine(in)); - String sql = "SELECT COUNT(*) FROM moon_mission WHERE YEAR(launch_date) = ?"; - try (PreparedStatement ps = connection.prepareStatement(sql)) { - ps.setInt(1, year); - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - System.out.println("Number of missions in " + year + ": " + rs.getInt(1)); - } - } - } - } // CREATE ACCOUNT private void createAccount(Connection connection, InputStream in) throws SQLException, IOException { diff --git a/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java b/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java index 483bca0..68469d3 100644 --- a/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java +++ b/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java @@ -1,4 +1,61 @@ package com.example.repository; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; + public class MoonMissionRepositoryJdbc { + + // LIST MOON MISSIONS + private void listMoonMissions(Connection connection) throws SQLException { + String sql = "SELECT mission_id, spacecraft FROM moon_mission"; + try (Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery(sql)) { + System.out.println("Moon Missions:"); + while (rs.next()) { + System.out.println(rs.getInt("mission_id") + ": " + rs.getString("spacecraft")); + } + } + } + + + // GET MOON MISSION BY ID + private void getMoonMissionById(Connection connection, InputStream in) throws SQLException, IOException { + System.out.print("Enter mission_id: "); + int id = Integer.parseInt(readLine(in)); + + String sql = "SELECT * FROM moon_mission WHERE mission_id = ?"; + try (PreparedStatement ps = connection.prepareStatement(sql)) { + ps.setInt(1, id); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + System.out.println("Spacecraft: " + rs.getString("spacecraft")); + System.out.println("Launch Date: " + rs.getDate("launch_date")); + System.out.println("Carrier Rocket: " + rs.getString("carrier_rocket")); + System.out.println("Operator: " + rs.getString("operator")); + System.out.println("Mission Type: " + rs.getString("mission_type")); + System.out.println("Outcome: " + rs.getString("outcome")); + } else { + System.out.println("No mission found with id " + id); + } + } + } + } + + // COUNT MISSIONS BY YEAR + private void countMissionsByYear(Connection connection, InputStream in) throws SQLException, IOException { + System.out.print("Enter year: "); + int year = Integer.parseInt(readLine(in)); + + String sql = "SELECT COUNT(*) FROM moon_mission WHERE YEAR(launch_date) = ?"; + try (PreparedStatement ps = connection.prepareStatement(sql)) { + ps.setInt(1, year); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + System.out.println("Number of missions in " + year + ": " + rs.getInt(1)); + } + } + } + } + } From b04d846ccd61d58331763fdef8c72943f0ee67c9 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:52:53 +0100 Subject: [PATCH 39/61] update main --- src/main/java/com/example/Main.java | 146 +++------------------------- 1 file changed, 14 insertions(+), 132 deletions(-) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index bf591b2..8cba7ef 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -1,147 +1,29 @@ package com.example; -import java.io.IOException; -import java.io.InputStream; -import java.sql.*; -import java.util.Arrays; +import com.example.cli.*; +import com.example.repository.*; +import com.example.service.*; + +import java.sql.SQLException; public class Main { - public static void main(String[] args) { - if (isDevMode(args)) { + public static void main(String[] args) throws SQLException { + if (ConfigUtils.isDevMode(args)) { DevDatabaseInitializer.start(); } - new Main().run(); - } - public void run() { - // Resolve DB settings with precedence: System properties -> Environment variables - String jdbcUrl = resolveConfig("APP_JDBC_URL", "APP_JDBC_URL"); - String dbUser = resolveConfig("APP_DB_USER", "APP_DB_USER"); - String dbPass = resolveConfig("APP_DB_PASS", "APP_DB_PASS"); - - /* - System.out.println("Dev Mode Check"); - System.out.println("JDBC URL: " + jdbcUrl); - System.out.println("DB User: " + dbUser); - System.out.println("DB Password: " + dbPass); - System.out.println("======================"); - */ + String jdbcUrl = ConfigUtils.resolveConfig("APP_JDBC_URL", "APP_JDBC_URL"); + String dbUser = ConfigUtils.resolveConfig("APP_DB_USER", "APP_DB_USER"); + String dbPass = ConfigUtils.resolveConfig("APP_DB_PASS", "APP_DB_PASS"); if (jdbcUrl == null || dbUser == null || dbPass == null) { - throw new IllegalStateException( - "Missing DB configuration. Provide APP_JDBC_URL, APP_DB_USER, APP_DB_PASS " + - "as system properties (-Dkey=value) or environment variables."); + throw new IllegalStateException("Missing DB configuration."); } - InputStream in = System.in; - - try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) { - - - - - // CREATE ACCOUNT - private void createAccount(Connection connection, InputStream in) throws SQLException, IOException { - System.out.print("First name: "); - String firstName = readLine(in); - - System.out.print("Last name: "); - String lastName = readLine(in); - - System.out.print("SSN: "); - String ssn = readLine(in); - - System.out.print("Password: "); - String password = readLine(in); - - String username = (firstName.length() >= 3 ? firstName.substring(0, 3) : firstName) - + (lastName.length() >= 3 ? lastName.substring(0, 3) : lastName); - - String sql = "INSERT INTO account (name, first_name, last_name, ssn, password) VALUES (?, ?, ?, ?, ?)"; - try (PreparedStatement ps = connection.prepareStatement(sql)) { - ps.setString(1, username); - ps.setString(2, firstName); - ps.setString(3, lastName); - ps.setString(4, ssn); - ps.setString(5, password); - ps.executeUpdate(); - System.out.println("Account " + username + " created!"); - } - } - - // UPDATE ACCOUNT PASSWORD - private void updateAccountPassword(Connection connection, InputStream in) throws SQLException, IOException { - // List all accounts first - listAccounts(connection); - - long userId = -1; - while (true) { - System.out.print("Enter the User ID to update: "); - String input = readLine(in); - try { - userId = Long.parseLong(input); - break; - } catch (NumberFormatException e) { - System.out.println("Invalid input. Please enter a numeric user ID."); - } - } - - System.out.print("New password: "); - String newPassword = readLine(in); - - String sql = "UPDATE account SET password = ? WHERE user_id = ?"; - try (PreparedStatement ps = connection.prepareStatement(sql)) { - ps.setString(1, newPassword); - ps.setLong(2, userId); - int updated = ps.executeUpdate(); - if (updated > 0) { - System.out.println("Account password updated successfully!"); - } else { - System.out.println("No account found with user_id " + userId); - } - } - } - - - // Helper to list accounts - private void listAccounts(Connection connection) throws SQLException { - String sql = "SELECT user_id, first_name, last_name FROM account"; - try (Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery(sql)) { - System.out.println("Accounts:"); - while (rs.next()) { - System.out.printf("%d: %s %s%n", rs.getLong("user_id"), rs.getString("first_name"), rs.getString("last_name")); - } - } - } - - // DELETE ACCOUNT - private void deleteAccount(Connection connection, InputStream in) throws SQLException, IOException { - // List all accounts first - listAccounts(connection); - - long userId = -1; - while (true) { - System.out.print("Enter the User ID to delete: "); - String input = readLine(in); - try { - userId = Long.parseLong(input); - break; - } catch (NumberFormatException e) { - System.out.println("Invalid input. Please enter a numeric user ID."); - } - } + SimpleDriverManagerDataSource dataSource = new SimpleDriverManagerDataSource(jdbcUrl, dbUser, dbPass); + boolean devMode = ConfigUtils.isDevMode(args); - String sql = "DELETE FROM account WHERE user_id = ?"; - try (PreparedStatement ps = connection.prepareStatement(sql)) { - ps.setLong(1, userId); - int deleted = ps.executeUpdate(); - if (deleted > 0) { - System.out.println("Account deleted successfully!"); - } else { - System.out.println("No account found with user_id " + userId); - } } } -} \ No newline at end of file +} From 966a1000d2597dfc5353514fc5ccf671980d99df Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:55:25 +0100 Subject: [PATCH 40/61] update repository classes --- .../repository/AccountRepositoryJdbc.java | 95 +++++++------------ .../example/repository/BaseRepository.java | 94 +++++++++++++++++- .../repository/MoonMissionRepositoryJdbc.java | 89 ++++++++--------- 3 files changed, 165 insertions(+), 113 deletions(-) diff --git a/src/main/java/com/example/repository/AccountRepositoryJdbc.java b/src/main/java/com/example/repository/AccountRepositoryJdbc.java index d3a8e57..5a0b03a 100644 --- a/src/main/java/com/example/repository/AccountRepositoryJdbc.java +++ b/src/main/java/com/example/repository/AccountRepositoryJdbc.java @@ -3,83 +3,60 @@ import com.example.model.Account; import javax.sql.DataSource; -import java.sql.*; -import java.util.ArrayList; import java.util.List; import java.util.Optional; -public class AccountRepositoryJdbc implements AccountRepository { - private final DataSource ds; +public class AccountRepositoryJdbc extends BaseRepository implements AccountRepository { - public AccountRepositoryJdbc(DataSource ds) { this.ds = ds; } + public AccountRepositoryJdbc(DataSource dataSource, boolean devMode) { + super(dataSource, devMode); + } @Override - public Optional findNameAndPassword(String name, String password) { - try (Connection c = ds.getConnection(); - PreparedStatement ps = c.prepareStatement( - "SELECT * FROM account WHERE name=? AND password=?")) { - ps.setString(1, name); - ps.setString(2, password); - ResultSet rs = ps.executeQuery(); - if (rs.next()) return Optional.of(mapAccount(rs)); - else return Optional.empty(); - } catch (SQLException e) { throw new RuntimeException(e); } + protected Account map(java.sql.ResultSet rs) throws java.sql.SQLException { + return new Account( + rs.getLong("user_id"), + rs.getString("first_name"), + rs.getString("last_name"), + rs.getString("ssn"), + rs.getString("password"), + rs.getString("name") + ); } @Override - public List findAll() { - try (Connection c = ds.getConnection(); - PreparedStatement ps = c.prepareStatement("SELECT * FROM account")) { - ResultSet rs = ps.executeQuery(); - List list = new ArrayList<>(); - while (rs.next()) list.add(mapAccount(rs)); - return list; - } catch (SQLException e) { throw new RuntimeException(e); } + public List listAccounts() { + return queryList("SELECT * FROM account"); } @Override - public long create(Account a) { - try (Connection c = ds.getConnection(); - PreparedStatement ps = c.prepareStatement( - "INSERT INTO account(name, first_name, last_name, ssn, password) VALUES (?,?,?,?,?)", - Statement.RETURN_GENERATED_KEYS)) { - ps.setString(1, a.name()); - ps.setString(2, a.firstName()); - ps.setString(3, a.lastName()); - ps.setString(4, a.ssn()); - ps.setString(5, a.password()); - ps.executeUpdate(); - try (ResultSet keys = ps.getGeneratedKeys()) { keys.next(); return keys.getLong(1); } - } catch (SQLException e) { throw new RuntimeException(e); } + public Optional getById(long userId) { + return querySingle("SELECT * FROM account WHERE user_id=?", userId); } @Override - public boolean updatePassword(long id, String pw) { - try (Connection c = ds.getConnection(); - PreparedStatement ps = c.prepareStatement("UPDATE account SET password=? WHERE user_id=?")) { - ps.setString(1, pw); - ps.setLong(2, id); - return ps.executeUpdate() > 0; - } catch (SQLException e) { throw new RuntimeException(e); } + public boolean validateLogin(String username, String password) { + return executeQuery( + "SELECT COUNT(*) FROM account WHERE name=? AND password=?", + rs -> { rs.next(); return rs.getInt(1) > 0; }, + username, password + ); } @Override - public boolean delete(long id) { - try (Connection c = ds.getConnection(); - PreparedStatement ps = c.prepareStatement("DELETE FROM account WHERE user_id=?")) { - ps.setLong(1, id); - return ps.executeUpdate() > 0; - } catch (SQLException e) { throw new RuntimeException(e); } + public long createAccount(String firstName, String lastName, String ssn, String password) { + String name = firstName.substring(0, 3) + lastName.substring(0, 3); + String sql = "INSERT INTO account (first_name, last_name, ssn, password, name) VALUES (?,?,?,?,?)"; + return executeUpdateReturnId(sql, firstName, lastName, ssn, password, name); } - private Account mapAccount(ResultSet rs) throws SQLException { - return new Account( - rs.getLong("user_id"), - rs.getString("name"), - rs.getString("first_name"), - rs.getString("last_name"), - rs.getString("ssn"), - rs.getString("password") - ); + @Override + public void updatePassword(long userId, String newPassword) { + executeUpdate("UPDATE account SET password=? WHERE user_id=?", newPassword, userId); + } + + @Override + public void deleteAccount(long userId) { + executeUpdate("DELETE FROM account WHERE user_id=?", userId); } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/repository/BaseRepository.java b/src/main/java/com/example/repository/BaseRepository.java index a3335a0..4ec37b9 100644 --- a/src/main/java/com/example/repository/BaseRepository.java +++ b/src/main/java/com/example/repository/BaseRepository.java @@ -1,4 +1,94 @@ package com.example.repository; -public class BaseRepository { -} +import javax.sql.DataSource; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public abstract class BaseRepository { + protected final DataSource dataSource; + protected final boolean devMode; + + protected BaseRepository(DataSource dataSource, boolean devMode) { + this.dataSource = dataSource; + this.devMode = devMode; + if (devMode) { + System.out.println("[DEV] Repository " + this.getClass().getSimpleName() + " initialized"); + } + } + + protected Connection getConnection() throws SQLException { + return dataSource.getConnection(); + } + + protected void log(String msg) { + if (devMode) System.out.println("[DEV] " + msg); + } + + protected RepositoryException dbError(String action, Exception e) { + log("ERROR during: " + action + " -> " + e.getMessage()); + return new RepositoryException("Database error during: " + action, e); + } + + protected R executeQuery(String sql, SQLFunction handler, Object... params) { + try (Connection conn = getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + for (int i = 0; i < params.length; i++) stmt.setObject(i + 1, params[i]); + try (ResultSet rs = stmt.executeQuery()) { + return handler.apply(rs); + } + } catch (Exception e) { + throw dbError("executeQuery: " + sql, e); // Inget cast här heller + } + } + + protected void executeUpdate(String sql, Object... params) { + try (Connection conn = getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + for (int i = 0; i < params.length; i++) stmt.setObject(i + 1, params[i]); + stmt.executeUpdate(); + } catch (SQLException e) { + throw dbError("executeUpdate: " + sql, e); + } + } + + protected long executeUpdateReturnId(String sql, Object... params) { + try (Connection conn = getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { + + for (int i = 0; i < params.length; i++) stmt.setObject(i + 1, params[i]); + stmt.executeUpdate(); + + try (ResultSet keys = stmt.getGeneratedKeys()) { + return keys.next() ? keys.getLong(1) : 0; + } + } catch (SQLException e) { + throw dbError("executeUpdateReturnId: " + sql, e); + } + } + + @FunctionalInterface + protected interface SQLFunction { T apply(R result) throws Exception; } + + protected abstract T map(ResultSet rs) throws SQLException; + + protected List queryList(String sql, Object... params) { + return executeQuery(sql, rs -> { + List list = new ArrayList<>(); + while (rs.next()) { + list.add(map(rs)); + } + return list; + }, params); + } + + protected Optional querySingle(String sql, Object... params) { + return executeQuery(sql, rs -> { + if (rs.next()) return Optional.of(map(rs)); + return Optional.empty(); + }, params); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java b/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java index 68469d3..abac7cb 100644 --- a/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java +++ b/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java @@ -1,61 +1,46 @@ package com.example.repository; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.Statement; - -public class MoonMissionRepositoryJdbc { - - // LIST MOON MISSIONS - private void listMoonMissions(Connection connection) throws SQLException { - String sql = "SELECT mission_id, spacecraft FROM moon_mission"; - try (Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery(sql)) { - System.out.println("Moon Missions:"); - while (rs.next()) { - System.out.println(rs.getInt("mission_id") + ": " + rs.getString("spacecraft")); - } - } +import com.example.model.MoonMission; + +import javax.sql.DataSource; +import java.util.List; +import java.util.Optional; + +public class MoonMissionRepositoryJdbc extends BaseRepository implements MoonMissionRepository { + + public MoonMissionRepositoryJdbc(DataSource dataSource, boolean devMode) { + super(dataSource, devMode); } + @Override + protected MoonMission map(java.sql.ResultSet rs) throws java.sql.SQLException { + return new MoonMission( + rs.getInt("mission_id"), + rs.getString("spacecraft"), + rs.getDate("launch_date"), + rs.getString("carrier_rocket"), + rs.getString("operator"), + rs.getString("mission_type"), + rs.getString("outcome") + ); + } - // GET MOON MISSION BY ID - private void getMoonMissionById(Connection connection, InputStream in) throws SQLException, IOException { - System.out.print("Enter mission_id: "); - int id = Integer.parseInt(readLine(in)); - - String sql = "SELECT * FROM moon_mission WHERE mission_id = ?"; - try (PreparedStatement ps = connection.prepareStatement(sql)) { - ps.setInt(1, id); - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - System.out.println("Spacecraft: " + rs.getString("spacecraft")); - System.out.println("Launch Date: " + rs.getDate("launch_date")); - System.out.println("Carrier Rocket: " + rs.getString("carrier_rocket")); - System.out.println("Operator: " + rs.getString("operator")); - System.out.println("Mission Type: " + rs.getString("mission_type")); - System.out.println("Outcome: " + rs.getString("outcome")); - } else { - System.out.println("No mission found with id " + id); - } - } - } + @Override + public List listMissions() { + return queryList("SELECT * FROM moon_mission"); } - // COUNT MISSIONS BY YEAR - private void countMissionsByYear(Connection connection, InputStream in) throws SQLException, IOException { - System.out.print("Enter year: "); - int year = Integer.parseInt(readLine(in)); - - String sql = "SELECT COUNT(*) FROM moon_mission WHERE YEAR(launch_date) = ?"; - try (PreparedStatement ps = connection.prepareStatement(sql)) { - ps.setInt(1, year); - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - System.out.println("Number of missions in " + year + ": " + rs.getInt(1)); - } - } - } + @Override + public Optional getMissionById(int missionId) { + return querySingle("SELECT * FROM moon_mission WHERE mission_id=?", missionId); } -} + @Override + public int countMissionsByYear(int year) { + return executeQuery( + "SELECT COUNT(*) FROM moon_mission WHERE YEAR(launch_date)=?", + rs -> { rs.next(); return rs.getInt(1); }, + year + ); + } +} \ No newline at end of file From a3b8b1368071bde21c8ae182cd0b1b2faece2960 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:57:18 +0100 Subject: [PATCH 41/61] update MoonMissionCLI --- .../java/com/example/cli/MoonMissionCLI.java | 70 ++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/cli/MoonMissionCLI.java b/src/main/java/com/example/cli/MoonMissionCLI.java index c9bc527..ea64e3d 100644 --- a/src/main/java/com/example/cli/MoonMissionCLI.java +++ b/src/main/java/com/example/cli/MoonMissionCLI.java @@ -1,4 +1,72 @@ package com.example.cli; -public class MoonMissionCLI { +import com.example.model.MoonMission; +import com.example.service.MoonMissionService; + +import java.sql.SQLException; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +public class MoonMissionCLI implements ExitMenuHandler { + + private final MoonMissionService service; + private final InputReader input; + + public MoonMissionCLI(MoonMissionService service, InputReader input) { + this.service = service; + this.input = input; + } + + public void listMissions() { + try { + List missions = service.listMissions(); + System.out.println("\n-- All Moon Missions --"); + missions.forEach(m -> System.out.println(m.spacecraft())); + System.out.println("----------------------\n"); + } catch (SQLException e) { + System.out.println("❌ Error listing missions: " + e.getMessage()); + } + } + + public void getMissionById() { + var idWrapper = input.readInt("Mission ID"); + if (handleExitOrMenu(idWrapper.result())) return; + + try { + Optional mission = service.getMissionById(idWrapper.value()); + mission.ifPresentOrElse( + m -> { + System.out.println("\n-- Mission Details --"); + System.out.println(m); + System.out.println("-------------------\n"); + }, + () -> System.out.println("❌ No mission with that ID ❌") + ); + } catch (SQLException e) { + System.out.println("❌ Error fetching mission: " + e.getMessage()); + } + } + + public void countMissionsByYear() { + var yearWrapper = input.readInt("Year"); + if (handleExitOrMenu(yearWrapper.result())) return; + + try { + List missions = service.listMissions(); + System.out.println("\nMissions in " + yearWrapper.value() + " (most recent first):"); + missions.stream() + .filter(m -> m.launchDate().toLocalDate().getYear() == yearWrapper.value()) + .sorted(Comparator.comparing(MoonMission::launchDate).reversed()) + .forEach(this::printMissionSummary); + System.out.println("-------------------\n"); + } catch (SQLException e) { + System.out.println("❌ Error counting missions: " + e.getMessage()); + } + } + + private void printMissionSummary(MoonMission m) { + System.out.printf("Spacecraft: %s | Launch Date: %s | Rocket: %s | Operator: %s%n", + m.spacecraft(), m.launchDate(), m.carrierRocket(), m.operator()); + } } From 1e2d5fc4229b3c1bb0ae1db3efae5ce2af25c93d Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:57:38 +0100 Subject: [PATCH 42/61] update MenyCLI --- src/main/java/com/example/cli/MenuCLI.java | 88 +++++++++++----------- 1 file changed, 42 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/example/cli/MenuCLI.java b/src/main/java/com/example/cli/MenuCLI.java index 8d8192c..21ccc83 100644 --- a/src/main/java/com/example/cli/MenuCLI.java +++ b/src/main/java/com/example/cli/MenuCLI.java @@ -1,55 +1,51 @@ package com.example.cli; -import java.io.IOException; -import java.sql.SQLException; - public class MenuCLI { + private final AccountCLI accountCLI; + private final MoonMissionCLI missionCLI; + private final InputReader input; - // MENU LOOP - boolean exit = false; - while (!exit) { - System.out.println("\nMenu:"); - System.out.println("1) List moon missions"); - System.out.println("2) Get a moon mission by mission_id"); - System.out.println("3) Count missions for a given year"); - System.out.println("4) Create an account"); - System.out.println("5) Update an account password"); - System.out.println("6) Delete an account"); - System.out.println("0) Exit"); - System.out.print("Choose an option: "); - String choice = readLine(in); - - switch (choice) { - case "1": - listMoonMissions(connection); - break; - case "2": - getMoonMissionById(connection, in); - break; - case "3": - countMissionsByYear(connection, in); - break; - case "4": - createAccount(connection, in); - break; - case "5": - updateAccountPassword(connection, in); - break; - case "6": - deleteAccount(connection, in); - break; - case "0": - exit = true; - break; - default: - System.out.println("Invalid option."); - } + public MenuCLI(AccountCLI accountCLI, MoonMissionCLI missionCLI, InputReader input) { + this.accountCLI = accountCLI; + this.missionCLI = missionCLI; + this.input = input; } -} catch (SQLException | IOException e) { - throw new RuntimeException(e); + public void showMainMenu() { + while (true) { + printHeader(); + var choiceWrapper = input.readInt("Choose option"); + + if (choiceWrapper.result() == InputReader.InputResult.EXIT) { + System.out.println("Exiting..."); + return; + } + + if (choiceWrapper.result() == InputReader.InputResult.MENU) continue; + + switch (choiceWrapper.value()) { + case 1 -> missionCLI.listMissions(); + case 2 -> missionCLI.getMissionById(); + case 3 -> missionCLI.countMissionsByYear(); + case 4 -> accountCLI.createAccount(); + case 5 -> accountCLI.updatePassword(); + case 6 -> accountCLI.deleteAccount(); + default -> System.out.println("Invalid option, try again."); + } } - } + } - } + private void printHeader() { + System.out.println("\n 🌕 MOON MISSION HUB 🌕 "); + System.out.println("----------------------------------------"); + System.out.println("Type 0 to exit from this menu, or 'menu' to go back to the main menu."); + System.out.println(" 1) List moon missions"); + System.out.println(" 2) Get mission by ID"); + System.out.println(" 3) Count missions by year"); + System.out.println(" 4) Create account"); + System.out.println(" 5) Update password"); + System.out.println(" 6) Delete account"); + System.out.println(" 0) Exit\n"); + } +} From bd45ee45c5002ecd1a1dfeb978a89b2e2c15852a Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:57:58 +0100 Subject: [PATCH 43/61] update LoginManager --- .../java/com/example/cli/LoginManager.java | 59 ++++++++++++------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/example/cli/LoginManager.java b/src/main/java/com/example/cli/LoginManager.java index b64cabe..cb2979e 100644 --- a/src/main/java/com/example/cli/LoginManager.java +++ b/src/main/java/com/example/cli/LoginManager.java @@ -1,37 +1,52 @@ package com.example.cli; -import java.sql.PreparedStatement; -import java.sql.ResultSet; +import com.example.service.AccountService; +import java.sql.SQLException; -public class LoginManager { +public class LoginManager implements ExitMenuHandler { + private final AccountService service; + private final int maxAttempts; + private final InputReader input; - // LOGIN LOOP - boolean loggedIn = false; - while (!loggedIn) { - System.out.print("Username: "); - String username = readLine(in); + public LoginManager(AccountService service, InputReader input) { + this(service, input, 5); + } + + public LoginManager(AccountService service, InputReader input, int maxAttempts) { + this.service = service; + this.input = input; + this.maxAttempts = maxAttempts; + } - System.out.print("Password: "); - String password = readLine(in); + public boolean login() { + System.out.println("Type 0 to exit anytime, or 'menu' to go back."); - try (PreparedStatement ps = connection.prepareStatement( - "SELECT 1 FROM account WHERE name = ? AND password = ?")) { + int attempts = 0; + while (attempts < maxAttempts) { + var usernameWrapper = input.readString("Username"); + if (handleExitOrMenu(usernameWrapper.result())) return false; - ps.setString(1, username); - ps.setString(2, password); + var passwordWrapper = input.readPassword("Password"); + if (handleExitOrMenu(passwordWrapper.result())) return false; - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - loggedIn = true; - System.out.println("Login successful!"); + try { + if (service.validateLogin(usernameWrapper.value(), passwordWrapper.value())) { + System.out.println("\n✅ Login successful! Welcome, " + usernameWrapper.value() + "!\n"); + return true; } else { - System.out.println("Invalid username or password"); - System.out.print("Press 0 to exit or enter to retry: "); - String exit = readLine(in); - if ("0".equals(exit)) return; + System.out.println("❌ Invalid username or password ❌"); } + } catch (SQLException e) { + System.out.println("❌ Error validating login: " + e.getMessage()); + } + + attempts++; + if (attempts < maxAttempts) { + System.out.println("Attempts left: " + (maxAttempts - attempts)); } } + + return false; } } From 50c4fbc0bca307ec6517380c9ee58c3474ad30cc Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:58:16 +0100 Subject: [PATCH 44/61] update InputReader --- .../java/com/example/cli/InputReader.java | 87 ++++++++++++++++--- 1 file changed, 77 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/example/cli/InputReader.java b/src/main/java/com/example/cli/InputReader.java index b794862..5a5b9a0 100644 --- a/src/main/java/com/example/cli/InputReader.java +++ b/src/main/java/com/example/cli/InputReader.java @@ -1,19 +1,86 @@ package com.example.cli; +import java.io.BufferedReader; +import java.io.InputStreamReader; + public class InputReader { + private final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + + public enum InputResult { CONTINUE, MENU, EXIT } + + public record InputWrapper(T value, InputResult result) {} + + private String readLine() { + try { + String line = reader.readLine(); + return line != null ? line.trim() : ""; + } catch (Exception e) { + throw new RuntimeException("Error reading input", e); + } + } + + private InputResult checkExitOrMenu(String input) { + if (input.equals("0")) return InputResult.EXIT; + if (input.equalsIgnoreCase("menu")) return InputResult.MENU; + return InputResult.CONTINUE; + } + + public InputWrapper readString(String label) { + System.out.print(label + ": "); + System.out.flush(); + String input = readLine(); + InputResult result = checkExitOrMenu(input); + return new InputWrapper<>(input, result); + } + + public InputWrapper readInt(String label) { + while (true) { + InputWrapper wrapper = readString(label); + if (wrapper.result() != InputResult.CONTINUE) return new InputWrapper<>(0, wrapper.result()); - // Reads a line from System.in using Java 25 IO - private static String readLine(InputStream in) throws IOException { - byte[] buffer = new byte[1024]; - int len = 0; - int b; - while ((b = in.read()) != -1) { - if (b == '\n') break; - buffer[len++] = (byte) b; + try { + return new InputWrapper<>(Integer.parseInt(wrapper.value()), InputResult.CONTINUE); + } catch (NumberFormatException e) { + System.out.println("❌ Please enter a valid number ❌"); + } } - return new String(buffer, 0, len).trim(); } + public InputWrapper readValidUserId(String label) { + while (true) { + InputWrapper wrapper = readString(label); + if (wrapper.result() != InputResult.CONTINUE) return new InputWrapper<>(0L, wrapper.result()); -} + try { + return new InputWrapper<>(Long.parseLong(wrapper.value()), InputResult.CONTINUE); + } catch (NumberFormatException e) { + System.out.println("❌ Please enter a valid number ❌"); + } + } + } + + public InputWrapper readName(String label) { + while (true) { + InputWrapper wrapper = readString(label); + if (wrapper.result() != InputResult.CONTINUE) return wrapper; + + if (wrapper.value().matches("[A-Z][a-zA-Z]{2,}")) return wrapper; + System.out.println("❌ Must start with a capital letter and be at least 3 letters ❌"); + } + } + + public InputWrapper readSSN(String label) { + while (true) { + InputWrapper wrapper = readString(label); + if (wrapper.result() != InputResult.CONTINUE) return wrapper; + + if (wrapper.value().matches("\\d{6}-\\d{4}")) return wrapper; + System.out.println("❌ SSN must match format ######-#### ❌"); + } + } + + public InputWrapper readPassword(String label) { + return readString(label); + } +} \ No newline at end of file From 645fca3c053a2bafc43e4bdeaf45a53ed652d674 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:58:47 +0100 Subject: [PATCH 45/61] add interface handleExitOrMenu to CLI package --- src/main/java/com/example/cli/ExitMenuHandler.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/com/example/cli/ExitMenuHandler.java diff --git a/src/main/java/com/example/cli/ExitMenuHandler.java b/src/main/java/com/example/cli/ExitMenuHandler.java new file mode 100644 index 0000000..0b8ceaa --- /dev/null +++ b/src/main/java/com/example/cli/ExitMenuHandler.java @@ -0,0 +1,12 @@ +package com.example.cli; + +public interface ExitMenuHandler { + + default boolean handleExitOrMenu(InputReader.InputResult result) { + if (result == InputReader.InputResult.EXIT) { + System.out.println("Exiting..."); + return true; + } + return result == InputReader.InputResult.MENU; + } +} From cb4e3706da0c13ffc8e08988683eea075759bf36 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 11:59:06 +0100 Subject: [PATCH 46/61] update AccountCLI --- src/main/java/com/example/cli/AccountCLI.java | 75 ++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/cli/AccountCLI.java b/src/main/java/com/example/cli/AccountCLI.java index 53918d7..c4be514 100644 --- a/src/main/java/com/example/cli/AccountCLI.java +++ b/src/main/java/com/example/cli/AccountCLI.java @@ -1,4 +1,77 @@ package com.example.cli; -public class AccountCLI { +import com.example.service.AccountService; +import java.sql.SQLException; + +public class AccountCLI implements ExitMenuHandler { + + private final AccountService service; + private final InputReader input; + + public AccountCLI(AccountService service, InputReader input) { + this.service = service; + this.input = input; + } + + public void createAccount() { + try { + var first = input.readName("First name"); + if (handleExitOrMenu(first.result())) return; + + var last = input.readName("Last name"); + if (handleExitOrMenu(last.result())) return; + + var ssn = input.readSSN("SSN"); + if (handleExitOrMenu(ssn.result())) return; + + var pass = input.readPassword("Password"); + if (handleExitOrMenu(pass.result())) return; + + long id = service.createAccount(first.value(), last.value(), ssn.value(), pass.value()); + System.out.println("\n✅ Account created with ID: " + id + " ✅\n"); + } catch (SQLException e) { + System.out.println("❌ Error creating account: " + e.getMessage()); + } + } + + public void updatePassword() { + try { + var idWrapper = input.readValidUserId("User ID"); + if (handleExitOrMenu(idWrapper.result())) return; + + var passWrapper = input.readPassword("New Password"); + if (handleExitOrMenu(passWrapper.result())) return; + + service.updatePassword(idWrapper.value(), passWrapper.value()); + System.out.println("\n✅ Password updated ✅\n"); + } catch (SQLException e) { + System.out.println("❌ Error updating password: " + e.getMessage()); + } + } + + public void deleteAccount() { + try { + var idWrapper = input.readValidUserId("User ID"); + if (handleExitOrMenu(idWrapper.result())) return; + + while (true) { + var confirmWrapper = input.readString("Are you sure you want to delete this account? (yes/no)"); + if (handleExitOrMenu(confirmWrapper.result())) return; + + String confirm = confirmWrapper.value(); + if (confirm.equalsIgnoreCase("yes") || confirm.equalsIgnoreCase("y")) { + service.deleteAccount(idWrapper.value()); + System.out.println("\n✅ Account deleted ✅\n"); + break; + } else if (confirm.equalsIgnoreCase("no") || confirm.equalsIgnoreCase("n")) { + System.out.println("❌ Account deletion cancelled ❌\n"); + break; + } else { + System.out.println("❌ Invalid input, type yes, no, or menu ❌"); + } + } + } catch (SQLException e) { + System.out.println("❌ Error deleting account: " + e.getMessage()); + } + } } From 6ddbfe30ffe9723bdb7b7ffb9b06c37a201e806a Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 12:00:22 +0100 Subject: [PATCH 47/61] add class RepositoryException to repository package --- .../java/com/example/repository/RepositoryException.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/com/example/repository/RepositoryException.java diff --git a/src/main/java/com/example/repository/RepositoryException.java b/src/main/java/com/example/repository/RepositoryException.java new file mode 100644 index 0000000..c28673f --- /dev/null +++ b/src/main/java/com/example/repository/RepositoryException.java @@ -0,0 +1,7 @@ +package com.example.repository; + +public class RepositoryException extends RuntimeException { + public RepositoryException(String message, Throwable cause) { + super(message, cause); + } +} From 089411ed255933e2a7891dde2bb5e145f0a5c3ac Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 12:01:29 +0100 Subject: [PATCH 48/61] add AccountService and MoonMissionService class to service package --- .../com/example/service/AccountService.java | 34 +++++++++++++++++++ .../example/service/MoonMissionService.java | 20 +++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/main/java/com/example/service/AccountService.java create mode 100644 src/main/java/com/example/service/MoonMissionService.java diff --git a/src/main/java/com/example/service/AccountService.java b/src/main/java/com/example/service/AccountService.java new file mode 100644 index 0000000..39e03ca --- /dev/null +++ b/src/main/java/com/example/service/AccountService.java @@ -0,0 +1,34 @@ +package com.example.service; + +import com.example.model.Account; +import com.example.repository.AccountRepository; + +import java.sql.SQLException; +import java.util.List; +import java.util.Optional; + +public class AccountService { + private final AccountRepository repo; + + public AccountService(AccountRepository repo) { this.repo = repo; } + + public boolean validateLogin(String username, String password) throws SQLException { + return repo.validateLogin(username, password); + } + + public long createAccount(String firstName, String lastName, String ssn, String password) throws SQLException { + return repo.createAccount(firstName, lastName, ssn, password); + } + + public void updatePassword(long userId, String newPassword) throws SQLException { + repo.updatePassword(userId, newPassword); + } + + public void deleteAccount(long userId) throws SQLException { + repo.deleteAccount(userId); + } + + public List listAccounts() throws SQLException { return repo.listAccounts(); } + + public Optional getById(long userId) throws SQLException { return repo.getById(userId); } +} diff --git a/src/main/java/com/example/service/MoonMissionService.java b/src/main/java/com/example/service/MoonMissionService.java new file mode 100644 index 0000000..b88377d --- /dev/null +++ b/src/main/java/com/example/service/MoonMissionService.java @@ -0,0 +1,20 @@ +package com.example.service; + +import com.example.model.MoonMission; +import com.example.repository.MoonMissionRepository; + +import java.sql.SQLException; +import java.util.List; +import java.util.Optional; + +public class MoonMissionService { + private final MoonMissionRepository repo; + + public MoonMissionService(MoonMissionRepository repo) { this.repo = repo; } + + public List listMissions() throws SQLException { return repo.listMissions(); } + + public Optional getMissionById(int id) throws SQLException { return repo.getMissionById(id); } + + public int countMissionsByYear(int year) throws SQLException { return repo.countMissionsByYear(year); } +} From 9d260275a0d81785a21ef4f4ce0f141f3d3cb5ce Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 12:02:32 +0100 Subject: [PATCH 49/61] update main and SimpleDriverManagerDataSource class --- src/main/java/com/example/Main.java | 25 +++++++++++++++ .../SimpleDriverManagerDataSource.java | 32 +++++++++++++++++-- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 8cba7ef..7edb737 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -9,10 +9,12 @@ public class Main { public static void main(String[] args) throws SQLException { + // Om devMode är på, initiera dev-databasen if (ConfigUtils.isDevMode(args)) { DevDatabaseInitializer.start(); } + // Hämta databaskonfiguration String jdbcUrl = ConfigUtils.resolveConfig("APP_JDBC_URL", "APP_JDBC_URL"); String dbUser = ConfigUtils.resolveConfig("APP_DB_USER", "APP_DB_USER"); String dbPass = ConfigUtils.resolveConfig("APP_DB_PASS", "APP_DB_PASS"); @@ -21,9 +23,32 @@ public static void main(String[] args) throws SQLException { throw new IllegalStateException("Missing DB configuration."); } + // Skapa datasource SimpleDriverManagerDataSource dataSource = new SimpleDriverManagerDataSource(jdbcUrl, dbUser, dbPass); boolean devMode = ConfigUtils.isDevMode(args); + // Skapa repositories + AccountRepositoryJdbc accountRepo = new AccountRepositoryJdbc(dataSource, devMode); + MoonMissionRepositoryJdbc missionRepo = new MoonMissionRepositoryJdbc(dataSource, devMode); + + // Skapa services + AccountService accountService = new AccountService(accountRepo); + MoonMissionService missionService = new MoonMissionService(missionRepo); + + // Skapa input reader + InputReader input = new InputReader(); + + // Skapa CLI-klasser + AccountCLI accountCLI = new AccountCLI(accountService, input); + MoonMissionCLI missionCLI = new MoonMissionCLI(missionService, input); + MenuCLI menu = new MenuCLI(accountCLI, missionCLI, input); + + // Skapa login manager (OBS: nu med InputReader) + LoginManager loginManager = new LoginManager(accountService, input); + + // Starta login, om lyckad, visa huvudmeny + if (loginManager.login()) { + menu.showMainMenu(); } } } diff --git a/src/main/java/com/example/SimpleDriverManagerDataSource.java b/src/main/java/com/example/SimpleDriverManagerDataSource.java index 4cf2145..bc1c15f 100644 --- a/src/main/java/com/example/SimpleDriverManagerDataSource.java +++ b/src/main/java/com/example/SimpleDriverManagerDataSource.java @@ -1,4 +1,32 @@ package com.example; -public class SimpleDriverManagerDataSource { -} \ No newline at end of file +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class SimpleDriverManagerDataSource implements DataSource { + private final String url; + private final String username; + private final String password; + + public SimpleDriverManagerDataSource(String url, String username, String password) { + this.url = url; + this.username = username; + this.password = password; + } + + @Override + public Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); } + + @Override + public Connection getConnection(String username, String password) throws SQLException { return DriverManager.getConnection(url, username, password); } + + @Override public T unwrap(Class iface) { throw new UnsupportedOperationException(); } + @Override public boolean isWrapperFor(Class iface) { return false; } + @Override public java.io.PrintWriter getLogWriter() { throw new UnsupportedOperationException(); } + @Override public void setLogWriter(java.io.PrintWriter out) { throw new UnsupportedOperationException(); } + @Override public void setLoginTimeout(int seconds) { throw new UnsupportedOperationException(); } + @Override public int getLoginTimeout() { return 0; } + @Override public java.util.logging.Logger getParentLogger() { throw new UnsupportedOperationException(); } +} From ce8c5acc20b0601bba821157f9dfe656cf108d06 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 12:09:22 +0100 Subject: [PATCH 50/61] update test deleteaccount with yes confirmation of deleting account --- src/main/java/com/example/cli/AccountCLI.java | 2 +- src/main/java/com/example/cli/MoonMissionCLI.java | 2 +- src/test/java/com/example/CliAppIT.java | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/example/cli/AccountCLI.java b/src/main/java/com/example/cli/AccountCLI.java index c4be514..2588e19 100644 --- a/src/main/java/com/example/cli/AccountCLI.java +++ b/src/main/java/com/example/cli/AccountCLI.java @@ -74,4 +74,4 @@ public void deleteAccount() { System.out.println("❌ Error deleting account: " + e.getMessage()); } } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/cli/MoonMissionCLI.java b/src/main/java/com/example/cli/MoonMissionCLI.java index ea64e3d..927e658 100644 --- a/src/main/java/com/example/cli/MoonMissionCLI.java +++ b/src/main/java/com/example/cli/MoonMissionCLI.java @@ -69,4 +69,4 @@ private void printMissionSummary(MoonMission m) { System.out.printf("Spacecraft: %s | Launch Date: %s | Rocket: %s | Operator: %s%n", m.spacecraft(), m.launchDate(), m.carrierRocket(), m.operator()); } -} +} \ No newline at end of file diff --git a/src/test/java/com/example/CliAppIT.java b/src/test/java/com/example/CliAppIT.java index 7d1eafb..984707d 100644 --- a/src/test/java/com/example/CliAppIT.java +++ b/src/test/java/com/example/CliAppIT.java @@ -179,6 +179,7 @@ void deleteAccount_thenRowIsGone_andPrintsConfirmation() throws Exception { "MB=V4cbAqPz4vqmQ", "6", // delete account (menu option 6 after reordering) Long.toString(userId),// user_id + "yes", // confirm delete ("y" eller "yes") "0" // exit ) + System.lineSeparator(); From 311755d3128cdd20b368f7c8ad556dbf734127fc Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 13:39:52 +0100 Subject: [PATCH 51/61] Refactor AccountCLI to display accounts using toString() in updatePassword and deleteAccount --- src/main/java/com/example/Main.java | 9 ----- .../SimpleDriverManagerDataSource.java | 2 +- src/main/java/com/example/cli/AccountCLI.java | 38 +++++++++++++++++++ .../java/com/example/cli/ExitMenuHandler.java | 2 +- .../example/repository/BaseRepository.java | 2 +- 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 7edb737..6b16e4d 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -9,12 +9,10 @@ public class Main { public static void main(String[] args) throws SQLException { - // Om devMode är på, initiera dev-databasen if (ConfigUtils.isDevMode(args)) { DevDatabaseInitializer.start(); } - // Hämta databaskonfiguration String jdbcUrl = ConfigUtils.resolveConfig("APP_JDBC_URL", "APP_JDBC_URL"); String dbUser = ConfigUtils.resolveConfig("APP_DB_USER", "APP_DB_USER"); String dbPass = ConfigUtils.resolveConfig("APP_DB_PASS", "APP_DB_PASS"); @@ -23,30 +21,23 @@ public static void main(String[] args) throws SQLException { throw new IllegalStateException("Missing DB configuration."); } - // Skapa datasource SimpleDriverManagerDataSource dataSource = new SimpleDriverManagerDataSource(jdbcUrl, dbUser, dbPass); boolean devMode = ConfigUtils.isDevMode(args); - // Skapa repositories AccountRepositoryJdbc accountRepo = new AccountRepositoryJdbc(dataSource, devMode); MoonMissionRepositoryJdbc missionRepo = new MoonMissionRepositoryJdbc(dataSource, devMode); - // Skapa services AccountService accountService = new AccountService(accountRepo); MoonMissionService missionService = new MoonMissionService(missionRepo); - // Skapa input reader InputReader input = new InputReader(); - // Skapa CLI-klasser AccountCLI accountCLI = new AccountCLI(accountService, input); MoonMissionCLI missionCLI = new MoonMissionCLI(missionService, input); MenuCLI menu = new MenuCLI(accountCLI, missionCLI, input); - // Skapa login manager (OBS: nu med InputReader) LoginManager loginManager = new LoginManager(accountService, input); - // Starta login, om lyckad, visa huvudmeny if (loginManager.login()) { menu.showMainMenu(); } diff --git a/src/main/java/com/example/SimpleDriverManagerDataSource.java b/src/main/java/com/example/SimpleDriverManagerDataSource.java index bc1c15f..f733cbb 100644 --- a/src/main/java/com/example/SimpleDriverManagerDataSource.java +++ b/src/main/java/com/example/SimpleDriverManagerDataSource.java @@ -29,4 +29,4 @@ public SimpleDriverManagerDataSource(String url, String username, String passwor @Override public void setLoginTimeout(int seconds) { throw new UnsupportedOperationException(); } @Override public int getLoginTimeout() { return 0; } @Override public java.util.logging.Logger getParentLogger() { throw new UnsupportedOperationException(); } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/cli/AccountCLI.java b/src/main/java/com/example/cli/AccountCLI.java index 2588e19..f0b820d 100644 --- a/src/main/java/com/example/cli/AccountCLI.java +++ b/src/main/java/com/example/cli/AccountCLI.java @@ -1,7 +1,9 @@ package com.example.cli; +import com.example.model.Account; import com.example.service.AccountService; import java.sql.SQLException; +import java.util.List; public class AccountCLI implements ExitMenuHandler { @@ -36,24 +38,60 @@ public void createAccount() { public void updatePassword() { try { + List accounts = service.listAccounts(); + if (accounts.isEmpty()) { + System.out.println("❌ No accounts found ❌"); + return; + } + + System.out.println("\n📋 Existing accounts:"); + for (Account acc : accounts) { + System.out.println(acc); // toString() används automatiskt + } + var idWrapper = input.readValidUserId("User ID"); if (handleExitOrMenu(idWrapper.result())) return; + if (service.getById(idWrapper.value()).isEmpty()) { + System.out.println("❌ Account with this ID does not exist ❌"); + return; + } + var passWrapper = input.readPassword("New Password"); if (handleExitOrMenu(passWrapper.result())) return; service.updatePassword(idWrapper.value(), passWrapper.value()); System.out.println("\n✅ Password updated ✅\n"); + } catch (SQLException e) { System.out.println("❌ Error updating password: " + e.getMessage()); } } + + + public void deleteAccount() { try { + List accounts = service.listAccounts(); + if (accounts.isEmpty()) { + System.out.println("❌ No accounts found ❌"); + return; + } + + System.out.println("\n📋 Existing accounts:"); + for (Account acc : accounts) { + System.out.println(acc); + } + var idWrapper = input.readValidUserId("User ID"); if (handleExitOrMenu(idWrapper.result())) return; + if (service.getById(idWrapper.value()).isEmpty()) { + System.out.println("❌ Account with this ID does not exist ❌"); + return; + } + while (true) { var confirmWrapper = input.readString("Are you sure you want to delete this account? (yes/no)"); if (handleExitOrMenu(confirmWrapper.result())) return; diff --git a/src/main/java/com/example/cli/ExitMenuHandler.java b/src/main/java/com/example/cli/ExitMenuHandler.java index 0b8ceaa..8ae17a6 100644 --- a/src/main/java/com/example/cli/ExitMenuHandler.java +++ b/src/main/java/com/example/cli/ExitMenuHandler.java @@ -9,4 +9,4 @@ default boolean handleExitOrMenu(InputReader.InputResult result) { } return result == InputReader.InputResult.MENU; } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/repository/BaseRepository.java b/src/main/java/com/example/repository/BaseRepository.java index 4ec37b9..41abd59 100644 --- a/src/main/java/com/example/repository/BaseRepository.java +++ b/src/main/java/com/example/repository/BaseRepository.java @@ -40,7 +40,7 @@ protected R executeQuery(String sql, SQLFunction handler, Obje return handler.apply(rs); } } catch (Exception e) { - throw dbError("executeQuery: " + sql, e); // Inget cast här heller + throw dbError("executeQuery: " + sql, e); } } From 81ccb5f0d4904e08b5d1971fa61043d4f63467d3 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 14:07:47 +0100 Subject: [PATCH 52/61] random commit --- src/main/java/com/example/Main.java | 4 ++-- src/main/java/com/example/repository/RepositoryException.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 6b16e4d..1b30b84 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -8,7 +8,7 @@ public class Main { - public static void main(String[] args) throws SQLException { + public static void main(String[] args) { if (ConfigUtils.isDevMode(args)) { DevDatabaseInitializer.start(); } @@ -42,4 +42,4 @@ public static void main(String[] args) throws SQLException { menu.showMainMenu(); } } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/repository/RepositoryException.java b/src/main/java/com/example/repository/RepositoryException.java index c28673f..7f74045 100644 --- a/src/main/java/com/example/repository/RepositoryException.java +++ b/src/main/java/com/example/repository/RepositoryException.java @@ -4,4 +4,4 @@ public class RepositoryException extends RuntimeException { public RepositoryException(String message, Throwable cause) { super(message, cause); } -} +} \ No newline at end of file From 7efbfd365377b238e4640b10448fcf8544d5b0c1 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 14:41:17 +0100 Subject: [PATCH 53/61] Refactor repositories and services to use unchecked RepositoryException --- src/main/java/com/example/cli/AccountCLI.java | 23 ++++---- .../java/com/example/cli/LoginManager.java | 4 +- .../java/com/example/cli/MoonMissionCLI.java | 10 ++-- .../example/repository/AccountRepository.java | 14 ++--- .../repository/MoonMissionRepository.java | 7 ++- .../com/example/service/AccountService.java | 54 ++++++++++++++----- .../example/service/MoonMissionService.java | 30 +++++++++-- 7 files changed, 93 insertions(+), 49 deletions(-) diff --git a/src/main/java/com/example/cli/AccountCLI.java b/src/main/java/com/example/cli/AccountCLI.java index f0b820d..fb00301 100644 --- a/src/main/java/com/example/cli/AccountCLI.java +++ b/src/main/java/com/example/cli/AccountCLI.java @@ -2,7 +2,8 @@ import com.example.model.Account; import com.example.service.AccountService; -import java.sql.SQLException; +import com.example.repository.RepositoryException; + import java.util.List; public class AccountCLI implements ExitMenuHandler { @@ -31,7 +32,8 @@ public void createAccount() { long id = service.createAccount(first.value(), last.value(), ssn.value(), pass.value()); System.out.println("\n✅ Account created with ID: " + id + " ✅\n"); - } catch (SQLException e) { + + } catch (RepositoryException e) { System.out.println("❌ Error creating account: " + e.getMessage()); } } @@ -45,9 +47,7 @@ public void updatePassword() { } System.out.println("\n📋 Existing accounts:"); - for (Account acc : accounts) { - System.out.println(acc); // toString() används automatiskt - } + accounts.forEach(System.out::println); var idWrapper = input.readValidUserId("User ID"); if (handleExitOrMenu(idWrapper.result())) return; @@ -63,14 +63,11 @@ public void updatePassword() { service.updatePassword(idWrapper.value(), passWrapper.value()); System.out.println("\n✅ Password updated ✅\n"); - } catch (SQLException e) { + } catch (RepositoryException e) { System.out.println("❌ Error updating password: " + e.getMessage()); } } - - - public void deleteAccount() { try { List accounts = service.listAccounts(); @@ -80,9 +77,7 @@ public void deleteAccount() { } System.out.println("\n📋 Existing accounts:"); - for (Account acc : accounts) { - System.out.println(acc); - } + accounts.forEach(System.out::println); var idWrapper = input.readValidUserId("User ID"); if (handleExitOrMenu(idWrapper.result())) return; @@ -108,8 +103,8 @@ public void deleteAccount() { System.out.println("❌ Invalid input, type yes, no, or menu ❌"); } } - } catch (SQLException e) { + } catch (RepositoryException e) { System.out.println("❌ Error deleting account: " + e.getMessage()); } } -} \ No newline at end of file +} diff --git a/src/main/java/com/example/cli/LoginManager.java b/src/main/java/com/example/cli/LoginManager.java index cb2979e..848cf3e 100644 --- a/src/main/java/com/example/cli/LoginManager.java +++ b/src/main/java/com/example/cli/LoginManager.java @@ -1,7 +1,7 @@ package com.example.cli; import com.example.service.AccountService; -import java.sql.SQLException; +import com.example.repository.RepositoryException; public class LoginManager implements ExitMenuHandler { @@ -37,7 +37,7 @@ public boolean login() { } else { System.out.println("❌ Invalid username or password ❌"); } - } catch (SQLException e) { + } catch (RepositoryException e) { System.out.println("❌ Error validating login: " + e.getMessage()); } diff --git a/src/main/java/com/example/cli/MoonMissionCLI.java b/src/main/java/com/example/cli/MoonMissionCLI.java index 927e658..ac82c5f 100644 --- a/src/main/java/com/example/cli/MoonMissionCLI.java +++ b/src/main/java/com/example/cli/MoonMissionCLI.java @@ -2,8 +2,8 @@ import com.example.model.MoonMission; import com.example.service.MoonMissionService; +import com.example.repository.RepositoryException; -import java.sql.SQLException; import java.util.Comparator; import java.util.List; import java.util.Optional; @@ -24,7 +24,7 @@ public void listMissions() { System.out.println("\n-- All Moon Missions --"); missions.forEach(m -> System.out.println(m.spacecraft())); System.out.println("----------------------\n"); - } catch (SQLException e) { + } catch (RepositoryException e) { System.out.println("❌ Error listing missions: " + e.getMessage()); } } @@ -43,7 +43,7 @@ public void getMissionById() { }, () -> System.out.println("❌ No mission with that ID ❌") ); - } catch (SQLException e) { + } catch (RepositoryException e) { System.out.println("❌ Error fetching mission: " + e.getMessage()); } } @@ -60,7 +60,7 @@ public void countMissionsByYear() { .sorted(Comparator.comparing(MoonMission::launchDate).reversed()) .forEach(this::printMissionSummary); System.out.println("-------------------\n"); - } catch (SQLException e) { + } catch (RepositoryException e) { System.out.println("❌ Error counting missions: " + e.getMessage()); } } @@ -69,4 +69,4 @@ private void printMissionSummary(MoonMission m) { System.out.printf("Spacecraft: %s | Launch Date: %s | Rocket: %s | Operator: %s%n", m.spacecraft(), m.launchDate(), m.carrierRocket(), m.operator()); } -} \ No newline at end of file +} diff --git a/src/main/java/com/example/repository/AccountRepository.java b/src/main/java/com/example/repository/AccountRepository.java index 71a0646..b1fdd20 100644 --- a/src/main/java/com/example/repository/AccountRepository.java +++ b/src/main/java/com/example/repository/AccountRepository.java @@ -7,10 +7,10 @@ import java.util.Optional; public interface AccountRepository { - boolean validateLogin(String username, String password) throws SQLException; - long createAccount(String firstName, String lastName, String ssn, String password) throws SQLException; - void updatePassword(long userId, String newPassword) throws SQLException; - void deleteAccount(long userId) throws SQLException; - List listAccounts() throws SQLException; - Optional getById(long userId) throws SQLException; -} + boolean validateLogin(String username, String password); + long createAccount(String firstName, String lastName, String ssn, String password); + void updatePassword(long userId, String newPassword); + void deleteAccount(long userId); + List listAccounts(); + Optional getById(long userId); +} \ No newline at end of file diff --git a/src/main/java/com/example/repository/MoonMissionRepository.java b/src/main/java/com/example/repository/MoonMissionRepository.java index 39f7ba2..c249f28 100644 --- a/src/main/java/com/example/repository/MoonMissionRepository.java +++ b/src/main/java/com/example/repository/MoonMissionRepository.java @@ -2,12 +2,11 @@ import com.example.model.MoonMission; -import java.sql.SQLException; import java.util.List; import java.util.Optional; public interface MoonMissionRepository { - List listMissions() throws SQLException; - Optional getMissionById(int missionId) throws SQLException; - int countMissionsByYear(int year) throws SQLException; + List listMissions(); + Optional getMissionById(int missionId); + int countMissionsByYear(int year); } \ No newline at end of file diff --git a/src/main/java/com/example/service/AccountService.java b/src/main/java/com/example/service/AccountService.java index 39e03ca..9b246f2 100644 --- a/src/main/java/com/example/service/AccountService.java +++ b/src/main/java/com/example/service/AccountService.java @@ -2,33 +2,63 @@ import com.example.model.Account; import com.example.repository.AccountRepository; +import com.example.repository.RepositoryException; -import java.sql.SQLException; import java.util.List; import java.util.Optional; public class AccountService { private final AccountRepository repo; - public AccountService(AccountRepository repo) { this.repo = repo; } + public AccountService(AccountRepository repo) { + this.repo = repo; + } - public boolean validateLogin(String username, String password) throws SQLException { - return repo.validateLogin(username, password); + public boolean validateLogin(String username, String password) { + try { + return repo.validateLogin(username, password); + } catch (Exception e) { + throw new RepositoryException("Error validating login", e); + } } - public long createAccount(String firstName, String lastName, String ssn, String password) throws SQLException { - return repo.createAccount(firstName, lastName, ssn, password); + public long createAccount(String firstName, String lastName, String ssn, String password) { + try { + return repo.createAccount(firstName, lastName, ssn, password); + } catch (Exception e) { + throw new RepositoryException("Error creating account", e); + } } - public void updatePassword(long userId, String newPassword) throws SQLException { - repo.updatePassword(userId, newPassword); + public void updatePassword(long userId, String newPassword) { + try { + repo.updatePassword(userId, newPassword); + } catch (Exception e) { + throw new RepositoryException("Error updating password", e); + } } - public void deleteAccount(long userId) throws SQLException { - repo.deleteAccount(userId); + public void deleteAccount(long userId) { + try { + repo.deleteAccount(userId); + } catch (Exception e) { + throw new RepositoryException("Error deleting account", e); + } } - public List listAccounts() throws SQLException { return repo.listAccounts(); } + public List listAccounts() { + try { + return repo.listAccounts(); + } catch (Exception e) { + throw new RepositoryException("Error listing accounts", e); + } + } - public Optional getById(long userId) throws SQLException { return repo.getById(userId); } + public Optional getById(long userId) { + try { + return repo.getById(userId); + } catch (Exception e) { + throw new RepositoryException("Error fetching account by ID", e); + } + } } diff --git a/src/main/java/com/example/service/MoonMissionService.java b/src/main/java/com/example/service/MoonMissionService.java index b88377d..bea1554 100644 --- a/src/main/java/com/example/service/MoonMissionService.java +++ b/src/main/java/com/example/service/MoonMissionService.java @@ -2,19 +2,39 @@ import com.example.model.MoonMission; import com.example.repository.MoonMissionRepository; +import com.example.repository.RepositoryException; -import java.sql.SQLException; import java.util.List; import java.util.Optional; public class MoonMissionService { private final MoonMissionRepository repo; - public MoonMissionService(MoonMissionRepository repo) { this.repo = repo; } + public MoonMissionService(MoonMissionRepository repo) { + this.repo = repo; + } - public List listMissions() throws SQLException { return repo.listMissions(); } + public List listMissions() { + try { + return repo.listMissions(); + } catch (Exception e) { + throw new RepositoryException("Error listing missions", e); + } + } - public Optional getMissionById(int id) throws SQLException { return repo.getMissionById(id); } + public Optional getMissionById(int id) { + try { + return repo.getMissionById(id); + } catch (Exception e) { + throw new RepositoryException("Error fetching mission by ID", e); + } + } - public int countMissionsByYear(int year) throws SQLException { return repo.countMissionsByYear(year); } + public int countMissionsByYear(int year) { + try { + return repo.countMissionsByYear(year); + } catch (Exception e) { + throw new RepositoryException("Error counting missions by year", e); + } + } } From b32a557baa3c728064b6dcc8247cc5530ff51878 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 14:42:27 +0100 Subject: [PATCH 54/61] remove comment from record --- src/main/java/com/example/model/Account.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/example/model/Account.java b/src/main/java/com/example/model/Account.java index a690a6b..d6df865 100644 --- a/src/main/java/com/example/model/Account.java +++ b/src/main/java/com/example/model/Account.java @@ -18,5 +18,4 @@ public String toString() { ", SSN: " + ssn + ", Name: " + name; } - // fix: skriv ut visuell record-account vid delete eller update password? } \ No newline at end of file From d5653e86f95a1726063155db7e3074475846c6ca Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 20:25:25 +0100 Subject: [PATCH 55/61] Refactor: replace manual year filtering with service.countMissionsByYear() --- src/main/java/com/example/Main.java | 1 - src/main/java/com/example/cli/AccountCLI.java | 2 +- .../java/com/example/cli/MoonMissionCLI.java | 19 ++++++++++++++----- .../example/service/MoonMissionService.java | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 1b30b84..40a1d8a 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -4,7 +4,6 @@ import com.example.repository.*; import com.example.service.*; -import java.sql.SQLException; public class Main { diff --git a/src/main/java/com/example/cli/AccountCLI.java b/src/main/java/com/example/cli/AccountCLI.java index fb00301..9587eea 100644 --- a/src/main/java/com/example/cli/AccountCLI.java +++ b/src/main/java/com/example/cli/AccountCLI.java @@ -107,4 +107,4 @@ public void deleteAccount() { System.out.println("❌ Error deleting account: " + e.getMessage()); } } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/cli/MoonMissionCLI.java b/src/main/java/com/example/cli/MoonMissionCLI.java index ac82c5f..c15fb1d 100644 --- a/src/main/java/com/example/cli/MoonMissionCLI.java +++ b/src/main/java/com/example/cli/MoonMissionCLI.java @@ -52,16 +52,25 @@ public void countMissionsByYear() { var yearWrapper = input.readInt("Year"); if (handleExitOrMenu(yearWrapper.result())) return; + int year = yearWrapper.value(); + try { + int count = service.countMissionsByYear(year); + + System.out.println("\n---- Missions for year " + year + " ----"); + System.out.println("Total missions: " + count); + + // 2) Lista alla missioner för året List missions = service.listMissions(); - System.out.println("\nMissions in " + yearWrapper.value() + " (most recent first):"); missions.stream() - .filter(m -> m.launchDate().toLocalDate().getYear() == yearWrapper.value()) + .filter(m -> m.launchDate().toLocalDate().getYear() == year) .sorted(Comparator.comparing(MoonMission::launchDate).reversed()) .forEach(this::printMissionSummary); - System.out.println("-------------------\n"); + + System.out.println("----------------------------------------\n"); + } catch (RepositoryException e) { - System.out.println("❌ Error counting missions: " + e.getMessage()); + System.out.println("❌ Error counting/listing missions: " + e.getMessage()); } } @@ -69,4 +78,4 @@ private void printMissionSummary(MoonMission m) { System.out.printf("Spacecraft: %s | Launch Date: %s | Rocket: %s | Operator: %s%n", m.spacecraft(), m.launchDate(), m.carrierRocket(), m.operator()); } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/service/MoonMissionService.java b/src/main/java/com/example/service/MoonMissionService.java index bea1554..0a77236 100644 --- a/src/main/java/com/example/service/MoonMissionService.java +++ b/src/main/java/com/example/service/MoonMissionService.java @@ -37,4 +37,4 @@ public int countMissionsByYear(int year) { throw new RepositoryException("Error counting missions by year", e); } } -} +} \ No newline at end of file From ba153500f3b6e30698366a3fe1b32496bc6d0a26 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 20:46:55 +0100 Subject: [PATCH 56/61] add javadoc comments to cli package --- src/main/java/com/example/cli/AccountCLI.java | 7 ++++++ .../java/com/example/cli/ExitMenuHandler.java | 10 ++++++++ .../java/com/example/cli/InputReader.java | 23 +++++++++++++++++++ .../java/com/example/cli/LoginManager.java | 18 +++++++++++++-- src/main/java/com/example/cli/MenuCLI.java | 16 +++++++++++++ .../java/com/example/cli/MoonMissionCLI.java | 18 ++++++++++++++- 6 files changed, 89 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/example/cli/AccountCLI.java b/src/main/java/com/example/cli/AccountCLI.java index 9587eea..bdc1311 100644 --- a/src/main/java/com/example/cli/AccountCLI.java +++ b/src/main/java/com/example/cli/AccountCLI.java @@ -6,6 +6,10 @@ import java.util.List; +/** + * CLI for managing accounts. + * Handles creating, updating, and deleting accounts via AccountService. + */ public class AccountCLI implements ExitMenuHandler { private final AccountService service; @@ -16,6 +20,7 @@ public AccountCLI(AccountService service, InputReader input) { this.input = input; } + /** Prompts the user and creates a new account. */ public void createAccount() { try { var first = input.readName("First name"); @@ -38,6 +43,7 @@ public void createAccount() { } } + /** Updates the password of an existing account after selecting by user ID. */ public void updatePassword() { try { List accounts = service.listAccounts(); @@ -68,6 +74,7 @@ public void updatePassword() { } } + /** Deletes an account after confirming with the user. */ public void deleteAccount() { try { List accounts = service.listAccounts(); diff --git a/src/main/java/com/example/cli/ExitMenuHandler.java b/src/main/java/com/example/cli/ExitMenuHandler.java index 8ae17a6..235126c 100644 --- a/src/main/java/com/example/cli/ExitMenuHandler.java +++ b/src/main/java/com/example/cli/ExitMenuHandler.java @@ -1,7 +1,17 @@ package com.example.cli; +/** + * Provides a default method to handle 'exit' or 'menu' commands from the user. + * Classes implementing this interface can easily check if the user wants to exit or return to the menu. + */ public interface ExitMenuHandler { + /** + * Handles input results indicating exit or menu commands. + * + * @param result the result from InputReader (CONTINUE, EXIT, MENU) + * @return true if the user wants to exit, false if continue or menu + */ default boolean handleExitOrMenu(InputReader.InputResult result) { if (result == InputReader.InputResult.EXIT) { System.out.println("Exiting..."); diff --git a/src/main/java/com/example/cli/InputReader.java b/src/main/java/com/example/cli/InputReader.java index 5a5b9a0..e3327da 100644 --- a/src/main/java/com/example/cli/InputReader.java +++ b/src/main/java/com/example/cli/InputReader.java @@ -3,14 +3,30 @@ import java.io.BufferedReader; import java.io.InputStreamReader; +/** + * Handles user input from the console with validation and special commands. + * Supports reading strings, integers, user IDs, names, SSNs, and passwords. + */ public class InputReader { private final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + /** + * Represents the result of reading input. + * CONTINUE = normal input, MENU = user wants to go back, EXIT = user wants to exit. + */ public enum InputResult { CONTINUE, MENU, EXIT } + /** + * Wraps a value read from the user along with the input result status. + * + * @param the type of the input value + * @param value the actual input value + * @param result the input result (CONTINUE, MENU, EXIT) + */ public record InputWrapper(T value, InputResult result) {} + // Reads a line from the console private String readLine() { try { String line = reader.readLine(); @@ -20,12 +36,14 @@ private String readLine() { } } + // Checks if input is '0' (exit) or 'menu' private InputResult checkExitOrMenu(String input) { if (input.equals("0")) return InputResult.EXIT; if (input.equalsIgnoreCase("menu")) return InputResult.MENU; return InputResult.CONTINUE; } + /** Reads a string from the user with exit/menu handling. */ public InputWrapper readString(String label) { System.out.print(label + ": "); System.out.flush(); @@ -34,6 +52,7 @@ public InputWrapper readString(String label) { return new InputWrapper<>(input, result); } + /** Reads an integer from the user with validation and exit/menu handling. */ public InputWrapper readInt(String label) { while (true) { InputWrapper wrapper = readString(label); @@ -47,6 +66,7 @@ public InputWrapper readInt(String label) { } } + /** Reads a valid user ID (long) from the user with validation and exit/menu handling. */ public InputWrapper readValidUserId(String label) { while (true) { InputWrapper wrapper = readString(label); @@ -60,6 +80,7 @@ public InputWrapper readValidUserId(String label) { } } + /** Reads a valid name from the user (capitalized, at least 3 letters). */ public InputWrapper readName(String label) { while (true) { InputWrapper wrapper = readString(label); @@ -70,6 +91,7 @@ public InputWrapper readName(String label) { } } + /** Reads a valid Swedish SSN from the user (######-####). */ public InputWrapper readSSN(String label) { while (true) { InputWrapper wrapper = readString(label); @@ -80,6 +102,7 @@ public InputWrapper readSSN(String label) { } } + /** Reads a password from the user. */ public InputWrapper readPassword(String label) { return readString(label); } diff --git a/src/main/java/com/example/cli/LoginManager.java b/src/main/java/com/example/cli/LoginManager.java index 848cf3e..7657055 100644 --- a/src/main/java/com/example/cli/LoginManager.java +++ b/src/main/java/com/example/cli/LoginManager.java @@ -3,24 +3,39 @@ import com.example.service.AccountService; import com.example.repository.RepositoryException; +/** + * Handles user login attempts, including input reading, validation, and retry limits. + */ public class LoginManager implements ExitMenuHandler { private final AccountService service; private final int maxAttempts; private final InputReader input; + /** + * Creates a LoginManager with default max attempts (5). + */ public LoginManager(AccountService service, InputReader input) { this(service, input, 5); } + /** + * Creates a LoginManager with a specified maximum number of login attempts. + * + * @param maxAttempts maximum allowed attempts before login fails + */ public LoginManager(AccountService service, InputReader input, int maxAttempts) { this.service = service; this.input = input; this.maxAttempts = maxAttempts; } + /** + * Performs the login process, asking for username and password. + * Returns true if login succeeds, false if attempts are exhausted or user exits. + */ public boolean login() { - System.out.println("Type 0 to exit anytime, or 'menu' to go back."); + System.out.println("Type 0 to exit anytime."); int attempts = 0; while (attempts < maxAttempts) { @@ -46,7 +61,6 @@ public boolean login() { System.out.println("Attempts left: " + (maxAttempts - attempts)); } } - return false; } } diff --git a/src/main/java/com/example/cli/MenuCLI.java b/src/main/java/com/example/cli/MenuCLI.java index 21ccc83..db94c82 100644 --- a/src/main/java/com/example/cli/MenuCLI.java +++ b/src/main/java/com/example/cli/MenuCLI.java @@ -1,17 +1,32 @@ package com.example.cli; +/** + * Handles the main menu display and routes user selections + * to the appropriate CLI handlers for accounts or moon missions. + */ public class MenuCLI { private final AccountCLI accountCLI; private final MoonMissionCLI missionCLI; private final InputReader input; + /** + * Creates a MenuCLI with the required CLI handlers and input reader. + * + * @param accountCLI CLI handler for account-related actions + * @param missionCLI CLI handler for moon mission-related actions + * @param input input reader for user interaction + */ public MenuCLI(AccountCLI accountCLI, MoonMissionCLI missionCLI, InputReader input) { this.accountCLI = accountCLI; this.missionCLI = missionCLI; this.input = input; } + /** + * Displays the main menu, handles user input, and routes commands + * to the appropriate CLI methods until the user exits. + */ public void showMainMenu() { while (true) { printHeader(); @@ -36,6 +51,7 @@ public void showMainMenu() { } } + /** Prints the menu header and available options. */ private void printHeader() { System.out.println("\n 🌕 MOON MISSION HUB 🌕 "); System.out.println("----------------------------------------"); diff --git a/src/main/java/com/example/cli/MoonMissionCLI.java b/src/main/java/com/example/cli/MoonMissionCLI.java index c15fb1d..4559324 100644 --- a/src/main/java/com/example/cli/MoonMissionCLI.java +++ b/src/main/java/com/example/cli/MoonMissionCLI.java @@ -8,16 +8,27 @@ import java.util.List; import java.util.Optional; +/** + * CLI handler for moon mission operations. + * Provides methods for listing, fetching by ID, and counting missions by year. + */ public class MoonMissionCLI implements ExitMenuHandler { private final MoonMissionService service; private final InputReader input; + /** + * Creates a MoonMissionCLI with the provided service and input reader. + * + * @param service service handling moon mission data + * @param input input reader for user interaction + */ public MoonMissionCLI(MoonMissionService service, InputReader input) { this.service = service; this.input = input; } + /** Lists all moon missions by spacecraft name. */ public void listMissions() { try { List missions = service.listMissions(); @@ -29,6 +40,7 @@ public void listMissions() { } } + /** Fetches and displays a mission by its ID. */ public void getMissionById() { var idWrapper = input.readInt("Mission ID"); if (handleExitOrMenu(idWrapper.result())) return; @@ -48,6 +60,10 @@ public void getMissionById() { } } + /** + * Counts missions for a given year, prints total and detailed summaries + * of missions sorted by launch date (most recent first). + */ public void countMissionsByYear() { var yearWrapper = input.readInt("Year"); if (handleExitOrMenu(yearWrapper.result())) return; @@ -60,7 +76,6 @@ public void countMissionsByYear() { System.out.println("\n---- Missions for year " + year + " ----"); System.out.println("Total missions: " + count); - // 2) Lista alla missioner för året List missions = service.listMissions(); missions.stream() .filter(m -> m.launchDate().toLocalDate().getYear() == year) @@ -74,6 +89,7 @@ public void countMissionsByYear() { } } + /** Prints a short summary for a single moon mission. */ private void printMissionSummary(MoonMission m) { System.out.printf("Spacecraft: %s | Launch Date: %s | Rocket: %s | Operator: %s%n", m.spacecraft(), m.launchDate(), m.carrierRocket(), m.operator()); From 1893076b91ca58b6234479d16591239403fbf3b5 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 20:48:23 +0100 Subject: [PATCH 57/61] add jacadoc comments to model package --- src/main/java/com/example/model/Account.java | 14 ++++++++++++++ src/main/java/com/example/model/MoonMission.java | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/main/java/com/example/model/Account.java b/src/main/java/com/example/model/Account.java index d6df865..dd40376 100644 --- a/src/main/java/com/example/model/Account.java +++ b/src/main/java/com/example/model/Account.java @@ -1,5 +1,15 @@ package com.example.model; +/** + * Represents a user account in the system. + * + * @param userId unique identifier for the account + * @param firstName first name of the account holder + * @param lastName last name of the account holder + * @param ssn social security number + * @param password account password + * @param name full display name of the account holder + */ public record Account( long userId, String firstName, @@ -9,6 +19,10 @@ public record Account( String name ) { + /** + * Returns a readable string representation of the account, + * excluding the password for security reasons. + */ @Override public String toString() { return "Account: " + diff --git a/src/main/java/com/example/model/MoonMission.java b/src/main/java/com/example/model/MoonMission.java index 977e3ce..5eafb84 100644 --- a/src/main/java/com/example/model/MoonMission.java +++ b/src/main/java/com/example/model/MoonMission.java @@ -2,6 +2,17 @@ import java.sql.Date; +/** + * Represents a moon mission with its main details. + * + * @param missionId unique identifier for the mission + * @param spacecraft name of the spacecraft + * @param launchDate launch date of the mission + * @param carrierRocket rocket used to carry the spacecraft + * @param operator organization responsible for the mission + * @param missionType type of mission (e.g., manned, unmanned) + * @param outcome result or outcome of the mission + */ public record MoonMission( int missionId, String spacecraft, @@ -12,6 +23,9 @@ public record MoonMission( String outcome ) { + /** + * Returns a readable string representation of the moon mission. + */ @Override public String toString() { return "MoonMission: " + From fb2a25ba48ba610d0734a74ca7dc13ea460e4457 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 20:54:29 +0100 Subject: [PATCH 58/61] add javadoc comments to repository package --- .../example/repository/AccountRepository.java | 3 ++ .../repository/AccountRepositoryJdbc.java | 16 +++++++ .../example/repository/BaseRepository.java | 46 +++++++++++++++++++ .../repository/MoonMissionRepository.java | 3 ++ .../repository/MoonMissionRepositoryJdbc.java | 16 +++++++ .../repository/RepositoryException.java | 10 ++++ 6 files changed, 94 insertions(+) diff --git a/src/main/java/com/example/repository/AccountRepository.java b/src/main/java/com/example/repository/AccountRepository.java index b1fdd20..fc5e062 100644 --- a/src/main/java/com/example/repository/AccountRepository.java +++ b/src/main/java/com/example/repository/AccountRepository.java @@ -6,6 +6,9 @@ import java.util.List; import java.util.Optional; +/** + * Interface defining the operations for managing accounts in the repository. + */ public interface AccountRepository { boolean validateLogin(String username, String password); long createAccount(String firstName, String lastName, String ssn, String password); diff --git a/src/main/java/com/example/repository/AccountRepositoryJdbc.java b/src/main/java/com/example/repository/AccountRepositoryJdbc.java index 5a0b03a..bdc5de6 100644 --- a/src/main/java/com/example/repository/AccountRepositoryJdbc.java +++ b/src/main/java/com/example/repository/AccountRepositoryJdbc.java @@ -6,12 +6,28 @@ import java.util.List; import java.util.Optional; +/** + * JDBC implementation of the {@link AccountRepository}. + * Handles database operations for Account objects. + */ public class AccountRepositoryJdbc extends BaseRepository implements AccountRepository { + /** + * Constructs the repository with a given DataSource. + * + * @param dataSource the database source + * @param devMode if true, enables debug logging + */ public AccountRepositoryJdbc(DataSource dataSource, boolean devMode) { super(dataSource, devMode); } + /** + * Maps a ResultSet row to an Account object. + * + * @param rs the ResultSet to map + * @return the Account object + */ @Override protected Account map(java.sql.ResultSet rs) throws java.sql.SQLException { return new Account( diff --git a/src/main/java/com/example/repository/BaseRepository.java b/src/main/java/com/example/repository/BaseRepository.java index 41abd59..c6bbeb7 100644 --- a/src/main/java/com/example/repository/BaseRepository.java +++ b/src/main/java/com/example/repository/BaseRepository.java @@ -6,10 +6,23 @@ import java.util.List; import java.util.Optional; + +/** + * Abstract base class for JDBC repositories. + * Provides common database operations such as query, update, and mapping. + * + * @param the type of entity this repository handles + */ public abstract class BaseRepository { protected final DataSource dataSource; protected final boolean devMode; + /** + * Constructs the repository with a DataSource. + * + * @param dataSource the database source + * @param devMode enables debug logging if true + */ protected BaseRepository(DataSource dataSource, boolean devMode) { this.dataSource = dataSource; this.devMode = devMode; @@ -18,19 +31,31 @@ protected BaseRepository(DataSource dataSource, boolean devMode) { } } + /** + * Gets a database connection from the DataSource. + */ protected Connection getConnection() throws SQLException { return dataSource.getConnection(); } + /** + * Logs messages if devMode is enabled. + */ protected void log(String msg) { if (devMode) System.out.println("[DEV] " + msg); } + /** + * Wraps database exceptions into a RepositoryException. + */ protected RepositoryException dbError(String action, Exception e) { log("ERROR during: " + action + " -> " + e.getMessage()); return new RepositoryException("Database error during: " + action, e); } + /** + * Executes a query and applies a handler function to the ResultSet. + */ protected R executeQuery(String sql, SQLFunction handler, Object... params) { try (Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { @@ -44,6 +69,9 @@ protected R executeQuery(String sql, SQLFunction handler, Obje } } + /** + * Executes an update/insert/delete SQL statement. + */ protected void executeUpdate(String sql, Object... params) { try (Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { @@ -55,6 +83,9 @@ protected void executeUpdate(String sql, Object... params) { } } + /** + * Executes an insert and returns the generated key. + */ protected long executeUpdateReturnId(String sql, Object... params) { try (Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { @@ -70,11 +101,23 @@ protected long executeUpdateReturnId(String sql, Object... params) { } } + /** + * Functional interface for processing ResultSets. + */ @FunctionalInterface protected interface SQLFunction { T apply(R result) throws Exception; } + /** + * Maps a ResultSet row to an entity. + * + * @param rs the ResultSet row + * @return the mapped entity + */ protected abstract T map(ResultSet rs) throws SQLException; + /** + * Executes a query and returns a list of entities. + */ protected List queryList(String sql, Object... params) { return executeQuery(sql, rs -> { List list = new ArrayList<>(); @@ -85,6 +128,9 @@ protected List queryList(String sql, Object... params) { }, params); } + /** + * Executes a query and returns a single entity wrapped in Optional. + */ protected Optional querySingle(String sql, Object... params) { return executeQuery(sql, rs -> { if (rs.next()) return Optional.of(map(rs)); diff --git a/src/main/java/com/example/repository/MoonMissionRepository.java b/src/main/java/com/example/repository/MoonMissionRepository.java index c249f28..f5bf1b8 100644 --- a/src/main/java/com/example/repository/MoonMissionRepository.java +++ b/src/main/java/com/example/repository/MoonMissionRepository.java @@ -5,6 +5,9 @@ import java.util.List; import java.util.Optional; +/** + * Interface defining the operations for managing MoonMissions in the repository. + */ public interface MoonMissionRepository { List listMissions(); Optional getMissionById(int missionId); diff --git a/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java b/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java index abac7cb..d9aca14 100644 --- a/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java +++ b/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java @@ -6,12 +6,28 @@ import java.util.List; import java.util.Optional; +/** + * JDBC implementation of MoonMissionRepository using a DataSource. + * Handles mapping of ResultSet to MoonMission objects and executing SQL queries. + */ public class MoonMissionRepositoryJdbc extends BaseRepository implements MoonMissionRepository { + /** + * Creates a new repository with the given DataSource and devMode flag. + * + * @param dataSource the DataSource to use for database connections + * @param devMode if true, prints debug information + */ public MoonMissionRepositoryJdbc(DataSource dataSource, boolean devMode) { super(dataSource, devMode); } + /** + * Maps a ResultSet row to a MoonMission object. + * + * @param rs the ResultSet to map + * @return a MoonMission object + */ @Override protected MoonMission map(java.sql.ResultSet rs) throws java.sql.SQLException { return new MoonMission( diff --git a/src/main/java/com/example/repository/RepositoryException.java b/src/main/java/com/example/repository/RepositoryException.java index 7f74045..9cd416f 100644 --- a/src/main/java/com/example/repository/RepositoryException.java +++ b/src/main/java/com/example/repository/RepositoryException.java @@ -1,6 +1,16 @@ package com.example.repository; +/** + * Custom unchecked exception for repository/database errors. + */ public class RepositoryException extends RuntimeException { + + /** + * Creates a new RepositoryException with a message and a cause. + * + * @param message descriptive error message + * @param cause the underlying exception that caused this error + */ public RepositoryException(String message, Throwable cause) { super(message, cause); } From c10196ba62596117ffd34b4b41cf5cb46423c5b6 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 20:56:14 +0100 Subject: [PATCH 59/61] add javadoc comments to service package --- src/main/java/com/example/service/AccountService.java | 4 ++++ src/main/java/com/example/service/MoonMissionService.java | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/main/java/com/example/service/AccountService.java b/src/main/java/com/example/service/AccountService.java index 9b246f2..7483fc2 100644 --- a/src/main/java/com/example/service/AccountService.java +++ b/src/main/java/com/example/service/AccountService.java @@ -7,6 +7,10 @@ import java.util.List; import java.util.Optional; +/** + * Service layer for managing accounts. Wraps AccountRepository + * and handles exceptions by throwing RepositoryException. + */ public class AccountService { private final AccountRepository repo; diff --git a/src/main/java/com/example/service/MoonMissionService.java b/src/main/java/com/example/service/MoonMissionService.java index 0a77236..9d11852 100644 --- a/src/main/java/com/example/service/MoonMissionService.java +++ b/src/main/java/com/example/service/MoonMissionService.java @@ -7,6 +7,10 @@ import java.util.List; import java.util.Optional; +/** + * Service layer for managing moon missions. Wraps MoonMissionRepository + * and handles exceptions by throwing RepositoryException. + */ public class MoonMissionService { private final MoonMissionRepository repo; From 9a9f51b295585b50ff895169d198e2927f8b4d46 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 20:59:00 +0100 Subject: [PATCH 60/61] add javdoc comments to main example package --- src/main/java/com/example/ConfigUtils.java | 20 +++++++++++++++++++ .../com/example/DevDatabaseInitializer.java | 4 ++++ src/main/java/com/example/Main.java | 14 ++++++++++++- .../SimpleDriverManagerDataSource.java | 16 +++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/ConfigUtils.java b/src/main/java/com/example/ConfigUtils.java index ae129e4..cde3447 100644 --- a/src/main/java/com/example/ConfigUtils.java +++ b/src/main/java/com/example/ConfigUtils.java @@ -2,13 +2,33 @@ import java.util.Arrays; +/** + * Utility class for reading configuration and environment variables. + */ public class ConfigUtils { + + /** + * Determines if the application should run in development mode. + * Checks system property "devMode", environment variable "DEV_MODE", + * and command-line argument "--dev". + * + * @param args command-line arguments + * @return true if dev mode is enabled, false otherwise + */ public static boolean isDevMode(String[] args) { if (Boolean.getBoolean("devMode")) return true; if ("true".equalsIgnoreCase(System.getenv("DEV_MODE"))) return true; return Arrays.asList(args).contains("--dev"); } + /** + * Resolves a configuration value from system properties or environment variables. + * Returns null if neither is set. + * + * @param propertyKey system property key + * @param envKey environment variable key + * @return trimmed configuration value or null if not set + */ public static String resolveConfig(String propertyKey, String envKey) { String v = System.getProperty(propertyKey); if (v == null || v.trim().isEmpty()) v = System.getenv(envKey); diff --git a/src/main/java/com/example/DevDatabaseInitializer.java b/src/main/java/com/example/DevDatabaseInitializer.java index 5526da9..b61fc2c 100644 --- a/src/main/java/com/example/DevDatabaseInitializer.java +++ b/src/main/java/com/example/DevDatabaseInitializer.java @@ -3,6 +3,10 @@ import org.testcontainers.containers.MySQLContainer; +/** + * Initializes a MySQL development database using Testcontainers. + * Sets system properties for JDBC URL, username, and password after startup. + */ public class DevDatabaseInitializer { private static MySQLContainer mysql; diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 40a1d8a..1b929c3 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -4,9 +4,21 @@ import com.example.repository.*; import com.example.service.*; - +/** + * Entry point for the Moon Mission application. + * + *

+ * Initializes the development database if dev mode is enabled, sets up repositories, + * services, and CLI components, handles user login, and shows the main menu. + *

+ */ public class Main { + /** + * Starts the application. + * + * @param args Command-line arguments. Supports "--dev" to enable dev mode. + */ public static void main(String[] args) { if (ConfigUtils.isDevMode(args)) { DevDatabaseInitializer.start(); diff --git a/src/main/java/com/example/SimpleDriverManagerDataSource.java b/src/main/java/com/example/SimpleDriverManagerDataSource.java index f733cbb..2109297 100644 --- a/src/main/java/com/example/SimpleDriverManagerDataSource.java +++ b/src/main/java/com/example/SimpleDriverManagerDataSource.java @@ -5,11 +5,27 @@ import java.sql.DriverManager; import java.sql.SQLException; +/** + * Simple DataSource implementation using DriverManager. + * + *

+ * Provides basic JDBC connections using a URL, username, and password. + * Only getConnection methods are supported; other DataSource features throw + * UnsupportedOperationException. + *

+ */ public class SimpleDriverManagerDataSource implements DataSource { private final String url; private final String username; private final String password; + /** + * Creates a new DataSource with the given JDBC parameters. + * + * @param url the JDBC URL + * @param username the database username + * @param password the database password + */ public SimpleDriverManagerDataSource(String url, String username, String password) { this.url = url; this.username = username; From c2ed679f9933c46a101d50cf52e28c4a993b7508 Mon Sep 17 00:00:00 2001 From: Kristina M Date: Thu, 11 Dec 2025 22:25:21 +0100 Subject: [PATCH 61/61] add more javadocs because coderabbit said so --- .../repository/AccountRepositoryJdbc.java | 39 +++++++++++++++ .../repository/MoonMissionRepositoryJdbc.java | 17 +++++++ .../com/example/service/AccountService.java | 49 +++++++++++++++++++ .../example/service/MoonMissionService.java | 25 ++++++++++ 4 files changed, 130 insertions(+) diff --git a/src/main/java/com/example/repository/AccountRepositoryJdbc.java b/src/main/java/com/example/repository/AccountRepositoryJdbc.java index bdc5de6..ea0a268 100644 --- a/src/main/java/com/example/repository/AccountRepositoryJdbc.java +++ b/src/main/java/com/example/repository/AccountRepositoryJdbc.java @@ -40,16 +40,35 @@ protected Account map(java.sql.ResultSet rs) throws java.sql.SQLException { ); } + /** + * Retrieves a list of all accounts in the database. + * + * @return a {@link List} of {@link Account} objects + */ @Override public List listAccounts() { return queryList("SELECT * FROM account"); } + /** + * Retrieves an account by its unique user ID. + * + * @param userId the ID of the account to retrieve + * @return an {@link Optional} containing the {@link Account} if found, otherwise empty + */ @Override public Optional getById(long userId) { return querySingle("SELECT * FROM account WHERE user_id=?", userId); } + /** + * Validates login credentials by checking if an account with the given + * username and password exists in the database. + * + * @param username the account's username + * @param password the account's password + * @return true if credentials match an account, false otherwise + */ @Override public boolean validateLogin(String username, String password) { return executeQuery( @@ -59,6 +78,15 @@ public boolean validateLogin(String username, String password) { ); } + /** + * Creates a new account with the given personal information. + * + * @param firstName the first name of the user + * @param lastName the last name of the user + * @param ssn the social security number of the user + * @param password the password for the account + * @return the generated user ID of the newly created account + */ @Override public long createAccount(String firstName, String lastName, String ssn, String password) { String name = firstName.substring(0, 3) + lastName.substring(0, 3); @@ -66,11 +94,22 @@ public long createAccount(String firstName, String lastName, String ssn, String return executeUpdateReturnId(sql, firstName, lastName, ssn, password, name); } + /** + * Updates the password for an existing account. + * + * @param userId the ID of the account to update + * @param newPassword the new password to set + */ @Override public void updatePassword(long userId, String newPassword) { executeUpdate("UPDATE account SET password=? WHERE user_id=?", newPassword, userId); } + /** + * Deletes an account from the database. + * + * @param userId the ID of the account to delete + */ @Override public void deleteAccount(long userId) { executeUpdate("DELETE FROM account WHERE user_id=?", userId); diff --git a/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java b/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java index d9aca14..845349a 100644 --- a/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java +++ b/src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java @@ -41,16 +41,33 @@ protected MoonMission map(java.sql.ResultSet rs) throws java.sql.SQLException { ); } + /** + * Retrieves a list of all moon missions in the database. + * + * @return a {@link List} of {@link MoonMission} objects + */ @Override public List listMissions() { return queryList("SELECT * FROM moon_mission"); } + /** + * Retrieves a moon mission by its unique mission ID. + * + * @param missionId the ID of the mission to retrieve + * @return an {@link Optional} containing the {@link MoonMission} if found, otherwise empty + */ @Override public Optional getMissionById(int missionId) { return querySingle("SELECT * FROM moon_mission WHERE mission_id=?", missionId); } + /** + * Counts the number of moon missions launched in a specific year. + * + * @param year the year to count missions for + * @return the number of missions launched in the given year + */ @Override public int countMissionsByYear(int year) { return executeQuery( diff --git a/src/main/java/com/example/service/AccountService.java b/src/main/java/com/example/service/AccountService.java index 7483fc2..ba0d6a7 100644 --- a/src/main/java/com/example/service/AccountService.java +++ b/src/main/java/com/example/service/AccountService.java @@ -14,10 +14,23 @@ public class AccountService { private final AccountRepository repo; + /** + * Constructs the service with a given repository. + * + * @param repo the {@link AccountRepository} to use for data access + */ public AccountService(AccountRepository repo) { this.repo = repo; } + /** + * Validates a user's login credentials. + * + * @param username the username to validate + * @param password the password to validate + * @return true if the credentials are valid, false otherwise + * @throws RepositoryException if an error occurs while accessing the repository + */ public boolean validateLogin(String username, String password) { try { return repo.validateLogin(username, password); @@ -26,6 +39,16 @@ public boolean validateLogin(String username, String password) { } } + /** + * Creates a new account in the system. + * + * @param firstName the first name of the user + * @param lastName the last name of the user + * @param ssn the Swedish personal identity number (######-####) + * @param password the password for the account + * @return the generated user ID of the new account + * @throws RepositoryException if an error occurs while creating the account + */ public long createAccount(String firstName, String lastName, String ssn, String password) { try { return repo.createAccount(firstName, lastName, ssn, password); @@ -34,6 +57,13 @@ public long createAccount(String firstName, String lastName, String ssn, String } } + /** + * Updates the password for an existing account. + * + * @param userId the ID of the account to update + * @param newPassword the new password to set + * @throws RepositoryException if an error occurs while updating the password + */ public void updatePassword(long userId, String newPassword) { try { repo.updatePassword(userId, newPassword); @@ -42,6 +72,12 @@ public void updatePassword(long userId, String newPassword) { } } + /** + * Deletes an account by its user ID. + * + * @param userId the ID of the account to delete + * @throws RepositoryException if an error occurs while deleting the account + */ public void deleteAccount(long userId) { try { repo.deleteAccount(userId); @@ -50,6 +86,12 @@ public void deleteAccount(long userId) { } } + /** + * Lists all accounts in the system. + * + * @return a {@link List} of {@link Account} objects + * @throws RepositoryException if an error occurs while retrieving accounts + */ public List listAccounts() { try { return repo.listAccounts(); @@ -58,6 +100,13 @@ public List listAccounts() { } } + /** + * Retrieves an account by its user ID. + * + * @param userId the ID of the account to retrieve + * @return an {@link Optional} containing the {@link Account} if found, otherwise empty + * @throws RepositoryException if an error occurs while fetching the account + */ public Optional getById(long userId) { try { return repo.getById(userId); diff --git a/src/main/java/com/example/service/MoonMissionService.java b/src/main/java/com/example/service/MoonMissionService.java index 9d11852..66538d4 100644 --- a/src/main/java/com/example/service/MoonMissionService.java +++ b/src/main/java/com/example/service/MoonMissionService.java @@ -14,10 +14,21 @@ public class MoonMissionService { private final MoonMissionRepository repo; + /** + * Constructs the service with a given repository. + * + * @param repo the {@link MoonMissionRepository} to use for data access + */ public MoonMissionService(MoonMissionRepository repo) { this.repo = repo; } + /** + * Lists all moon missions in the repository. + * + * @return a {@link List} of {@link MoonMission} objects + * @throws RepositoryException if an error occurs while retrieving missions + */ public List listMissions() { try { return repo.listMissions(); @@ -26,6 +37,13 @@ public List listMissions() { } } + /** + * Retrieves a moon mission by its ID. + * + * @param id the ID of the mission to fetch + * @return an {@link Optional} containing the {@link MoonMission} if found, otherwise empty + * @throws RepositoryException if an error occurs while fetching the mission + */ public Optional getMissionById(int id) { try { return repo.getMissionById(id); @@ -34,6 +52,13 @@ public Optional getMissionById(int id) { } } + /** + * Counts the number of moon missions that occurred in a specific year. + * + * @param year the year to count missions for + * @return the number of missions in the given year + * @throws RepositoryException if an error occurs while counting missions + */ public int countMissionsByYear(int year) { try { return repo.countMissionsByYear(year);