diff --git a/cmd/jws/projects/jakarta-rest-todo/.devcontainer/.dockerignore b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/.dockerignore
new file mode 100644
index 0000000..017b280
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/.dockerignore
@@ -0,0 +1,8 @@
+.git
+.github
+.settings
+target
+*.iml
+.idea
+.vscode
+node_modules
diff --git a/cmd/jws/projects/jakarta-rest-todo/.devcontainer/.gitignore b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/.gitignore
new file mode 100644
index 0000000..82b069a
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/.gitignore
@@ -0,0 +1,27 @@
+# Maven
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+# IDE-spezifische Dateien
+.idea/
+*.iws
+*.iml
+*.ipr
+.vscode/
+.settings/
+.classpath
+.project
+.factorypath
+
+# Temporäre Dateien
+*.log
+*.tmp
+*.temp
+
+# Lokale Konfigurationsdateien
+src/main/resources/application-local.properties
+
+# Devcontainer Volumen
+.devcontainer/postgres-data/
diff --git a/cmd/jws/projects/jakarta-rest-todo/.devcontainer/Dockerfile b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/Dockerfile
new file mode 100644
index 0000000..e9d615d
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/Dockerfile
@@ -0,0 +1,12 @@
+FROM icr.io/appcafe/open-liberty:23.0.0.4-kernel-slim-java17-openj9-ubi
+
+COPY --chown=1001:0 src/main/liberty/config /config
+# # Verzeichnis erstellen und PostgreSQL JDBC-Treiber herunterladen
+RUN mkdir -p /config/lib && \
+ curl -o /config/lib/postgresql.jar https://jdbc.postgresql.org/download/postgresql-42.6.0.jar
+
+RUN features.sh
+
+#COPY --chown=1001:0 target/*.war /config/apps
+
+RUN configure.sh
diff --git a/cmd/jws/projects/jakarta-rest-todo/.devcontainer/Dockerfile.dev b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/Dockerfile.dev
new file mode 100644
index 0000000..412eb57
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/Dockerfile.dev
@@ -0,0 +1,14 @@
+FROM mcr.microsoft.com/devcontainers/java:1-21-bullseye
+
+#ARG INSTALL_MAVEN="true"
+#ARG MAVEN_VERSION=""
+
+#ARG INSTALL_GRADLE="false"
+#ARG GRADLE_VERSION=""
+
+#RUN if [ "${INSTALL_MAVEN}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install maven \"${MAVEN_VERSION}\""; fi \
+# && if [ "${INSTALL_GRADLE}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install gradle \"${GRADLE_VERSION}\""; fi
+
+# Hier kannst du zusätzliche Tools oder Bibliotheken installieren, die du für die Entwicklung benötigst.
+# Zum Beispiel den PostgreSQL JDBC-Treiber, wenn du ihn für die Entwicklung benötigst:
+RUN apt-get update && apt-get install -y curl && curl -o /tmp/postgresql.jar https://jdbc.postgresql.org/download/postgresql-42.6.0.jar && mkdir -p /usr/local/lib/ && mv /tmp/postgresql.jar /usr/local/lib/postgresql.jar
diff --git a/cmd/jws/projects/jakarta-rest-todo/.devcontainer/devcontainer.json b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..a1901d5
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/devcontainer.json
@@ -0,0 +1,14 @@
+{
+ "name": "Java & PostgreSQL",
+ "dockerComposeFile": "docker-compose.yml",
+ "service": "app",
+ "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
+ "forwardPorts": [9080, 5432, 9433],
+ "postCreateCommand": "mvn clean install -DskipTests",
+ "remoteUser": "vscode",
+ "features": {
+ "ghcr.io/devcontainers/features/docker-in-docker:2": {"version": "latest"},
+ "ghcr.io/devcontainers-extra/features/maven-sdkman:2": {"version": "latest"},
+ "ghcr.io/devcontainers/features/git:1": {"version": "latest"}
+}
+}
diff --git a/cmd/jws/projects/jakarta-rest-todo/.devcontainer/docker-compose.yml b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/docker-compose.yml
new file mode 100644
index 0000000..3141d45
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/docker-compose.yml
@@ -0,0 +1,60 @@
+services:
+ app:
+ container_name: javadev
+ build:
+ context: ../
+ dockerfile: .devcontainer/Dockerfile.dev
+ volumes:
+ - ../..:/workspaces:cached
+ - ./app:/app
+ environment:
+ POSTGRES_HOST: postgresdb
+ POSTGRES_PORT: 5432
+ POSTGRES_DB: postgres
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: postgres
+ networks:
+ - my_network
+ command: sleep infinity
+
+ openliberty:
+ container_name: openliberty_app
+ build:
+ context: ../
+ dockerfile: .devcontainer/Dockerfile
+ ports:
+ - "9080:9080"
+ - "9443:9443"
+ environment:
+ POSTGRES_HOST: postgresdb
+ POSTGRES_PORT: 5432
+ POSTGRES_DB: todo_db
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: postgres
+ volumes:
+ - ./app:/config/dropins
+ depends_on:
+ - db
+ networks:
+ - my_network
+
+ db:
+ container_name: postgresdb
+ image: postgres:latest
+ restart: unless-stopped
+ volumes:
+ - postgres-data:/var/lib/postgresql/data
+ - ./init.sql:/docker-entrypoint-initdb.d/init.sql
+ environment:
+ POSTGRES_PASSWORD: postgres
+ POSTGRES_USER: postgres
+ POSTGRES_DB: todo_db
+ networks:
+ - my_network
+
+volumes:
+ postgres-data:
+
+networks:
+ my_network:
+ driver: bridge
diff --git a/cmd/jws/projects/jakarta-rest-todo/.devcontainer/init.sql b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/init.sql
new file mode 100644
index 0000000..818eadb
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/.devcontainer/init.sql
@@ -0,0 +1,19 @@
+CREATE TABLE users (
+ id SERIAL PRIMARY KEY,
+ username VARCHAR(255) NOT NULL UNIQUE,
+ password VARCHAR(255) NOT NULL,
+ email VARCHAR(255) NOT NULL
+);
+
+CREATE TABLE todos (
+ id SERIAL PRIMARY KEY,
+ title VARCHAR(255) NOT NULL,
+ description VARCHAR(1000),
+ target_date DATE,
+ completed BOOLEAN,
+ user_id BIGINT NOT NULL,
+ FOREIGN KEY (user_id) REFERENCES users(id)
+);
+
+INSERT INTO users (username, password, email) VALUES
+('test', '$2a$12$nt7xQKNDKZhxQFZGD5Wy0.Uh0wdPtWDgwfnWnPLgBWnQDGGkNLKBi', 'test@example.com');
diff --git a/cmd/jws/projects/jakarta-rest-todo/pom.xml b/cmd/jws/projects/jakarta-rest-todo/pom.xml
index e69de29..e0eee0f 100644
--- a/cmd/jws/projects/jakarta-rest-todo/pom.xml
+++ b/cmd/jws/projects/jakarta-rest-todo/pom.xml
@@ -0,0 +1,112 @@
+
+
+ 4.0.0
+
+ com.todoapp
+ todo-app
+ 1.0-SNAPSHOT
+ war
+
+
+ UTF-8
+ 17
+ 17
+ 10.0.0
+ 6.2.5.Final
+ 42.6.0
+
+
+
+
+
+ jakarta.platform
+ jakarta.jakartaee-api
+ ${jakartaee.version}
+ provided
+
+
+
+
+ org.hibernate.orm
+ hibernate-core
+ ${hibernate.version}
+
+
+
+
+ org.postgresql
+ postgresql
+ ${postgresql.version}
+
+
+
+
+ jakarta.servlet.jsp.jstl
+ jakarta.servlet.jsp.jstl-api
+ 3.0.0
+
+
+ org.glassfish.web
+ jakarta.servlet.jsp.jstl
+ 3.0.1
+
+
+
+
+ jakarta.ws.rs
+ jakarta.ws.rs-api
+ 3.1.0
+ provided
+
+
+
+
+ jakarta.json.bind
+ jakarta.json.bind-api
+ 3.0.0
+ provided
+
+
+
+
+ jakarta.json
+ jakarta.json-api
+ 2.1.0
+ provided
+
+
+ org.hibernate
+ hibernate-core
+ 6.3.1.Final
+
+
+
+ at.favre.lib
+ bcrypt
+ 0.10.2
+
+
+
+
+ todo-app
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.10.1
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ 3.3.2
+
+
+ io.openliberty.tools
+ liberty-maven-plugin
+ 3.7.1
+
+
+
+
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/dao/TodoDAO.java b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/dao/TodoDAO.java
new file mode 100644
index 0000000..45d5557
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/dao/TodoDAO.java
@@ -0,0 +1,135 @@
+package com.todoapp.dao;
+
+import com.todoapp.model.Todo;
+import com.todoapp.model.User;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.query.Query;
+import java.util.List;
+
+@ApplicationScoped
+public class TodoDAO {
+
+ @Inject
+ private SessionFactory sessionFactory;
+
+ @Inject
+ private UserDAO userDAO;
+
+ public Todo saveTodo(Todo todo, String username) {
+ Transaction transaction = null;
+ try (Session session = sessionFactory.openSession()) {
+ transaction = session.beginTransaction();
+
+ // Benutzer aus der Datenbank holen
+ User user = userDAO.getUserByUsername(username);
+ if (user == null) {
+ throw new IllegalArgumentException("User not found");
+ }
+
+ todo.setUser(user);
+ session.persist(todo);
+ transaction.commit();
+ return todo;
+ } catch (Exception e) {
+ if (transaction != null) {
+ transaction.rollback();
+ }
+ e.printStackTrace();
+ throw new RuntimeException("Error saving todo", e);
+ }
+ }
+
+ public Todo updateTodo(Todo todo) {
+ Transaction transaction = null;
+ try (Session session = sessionFactory.openSession()) {
+ transaction = session.beginTransaction();
+ Todo updatedTodo = session.merge(todo);
+ transaction.commit();
+ return updatedTodo;
+ } catch (Exception e) {
+ if (transaction != null) {
+ transaction.rollback();
+ }
+ e.printStackTrace();
+ throw new RuntimeException("Error updating todo", e);
+ }
+ }
+
+ public boolean deleteTodo(Long id) {
+ Transaction transaction = null;
+ try (Session session = sessionFactory.openSession()) {
+ transaction = session.beginTransaction();
+ Todo todo = session.get(Todo.class, id);
+ if (todo != null) {
+ session.remove(todo);
+ transaction.commit();
+ return true;
+ }
+ return false;
+ } catch (Exception e) {
+ if (transaction != null) {
+ transaction.rollback();
+ }
+ e.printStackTrace();
+ throw new RuntimeException("Error deleting todo", e);
+ }
+ }
+
+ public Todo getTodoById(Long id) {
+ try (Session session = sessionFactory.openSession()) {
+ return session.get(Todo.class, id);
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Error getting todo by ID", e);
+ }
+ }
+
+ public List getAllTodos() {
+ try (Session session = sessionFactory.openSession()) {
+ return session.createQuery("FROM Todo", Todo.class).list();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Error getting all todos", e);
+ }
+ }
+
+ public List getTodosByUsername(String username) {
+ try (Session session = sessionFactory.openSession()) {
+ Query query = session.createQuery(
+ "FROM Todo t WHERE t.user.username = :username ORDER BY t.completed, t.targetDate", Todo.class);
+ query.setParameter("username", username);
+ return query.list();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Error getting todos by username", e);
+ }
+ }
+
+ public List getCompletedTodosByUsername(String username) {
+ try (Session session = sessionFactory.openSession()) {
+ Query query = session.createQuery(
+ "FROM Todo t WHERE t.user.username = :username AND t.completed = true", Todo.class);
+ query.setParameter("username", username);
+ return query.list();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Error getting completed todos", e);
+ }
+ }
+
+ public List getIncompleteTodosByUsername(String username) {
+ try (Session session = sessionFactory.openSession()) {
+ Query query = session.createQuery(
+ "FROM Todo t WHERE t.user.username = :username AND t.completed = false ORDER BY t.targetDate", Todo.class);
+ query.setParameter("username", username);
+ return query.list();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Error getting incomplete todos", e);
+ }
+ }
+}
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/dao/UserDAO.java b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/dao/UserDAO.java
new file mode 100644
index 0000000..3893bb7
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/dao/UserDAO.java
@@ -0,0 +1,124 @@
+package com.todoapp.dao;
+
+import com.todoapp.model.User;
+import com.todoapp.util.HibernateUtil;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.query.Query;
+import java.util.List;
+import java.util.Optional;
+
+@ApplicationScoped
+public class UserDAO {
+
+ @Inject
+ private SessionFactory sessionFactory;
+
+ public User saveUser(User user) {
+ Transaction transaction = null;
+ try (Session session = sessionFactory.openSession()) {
+ transaction = session.beginTransaction();
+ session.persist(user);
+ transaction.commit();
+ return user;
+ } catch (Exception e) {
+ if (transaction != null) {
+ transaction.rollback();
+ }
+ e.printStackTrace();
+ throw new RuntimeException("Error saving user", e);
+ }
+ }
+
+ public User updateUser(User user) {
+ Transaction transaction = null;
+ try (Session session = sessionFactory.openSession()) {
+ transaction = session.beginTransaction();
+ User updatedUser = session.merge(user);
+ transaction.commit();
+ return updatedUser;
+ } catch (Exception e) {
+ if (transaction != null) {
+ transaction.rollback();
+ }
+ e.printStackTrace();
+ throw new RuntimeException("Error updating user", e);
+ }
+ }
+
+ public boolean deleteUser(Long id) {
+ Transaction transaction = null;
+ try (Session session = sessionFactory.openSession()) {
+ transaction = session.beginTransaction();
+ User user = session.get(User.class, id);
+ if (user != null) {
+ session.remove(user);
+ transaction.commit();
+ return true;
+ }
+ return false;
+ } catch (Exception e) {
+ if (transaction != null) {
+ transaction.rollback();
+ }
+ e.printStackTrace();
+ throw new RuntimeException("Error deleting user", e);
+ }
+ }
+
+ public User getUserById(Long id) {
+ try (Session session = sessionFactory.openSession()) {
+ return session.get(User.class, id);
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Error getting user by ID", e);
+ }
+ }
+
+ public User getUserByUsername(String username) {
+ try (Session session = sessionFactory.openSession()) {
+ Query query = session.createQuery("FROM User WHERE username = :username", User.class);
+ query.setParameter("username", username);
+ return query.uniqueResult();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Error getting user by username", e);
+ }
+ }
+
+ public List getAllUsers() {
+ try (Session session = sessionFactory.openSession()) {
+ return session.createQuery("FROM User", User.class).list();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Error getting all users", e);
+ }
+ }
+
+ public boolean usernameExists(String username) {
+ try (Session session = sessionFactory.openSession()) {
+ Query query = session.createQuery(
+ "SELECT COUNT(u) FROM User u WHERE u.username = :username", Long.class);
+ query.setParameter("username", username);
+ return query.uniqueResult() > 0;
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Error checking if username exists", e);
+ }
+ }
+
+ public boolean emailExists(String email) {
+ try (Session session = sessionFactory.openSession()) {
+ Query query = session.createQuery(
+ "SELECT COUNT(u) FROM User u WHERE u.email = :email", Long.class);
+ query.setParameter("email", email);
+ return query.uniqueResult() > 0;
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Error checking if email exists", e);
+ }
+ }
+}
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/dto/LoginRequest.java b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/dto/LoginRequest.java
new file mode 100644
index 0000000..5608a6e
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/dto/LoginRequest.java
@@ -0,0 +1,23 @@
+package com.todoapp.dto;
+
+public class LoginRequest {
+ private String username;
+ private String password;
+
+ // Getter und Setter
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/dto/RegistrationRequest.java b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/dto/RegistrationRequest.java
new file mode 100644
index 0000000..04561ec
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/dto/RegistrationRequest.java
@@ -0,0 +1,32 @@
+package com.todoapp.dto;
+
+public class RegistrationRequest {
+ private String username;
+ private String password;
+ private String email;
+
+ // Getter und Setter
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+}
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/model/Todo.java b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/model/Todo.java
new file mode 100644
index 0000000..18560e4
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/model/Todo.java
@@ -0,0 +1,109 @@
+package com.todoapp.model;
+
+import jakarta.persistence.*;
+import jakarta.json.bind.annotation.JsonbTransient;
+import java.io.Serializable;
+import java.util.Date;
+
+@Entity
+@Table(name = "todos")
+public class Todo implements Serializable {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(nullable = false)
+ private String title;
+
+ @Column(length = 1000)
+ private String description;
+
+ @Column(name = "target_date")
+ @Temporal(TemporalType.DATE)
+ private Date targetDate;
+
+ @Column
+ private boolean completed;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id", nullable = false)
+ @JsonbTransient // Verhindert zirkuläre Referenzen
+ private User user;
+
+ // Konstruktoren
+ public Todo() {
+ }
+
+ public Todo(String title, String description, Date targetDate, boolean completed) {
+ this.title = title;
+ this.description = description;
+ this.targetDate = targetDate;
+ this.completed = completed;
+ }
+
+ // Getter und Setter
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Date getTargetDate() {
+ return targetDate;
+ }
+
+ public void setTargetDate(Date targetDate) {
+ this.targetDate = targetDate;
+ }
+
+ public boolean isCompleted() {
+ return completed;
+ }
+
+ public void setCompleted(boolean completed) {
+ this.completed = completed;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ public void setUser(User user) {
+ this.user = user;
+ }
+
+ // Zusätzliche Methode für REST-Antworten
+ public Long getUserId() {
+ return user != null ? user.getId() : null;
+ }
+
+ @Override
+ public String toString() {
+ return "Todo{" +
+ "id=" + id +
+ ", title='" + title + '\'' +
+ ", description='" + description + '\'' +
+ ", targetDate=" + targetDate +
+ ", completed=" + completed +
+ '}';
+ }
+}
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/model/User.java b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/model/User.java
new file mode 100644
index 0000000..e530617
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/model/User.java
@@ -0,0 +1,101 @@
+package com.todoapp.model;
+
+import jakarta.persistence.*;
+import jakarta.json.bind.annotation.JsonbTransient;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Table(name = "users")
+public class User implements Serializable {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(nullable = false, unique = true)
+ private String username;
+
+ @Column(nullable = false)
+ @JsonbTransient // Verhindert, dass das Passwort in JSON serialisiert wird
+ private String password;
+
+ @Column(nullable = false)
+ private String email;
+
+ @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
+ @JsonbTransient // Verhindert zirkuläre Referenzen bei der JSON-Serialisierung
+ private List todos = new ArrayList<>();
+
+ // Konstruktoren
+ public User() {
+ }
+
+ public User(String username, String password, String email) {
+ this.username = username;
+ this.password = password;
+ this.email = email;
+ }
+
+ // Getter und Setter
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public List getTodos() {
+ return todos;
+ }
+
+ public void setTodos(List todos) {
+ this.todos = todos;
+ }
+
+ // Hilfsmethoden
+ public void addTodo(Todo todo) {
+ todos.add(todo);
+ todo.setUser(this);
+ }
+
+ public void removeTodo(Todo todo) {
+ todos.remove(todo);
+ todo.setUser(null);
+ }
+
+ @Override
+ public String toString() {
+ return "User{" +
+ "id=" + id +
+ ", username='" + username + '\'' +
+ ", email='" + email + '\'' +
+ '}';
+ }
+}
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/rest/AuthResource.java b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/rest/AuthResource.java
new file mode 100644
index 0000000..1e8a901
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/rest/AuthResource.java
@@ -0,0 +1,95 @@
+package com.todoapp.rest;
+
+import com.todoapp.dao.UserDAO;
+import com.todoapp.model.User;
+import com.todoapp.dto.LoginRequest;
+import com.todoapp.dto.RegistrationRequest;
+
+import jakarta.annotation.security.RolesAllowed;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import at.favre.lib.crypto.bcrypt.BCrypt;
+import java.util.Map;
+
+@Path("/auth")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class AuthResource {
+
+ @Inject
+ private UserDAO userDAO;
+
+ @POST
+ @Path("/register")
+ public Response register(RegistrationRequest request) {
+ if (request.getUsername() == null || request.getPassword() == null || request.getEmail() == null) {
+ return Response.status(Response.Status.BAD_REQUEST)
+ .entity(Map.of("error", "Username, password, and email are required"))
+ .build();
+ }
+
+ if (userDAO.usernameExists(request.getUsername())) {
+ System.out.println(userDAO.usernameExists(request.getUsername()));
+ return Response.status(Response.Status.CONFLICT)
+ .entity(Map.of("error", "Username already exists"))
+ .build();
+ }
+
+ if (userDAO.emailExists(request.getEmail())) {
+ return Response.status(Response.Status.CONFLICT)
+ .entity(Map.of("error", "Email already exists"))
+ .build();
+ }
+
+ User user = new User();
+ user.setUsername(request.getUsername());
+ user.setEmail(request.getEmail());
+
+ String hashedPassword = BCrypt.withDefaults()
+ .hashToString(12, request.getPassword().toCharArray());
+ user.setPassword(hashedPassword);
+
+ userDAO.saveUser(user);
+
+ return Response.status(Response.Status.CREATED)
+ .entity(Map.of("message", "User registered successfully"))
+ .build();
+ }
+
+ @POST
+ @RolesAllowed("user")
+ @Path("/login")
+ public Response login(LoginRequest request) {
+ // Validierung
+ if (request.getUsername() == null || request.getPassword() == null) {
+ return Response.status(Response.Status.BAD_REQUEST)
+ .entity(Map.of("error", "Username and password are required"))
+ .build();
+ }
+
+ // Benutzer suchen
+ User user = userDAO.getUserByUsername(request.getUsername());
+ if (user == null) {
+ return Response.status(Response.Status.UNAUTHORIZED)
+ .entity(Map.of("error", "Invalid credentials"))
+ .build();
+ }
+
+ // Passwort überprüfen
+ BCrypt.Result result = BCrypt.verifyer()
+ .verify(request.getPassword().toCharArray(), user.getPassword());
+ if (!result.verified) {
+ return Response.status(Response.Status.UNAUTHORIZED)
+ .entity(Map.of("error", "Invalid credentials"))
+ .build();
+ }
+
+ // Erfolgreiche Anmeldung
+ return Response.ok(Map.of(
+ "message", "Login successful",
+ "username", user.getUsername(),
+ "email", user.getEmail())).build();
+ }
+}
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/rest/TodoApplication.java b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/rest/TodoApplication.java
new file mode 100644
index 0000000..6ab8ce0
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/rest/TodoApplication.java
@@ -0,0 +1,9 @@
+package com.todoapp.rest;
+
+import jakarta.ws.rs.ApplicationPath;
+import jakarta.ws.rs.core.Application;
+
+@ApplicationPath("/api")
+public class TodoApplication extends Application {
+ // Die leere Klasse ist ausreichend, um den REST-Endpunkt zu definieren
+}
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/rest/TodoResource.java b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/rest/TodoResource.java
new file mode 100644
index 0000000..9e0575f
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/rest/TodoResource.java
@@ -0,0 +1,130 @@
+package com.todoapp.rest;
+
+import com.todoapp.dao.TodoDAO;
+import com.todoapp.model.Todo;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.SecurityContext;
+import java.util.List;
+
+@Path("/todos")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class TodoResource {
+
+ @Inject
+ private TodoDAO todoDAO;
+
+ @GET
+ public Response getAllTodos(@Context SecurityContext securityContext) {
+ String username = securityContext.getUserPrincipal().getName();
+ List todos = todoDAO.getTodosByUsername(username);
+ return Response.ok(todos).build();
+ }
+
+ @GET
+ @Path("/{id}")
+ public Response getTodoById(@PathParam("id") Long id, @Context SecurityContext securityContext) {
+ String username = securityContext.getUserPrincipal().getName();
+ Todo todo = todoDAO.getTodoById(id);
+
+ if (todo == null) {
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+
+ // Sicherheitscheck: Nutzer darf nur eigene Todos sehen
+ if (!todo.getUser().getUsername().equals(username)) {
+ return Response.status(Response.Status.FORBIDDEN).build();
+ }
+
+ return Response.ok(todo).build();
+ }
+
+ @GET
+ @Path("/completed")
+ public Response getCompletedTodos(@Context SecurityContext securityContext) {
+ String username = securityContext.getUserPrincipal().getName();
+ List todos = todoDAO.getCompletedTodosByUsername(username);
+ return Response.ok(todos).build();
+ }
+
+ @GET
+ @Path("/incomplete")
+ public Response getIncompleteTodos(@Context SecurityContext securityContext) {
+ String username = securityContext.getUserPrincipal().getName();
+ List todos = todoDAO.getIncompleteTodosByUsername(username);
+ return Response.ok(todos).build();
+ }
+
+ @POST
+ public Response createTodo(Todo todo, @Context SecurityContext securityContext) {
+ String username = securityContext.getUserPrincipal().getName();
+ todo = todoDAO.saveTodo(todo, username);
+ return Response.status(Response.Status.CREATED).entity(todo).build();
+ }
+
+ @PUT
+ @Path("/{id}")
+ public Response updateTodo(@PathParam("id") Long id, Todo updatedTodo, @Context SecurityContext securityContext) {
+ String username = securityContext.getUserPrincipal().getName();
+ Todo existingTodo = todoDAO.getTodoById(id);
+
+ if (existingTodo == null) {
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+
+ // Sicherheitscheck: Nutzer darf nur eigene Todos aktualisieren
+ if (!existingTodo.getUser().getUsername().equals(username)) {
+ return Response.status(Response.Status.FORBIDDEN).build();
+ }
+
+ // ID und Benutzer beibehalten
+ updatedTodo.setId(id);
+ updatedTodo.setUser(existingTodo.getUser());
+
+ todoDAO.updateTodo(updatedTodo);
+ return Response.ok(updatedTodo).build();
+ }
+
+ @DELETE
+ @Path("/{id}")
+ public Response deleteTodo(@PathParam("id") Long id, @Context SecurityContext securityContext) {
+ String username = securityContext.getUserPrincipal().getName();
+ Todo todo = todoDAO.getTodoById(id);
+
+ if (todo == null) {
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+
+ // Sicherheitscheck: Nutzer darf nur eigene Todos löschen
+ if (!todo.getUser().getUsername().equals(username)) {
+ return Response.status(Response.Status.FORBIDDEN).build();
+ }
+
+ boolean deleted = todoDAO.deleteTodo(id);
+ return deleted ? Response.noContent().build() : Response.status(Response.Status.NOT_FOUND).build();
+ }
+
+ @PUT
+ @Path("/{id}/complete")
+ public Response markTodoComplete(@PathParam("id") Long id, @Context SecurityContext securityContext) {
+ String username = securityContext.getUserPrincipal().getName();
+ Todo todo = todoDAO.getTodoById(id);
+
+ if (todo == null) {
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+
+ // Sicherheitscheck: Nutzer darf nur eigene Todos aktualisieren
+ if (!todo.getUser().getUsername().equals(username)) {
+ return Response.status(Response.Status.FORBIDDEN).build();
+ }
+
+ todo.setCompleted(true);
+ todoDAO.updateTodo(todo);
+ return Response.ok(todo).build();
+ }
+}
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/security/BasicAuthenticationFilter.java b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/security/BasicAuthenticationFilter.java
new file mode 100644
index 0000000..6d4f104
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/security/BasicAuthenticationFilter.java
@@ -0,0 +1,104 @@
+package com.todoapp.security;
+
+import com.todoapp.dao.UserDAO;
+import com.todoapp.model.User;
+import jakarta.annotation.Priority;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.Priorities;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerRequestFilter;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.SecurityContext;
+import jakarta.ws.rs.ext.Provider;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Base64;
+import at.favre.lib.crypto.bcrypt.BCrypt;
+
+@Provider
+@Priority(Priorities.AUTHENTICATION)
+@ApplicationScoped
+public class BasicAuthenticationFilter implements ContainerRequestFilter {
+
+ @Inject
+ private UserDAO userDAO;
+
+ @Override
+ public void filter(ContainerRequestContext requestContext) throws IOException {
+ System.out.println("BasicAuthenticationFilter triggered for path: " + requestContext.getUriInfo().getPath());
+ String path = requestContext.getUriInfo().getPath();
+ if (path.endsWith("auth/register") || path.endsWith("auth/login")) {
+ return;
+ }
+
+ String authHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
+ if (authHeader == null || !authHeader.startsWith("Basic ")) {
+ abortWithUnauthorized(requestContext);
+ return;
+ }
+
+ String[] credentials = extractCredentials(authHeader);
+ if (credentials.length != 2) {
+ abortWithUnauthorized(requestContext);
+ return;
+ }
+
+ String username = credentials[0];
+ String password = credentials[1];
+
+ User user = userDAO.getUserByUsername(username);
+ if (user == null) {
+ abortWithUnauthorized(requestContext);
+ return;
+ }
+
+ BCrypt.Result result = BCrypt.verifyer()
+ .verify(password.toCharArray(), user.getPassword());
+ if (!result.verified) {
+ abortWithUnauthorized(requestContext);
+ return;
+ }
+
+ final SecurityContext currentSecurityContext = requestContext.getSecurityContext();
+ requestContext.setSecurityContext(new SecurityContext() {
+ @Override
+ public Principal getUserPrincipal() {
+ return () -> username;
+ }
+
+ @Override
+ public boolean isUserInRole(String role) {
+ return true;
+ }
+
+ @Override
+ public boolean isSecure() {
+ return currentSecurityContext.isSecure();
+ }
+
+ @Override
+ public String getAuthenticationScheme() {
+ return "BASIC";
+ }
+ });
+ System.out.println("SecurityContext gesetzt für User: " + username);
+ System.out.println("User in Rolle user: " + requestContext.getSecurityContext().isUserInRole("user"));
+
+ }
+
+ private String[] extractCredentials(String authHeader) {
+ String base64Credentials = authHeader.substring("Basic ".length()).trim();
+ byte[] decoded = Base64.getDecoder().decode(base64Credentials);
+ String credentials = new String(decoded);
+ return credentials.split(":", 2);
+ }
+
+ private void abortWithUnauthorized(ContainerRequestContext requestContext) {
+ requestContext.abortWith(Response
+ .status(Response.Status.UNAUTHORIZED)
+ .header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"TodoApp\"")
+ .build());
+ }
+}
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/security/CORSFilter.java b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/security/CORSFilter.java
new file mode 100644
index 0000000..b08586a
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/security/CORSFilter.java
@@ -0,0 +1,20 @@
+package com.todoapp.security;
+
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerResponseContext;
+import jakarta.ws.rs.container.ContainerResponseFilter;
+import jakarta.ws.rs.ext.Provider;
+import java.io.IOException;
+
+@Provider
+public class CORSFilter implements ContainerResponseFilter {
+
+ @Override
+ public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
+ throws IOException {
+ responseContext.getHeaders().add("Access-Control-Allow-Origin", "*");
+ responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
+ responseContext.getHeaders().add("Access-Control-Allow-Headers", "Content-Type, Authorization");
+ responseContext.getHeaders().add("Access-Control-Max-Age", "86400");
+ }
+}
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/util/HibernateUtil.java b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/util/HibernateUtil.java
new file mode 100644
index 0000000..2ddaaf2
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/java/com/todoapp/util/HibernateUtil.java
@@ -0,0 +1,41 @@
+package com.todoapp.util;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.inject.Produces;
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.Configuration;
+
+@ApplicationScoped
+public class HibernateUtil {
+
+ private static SessionFactory sessionFactory;
+
+ @Produces
+ @ApplicationScoped
+ public SessionFactory getSessionFactory() {
+ if (sessionFactory == null) {
+ try {
+ Configuration configuration = new Configuration();
+ configuration.setProperty("hibernate.connection.driver_class", "org.postgresql.Driver");
+ configuration.setProperty("hibernate.connection.url", "jdbc:postgresql://postgresdb:5432/todo_db");
+ configuration.setProperty("hibernate.connection.username", "postgres");
+ configuration.setProperty("hibernate.connection.password", "postgres");
+
+ // Entity-Klassen registrieren
+ configuration.addAnnotatedClass(com.todoapp.model.User.class);
+ configuration.addAnnotatedClass(com.todoapp.model.Todo.class);
+
+ sessionFactory = configuration.buildSessionFactory();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return sessionFactory;
+ }
+
+ public static void shutdown() {
+ if (sessionFactory != null) {
+ sessionFactory.close();
+ }
+ }
+}
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/liberty/config/server.xml b/cmd/jws/projects/jakarta-rest-todo/src/main/liberty/config/server.xml
new file mode 100644
index 0000000..f281c16
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/liberty/config/server.xml
@@ -0,0 +1,39 @@
+
+
+
+
+ servlet-6.0
+ pages-3.1
+ jdbc-4.2
+ jndi-1.0
+ monitor-1.0
+ restfulWS-3.1
+ jsonb-3.0
+ jsonp-2.1
+
+ transportSecurity-1.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/resources/META-INF/persistence.xml b/cmd/jws/projects/jakarta-rest-todo/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 0000000..1e6b316
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,20 @@
+
+
+
+ org.hibernate.jpa.HibernatePersistenceProvider
+ com.todoapp.model.User
+ com.todoapp.model.Todo
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/resources/hibernate.cfg.xml b/cmd/jws/projects/jakarta-rest-todo/src/main/resources/hibernate.cfg.xml
new file mode 100644
index 0000000..33bdd20
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/resources/hibernate.cfg.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+ jdbc/PostgresDataSource
+
+
+ org.hibernate.dialect.PostgreSQLDialect
+
+
+ true
+
+
+ thread
+
+
+ update
+
+
+
+
+
+
diff --git a/cmd/jws/projects/jakarta-rest-todo/src/main/webapp/WEB-INF/web.xml b/cmd/jws/projects/jakarta-rest-todo/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..80ab99b
--- /dev/null
+++ b/cmd/jws/projects/jakarta-rest-todo/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,12 @@
+
+
+
+ Todo Application
+
+
+ 30
+
+