diff --git a/pom.xml b/pom.xml index 002ff66..5bd8e26 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,11 @@ mysql 1.21.3 + + org.slf4j + slf4j-nop + 2.0.17 + diff --git a/src/main/java/com/example/AccountRepository.java b/src/main/java/com/example/AccountRepository.java new file mode 100644 index 0000000..674ea30 --- /dev/null +++ b/src/main/java/com/example/AccountRepository.java @@ -0,0 +1,10 @@ +package com.example; + +import java.sql.SQLException; + +public interface AccountRepository { + boolean findByNameAndPassword(String username, String password) throws SQLException; + int create(String firstName, String lastName, String ssn, String password, String username) throws SQLException; + int updatePassword(long userId, String newPassword) throws SQLException; + int delete(long userId) throws SQLException; +} diff --git a/src/main/java/com/example/DataSource.java b/src/main/java/com/example/DataSource.java new file mode 100644 index 0000000..3f2e44d --- /dev/null +++ b/src/main/java/com/example/DataSource.java @@ -0,0 +1,8 @@ +package com.example; + +import java.sql.Connection; +import java.sql.SQLException; + +public interface DataSource { + Connection getConnection() throws SQLException; +} diff --git a/src/main/java/com/example/JdbcAccountRepository.java b/src/main/java/com/example/JdbcAccountRepository.java new file mode 100644 index 0000000..210e6f2 --- /dev/null +++ b/src/main/java/com/example/JdbcAccountRepository.java @@ -0,0 +1,67 @@ +package com.example; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +public class JdbcAccountRepository implements AccountRepository { + + private final DataSource dataSource; + + public JdbcAccountRepository(DataSource dataSource) { + this.dataSource = dataSource; + } + + @Override + public boolean findByNameAndPassword(String username, String password) throws SQLException { + String sql = "SELECT user_id FROM account WHERE name = ? AND password = ?"; + // Använder try-with-resources för att säkerställa att Connection stängs + try (Connection connection = dataSource.getConnection(); + PreparedStatement stmt = connection.prepareStatement(sql)) { + stmt.setString(1, username); + stmt.setString(2, password); + try (ResultSet rs = stmt.executeQuery()) { + return rs.next(); + } + } + } + + @Override + public int create(String firstName, String lastName, String ssn, String password, String username) throws SQLException { + String sql = "INSERT INTO account (first_name, last_name, ssn, password, name) VALUES (?, ?, ?, ?, ?)"; + try (Connection connection = dataSource.getConnection(); + PreparedStatement stmt = connection.prepareStatement(sql)) { + stmt.setString(1, firstName); + stmt.setString(2, lastName); + stmt.setString(3, ssn); + stmt.setString(4, password); + stmt.setString(5, username); + + return stmt.executeUpdate(); + } + } + + @Override + public int updatePassword(long userId, String newPassword) throws SQLException { + String sql = "UPDATE account SET password = ? WHERE user_id = ?"; + try (Connection connection = dataSource.getConnection(); + PreparedStatement stmt = connection.prepareStatement(sql)) { + stmt.setString(1, newPassword); + stmt.setLong(2, userId); + + return stmt.executeUpdate(); + } + } + + @Override + public int delete(long userId) throws SQLException { + String sql = "DELETE FROM account WHERE user_id = ?"; + try (Connection connection = dataSource.getConnection(); + PreparedStatement stmt = connection.prepareStatement(sql)) { + stmt.setLong(1, userId); + + return stmt.executeUpdate(); + } + } +} diff --git a/src/main/java/com/example/JdbcMoonMissionRepository.java b/src/main/java/com/example/JdbcMoonMissionRepository.java new file mode 100644 index 0000000..0e7ce9c --- /dev/null +++ b/src/main/java/com/example/JdbcMoonMissionRepository.java @@ -0,0 +1,56 @@ +package com.example; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +public class JdbcMoonMissionRepository implements MoonMissionRepository { + + private final DataSource dataSource; + + public JdbcMoonMissionRepository(DataSource dataSource) { + this.dataSource = dataSource; + } + + @Override + public List findAllSpacecraftNames() throws SQLException { + List names = new ArrayList<>(); + String sql = "SELECT spacecraft FROM moon_mission"; + try (Connection connection = dataSource.getConnection(); + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + names.add(rs.getString("spacecraft")); + } + } + return names; + } + + + @Override + public ResultSet findMissionById(long missionId, Connection connection) throws SQLException { + String sql = "SELECT * FROM moon_mission WHERE mission_id = ?"; + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setLong(1, missionId); + return stmt.executeQuery(); + } + + @Override + public int countMissionsByYear(int year) throws SQLException { + String sql = "SELECT COUNT(*) FROM moon_mission WHERE YEAR(launch_date) = ?"; + try (Connection connection = dataSource.getConnection(); + PreparedStatement stmt = connection.prepareStatement(sql)) { + stmt.setInt(1, year); + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + return rs.getInt(1); + } + return 0; + } + } + } +} diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 6dc6fbd..db318b8 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -1,13 +1,19 @@ package com.example; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; +import java.sql.*; import java.util.Arrays; +import java.util.InputMismatchException; +import java.util.List; +import java.util.Scanner; public class Main { - static void main(String[] args) { + private final Scanner scanner = new Scanner(System.in); + private AccountRepository accountRepository; + private MoonMissionRepository moonMissionRepository; + private DataSource dataSource; // Behålls för att hantera anslutning i getMoonMissionById + + public static void main(String[] args) { if (isDevMode(args)) { DevDatabaseInitializer.start(); } @@ -15,31 +21,236 @@ static void main(String[] args) { } 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"); - 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."); + String JDBC_URL = resolveConfig("APP_JDBC_URL", "APP_JDBC_URL"); + String DB_USER = resolveConfig("APP_DB_USER", "APP_DB_USER"); + String DB_PASS = resolveConfig("APP_DB_PASS", "APP_DB_PASS"); + + if (JDBC_URL == null || DB_USER == null || DB_PASS == null) { + System.err.println("Database configuration environment variables not set."); + return; } - try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) { + // --- Konfigurera Repository Pattern --- + dataSource = new SimpleDriverManagerDataSource(JDBC_URL, DB_USER, DB_PASS); + accountRepository = new JdbcAccountRepository(dataSource); + moonMissionRepository = new JdbcMoonMissionRepository(dataSource); + // --- Slut Konfiguration --- + + System.out.println("Application initialized."); + + try { + // Vi använder dataSource.getConnection() här för att testa uppkopplingen + // och bekräfta att den fungerar. + try (Connection testConnection = dataSource.getConnection()) { + System.out.println("Database connection established."); + } + + if (login()) { + mainMenu(); + } else { + handleInvalidLogin(); + } } catch (SQLException e) { - throw new RuntimeException(e); + System.err.println("Database error: " + e.getMessage()); } - //Todo: Starting point for your code + // Vi behöver inte stänga en global 'connection' längre, + // eftersom varje Repository-metod hanterar sin egen Connection. } - /** - * 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 - */ + // --- Inloggning --- + + private boolean login() throws SQLException { + System.out.print("Username: "); + String username = scanner.nextLine(); + System.out.print("Password: "); + String password = scanner.nextLine(); + + // Använder AccountRepository + return accountRepository.findByNameAndPassword(username, password); + } + + + private void handleInvalidLogin() { + System.out.println("Invalid username or password. Press 0 to exit."); + while (true) { + System.out.print("Choice: "); + String choice = scanner.nextLine().trim(); + if ("0".equals(choice)) { + break; + } + } + } + + // --- Menyhantering --- + + private void mainMenu() { + // ... (mainMenu och printMenu förblir oförändrade, förutom anropen nedan) ... + int choice = -1; + while (choice != 0) { + printMenu(); + try { + System.out.print("Choice: "); + choice = scanner.nextInt(); + scanner.nextLine(); + + switch (choice) { + case 1: + listMoonMissions(); + break; + case 2: + getMoonMissionById(); + break; + case 3: + countMissionsByYear(); + break; + case 4: + createAccount(); + break; + case 5: + updateAccountPassword(); + break; + case 6: + deleteAccount(); + break; + case 0: + System.out.println("Exiting application."); + break; + default: + System.out.println("Invalid choice. Try again."); + } + } catch (InputMismatchException e) { + System.out.println("Invalid input. Please enter a number."); + scanner.nextLine(); + } catch (SQLException e) { + System.err.println("Database operation failed: " + e.getMessage()); + } + } + } + + private void printMenu() { + System.out.println("\n--- Menu ---"); + System.out.println("1) List moon missions (spacecraft names)"); + System.out.println("2) Get a moon mission by mission_id (details)"); + System.out.println("3) Count missions for a given year"); + System.out.println("4) Create an account (first name, last name, ssn, password)"); + System.out.println("5) Update an account password (user_id, new password)"); + System.out.println("6) Delete an account (user_id)"); + System.out.println("0) Exit"); + System.out.println("------------"); + } + + + // 1) List moon missions + private void listMoonMissions() throws SQLException { + // Använder MoonMissionRepository + List spacecrafts = moonMissionRepository.findAllSpacecraftNames(); + System.out.println("--- Moon Missions ---"); + for (String name : spacecrafts) { + System.out.println(" - " + name); + } + } + + // 2) Get a moon mission by mission_id + private void getMoonMissionById() throws SQLException { + System.out.print("Enter mission_id: "); + long missionId; + try { + missionId = scanner.nextLong(); + scanner.nextLine(); + }catch (InputMismatchException e){ + System.out.println("Please enter a number"); + return; + } + + try (Connection connection = dataSource.getConnection(); + ResultSet rs = moonMissionRepository.findMissionById(missionId, connection)) { + + if (rs.next()) { + System.out.println("--- Mission Details (ID: " + missionId + ") ---"); + System.out.println("Spacecraft: " + rs.getString("spacecraft")); + System.out.println("Launch Date: " + rs.getDate("launch_date")); + System.out.println("Outcome: " + rs.getString("outcome")); + } else { + System.out.println("Mission with ID " + missionId + " not found."); + } + } + } + + // 3) Count missions for a given year + private void countMissionsByYear() throws SQLException { + System.out.print("Enter year: "); + int year = scanner.nextInt(); + scanner.nextLine(); + + // Använder MoonMissionRepository + int count = moonMissionRepository.countMissionsByYear(year); + System.out.println("Found " + count + " missions launched in " + year + "."); + } + + // 4) Create an account + private void createAccount() throws SQLException { + System.out.print("Enter first name: "); + String firstName = scanner.nextLine(); + System.out.print("Enter last name: "); + String lastName = scanner.nextLine(); + System.out.print("Enter ssn: "); + String ssn = scanner.nextLine(); + System.out.print("Enter password: "); + String password = scanner.nextLine(); + + String username = firstName.substring(0, Math.min(firstName.length(), 3)) + + lastName.substring(0, Math.min(lastName.length(), 3)); + + if (username.isEmpty()) { + System.out.println("Cannot create account: first name and last name cannot both be empty."); + return; + } + + // Använder AccountRepository + int rowsAffected = accountRepository.create(firstName, lastName, ssn, password, username); + + if (rowsAffected > 0) { + System.out.println("Account created successfully. Username is " + username + "."); + } else { + System.out.println("Failed to create account."); + } + } + + // 5) Update an account password + private void updateAccountPassword() throws SQLException { + System.out.print("Enter user_id to update: "); + long userId = scanner.nextLong(); + scanner.nextLine(); + System.out.print("Enter new password: "); + String newPassword = scanner.nextLine(); + + // Använder AccountRepository + int rowsAffected = accountRepository.updatePassword(userId, newPassword); + if (rowsAffected > 0) { + System.out.println("Account password updated successfully for user_id " + userId + "."); + } else { + System.out.println("No account found with user_id " + userId + " to update."); + } + } + + // 6) Delete an account + private void deleteAccount() throws SQLException { + System.out.print("Enter user_id to delete: "); + long userId = scanner.nextLong(); + scanner.nextLine(); + + // Använder AccountRepository + int rowsAffected = accountRepository.delete(userId); + + if (rowsAffected > 0) { + System.out.println("Account deleted successfully for user_id " + userId + "."); + } else { + System.out.println("No account found with user_id " + userId + " to delete."); + } + } + + private static boolean isDevMode(String[] args) { if (Boolean.getBoolean("devMode")) //Add VM option -DdevMode=true return true; @@ -59,4 +270,4 @@ private static String resolveConfig(String propertyKey, String envKey) { } return (v == null || v.trim().isEmpty()) ? null : v.trim(); } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/MoonMissionRepository.java b/src/main/java/com/example/MoonMissionRepository.java new file mode 100644 index 0000000..bcb0a36 --- /dev/null +++ b/src/main/java/com/example/MoonMissionRepository.java @@ -0,0 +1,12 @@ +package com.example; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +public interface MoonMissionRepository { + List findAllSpacecraftNames() throws SQLException; + ResultSet findMissionById(long missionId, Connection connection) throws SQLException; + int countMissionsByYear(int year) throws SQLException; +} diff --git a/src/main/java/com/example/SimpleDriverManagerDataSource.java b/src/main/java/com/example/SimpleDriverManagerDataSource.java new file mode 100644 index 0000000..80f7e06 --- /dev/null +++ b/src/main/java/com/example/SimpleDriverManagerDataSource.java @@ -0,0 +1,24 @@ +package com.example; + +import java.sql.DriverManager; +import java.sql.Connection; +import java.sql.SQLException; + +public class SimpleDriverManagerDataSource implements DataSource { + private final String url; + private final String user; + private final String password; + + public SimpleDriverManagerDataSource(String url, String user, String password) { + this.url = url; + this.user = user; + this.password = password; + } + + @Override + public Connection getConnection() throws SQLException { + // Skapar en ny Connection varje gång den anropas. + + return DriverManager.getConnection(url, user, password); + } +}