feat: Add complete example for Jakarta Faces and a devcontainer setup

This commit is contained in:
Patryk Hegenberg 2025-03-21 20:43:41 +01:00
parent d851c1b628
commit a03f180e43
45 changed files with 1791 additions and 294 deletions

View file

@ -100,9 +100,9 @@ func createMainWindow(a fyne.App, config *config.Config) {
standartProjects := []project.Project{
{
Name: "JakartaEE Todo-App mit Servlet",
Description: "Eine Todo-Anwendung mit Jakarta Servlet und\nPostgreSQL-Datenbank.",
FolderName: "jakarta-servlet-todo",
Name: "JakartaEE Todo-App mit Server Faces",
Description: "Eine Todo-Anwendung mit Jakarta Faces und\nPostgreSQL-Datenbank.",
FolderName: "jakarta-faces-todo",
},
{
Name: "JakartaEE Todo-App mit JSP",

View file

@ -0,0 +1,8 @@
.git
.github
.settings
target
*.iml
.idea
.vscode
node_modules

View file

@ -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/

View file

@ -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

View file

@ -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

View file

@ -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"}
}
}

View file

@ -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

View file

@ -0,0 +1,17 @@
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)
);

View file

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.todoapp</groupId>
<artifactId>todo-app</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<jakartaee.version>10.0.0</jakartaee.version>
<hibernate.version>6.2.5.Final</hibernate.version>
<postgresql.version>42.6.0</postgresql.version>
</properties>
<dependencies>
<!-- Jakarta EE 10 API -->
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>${jakartaee.version}</version>
<scope>provided</scope>
</dependency>
<!-- Hibernate Core -->
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- PostgreSQL JDBC Driver -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
<dependency>
<groupId>jakarta.websocket</groupId>
<artifactId>jakarta.websocket-api</artifactId>
<version>2.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.3.1.Final</version> <!-- Stelle sicher, dass du eine aktuelle Version benutzt -->
</dependency>
<!-- Mindestens zum Passwort-Hashing -->
<dependency>
<groupId>at.favre.lib</groupId>
<artifactId>bcrypt</artifactId>
<version>0.10.2</version>
</dependency>
</dependencies>
<build>
<finalName>todo-app</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
<plugin>
<groupId>io.openliberty.tools</groupId>
<artifactId>liberty-maven-plugin</artifactId>
<version>3.7.1</version>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<server description="OpenLiberty Todo App Server">
<featureManager>
<feature>servlet-6.0</feature>
<feature>pages-3.1</feature>
<feature>jdbc-4.2</feature>
<feature>jndi-1.0</feature>
<feature>monitor-1.0</feature>
<feature>faces-4.0</feature>
</featureManager>
<httpEndpoint id="defaultHttpEndpoint"
httpPort="9080"
httpsPort="9443" />
<applicationManager autoExpand="true" />
<dataSource id="PostgresDataSource">
<jdbcDriver libraryRef="PostgresLib" />
<properties.postgresql serverName="postgres"
portNumber="5432"
databaseName="todo_db"
user="postgres"
password="postgres" />
</dataSource>
<library id="PostgresLib">
<fileset dir="${server.config.dir}/lib" includes="postgresql-*.jar" />
</library>
<webApplication location="todo-app.war" contextRoot="/todo-app" />
</server>

View file

@ -0,0 +1,133 @@
package com.todoapp.bean;
import at.favre.lib.crypto.bcrypt.BCrypt;
import com.todoapp.dao.UserDAO;
import com.todoapp.model.User;
import jakarta.enterprise.context.SessionScoped;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.io.Serializable;
@Named
@SessionScoped
public class AuthBean implements Serializable {
@Inject
private UserDAO userDAO;
private User user = new User();
private String confirmPassword;
public String login() {
User foundUser = userDAO
.getUserByUsername(user.getUsername())
.orElse(null);
if (
foundUser != null &&
BCrypt.verifyer()
.verify(
user.getPassword().toCharArray(),
foundUser.getPassword()
)
.verified
) {
FacesContext.getCurrentInstance()
.getExternalContext()
.getSessionMap()
.put("user", foundUser);
return "todo-list?faces-redirect=true";
} else {
FacesContext.getCurrentInstance()
.addMessage(
null,
new FacesMessage(
FacesMessage.SEVERITY_ERROR,
"Invalid username or password",
null
)
);
return null;
}
}
public String register() {
if (!user.getPassword().equals(confirmPassword)) {
FacesContext.getCurrentInstance()
.addMessage(
null,
new FacesMessage(
FacesMessage.SEVERITY_ERROR,
"Passwords do not match",
null
)
);
return null;
}
if (userDAO.usernameExists(user.getUsername())) {
FacesContext.getCurrentInstance()
.addMessage(
null,
new FacesMessage(
FacesMessage.SEVERITY_ERROR,
"Username already exists",
null
)
);
return null;
}
if (userDAO.emailExists(user.getEmail())) {
FacesContext.getCurrentInstance()
.addMessage(
null,
new FacesMessage(
FacesMessage.SEVERITY_ERROR,
"Email already registered",
null
)
);
return null;
}
String hashedPassword = BCrypt.withDefaults()
.hashToString(12, user.getPassword().toCharArray());
user.setPassword(hashedPassword);
userDAO.saveUser(user);
FacesContext.getCurrentInstance()
.addMessage(
null,
new FacesMessage(
FacesMessage.SEVERITY_INFO,
"Registration successful. Please log in.",
null
)
);
return "login?faces-redirect=true";
}
public String logout() {
FacesContext.getCurrentInstance()
.getExternalContext()
.invalidateSession();
return "login?faces-redirect=true";
}
// Getter und Setter
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
}

View file

@ -0,0 +1,112 @@
package com.todoapp.bean;
import com.todoapp.dao.TodoDAO;
import com.todoapp.model.Todo;
import com.todoapp.model.User;
import jakarta.enterprise.context.SessionScoped;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.io.Serializable;
import java.util.List;
@Named
@SessionScoped
public class TodoBean implements Serializable {
@Inject
private TodoDAO todoDAO;
private Todo todo = new Todo();
private List<Todo> todos;
public String saveOrUpdateTodo() {
if (todo.getId() == null) {
return saveTodo();
} else {
return updateTodo();
}
}
public List<Todo> getTodosByUser(Long userId) {
if (todos == null) {
todos = todoDAO.getTodosByUser(userId);
}
return todos;
}
public List<Todo> getTodosByUser() {
User user = (User) FacesContext.getCurrentInstance()
.getExternalContext()
.getSessionMap()
.get("user");
System.out.println("Fetching todos for user ID: " + user.getId());
List<Todo> todos = todoDAO.getTodosByUser(user.getId());
System.out.println("Fetched todos: " + todos);
return todos;
}
public void loadTodo() {
String idParam = FacesContext.getCurrentInstance()
.getExternalContext()
.getRequestParameterMap()
.get("id");
if (idParam != null && !idParam.isEmpty()) {
Long id = Long.parseLong(idParam);
todo = todoDAO.getTodoById(id);
} else {
todo = new Todo();
}
}
public String saveTodo(User user) {
todo.setUser(user);
todoDAO.saveTodo(todo);
todo = new Todo();
todos = null;
return "todo-list?faces-redirect=true";
}
public String saveTodo() {
User user = (User) FacesContext.getCurrentInstance()
.getExternalContext()
.getSessionMap()
.get("user");
System.out.println(user);
todo.setUser(user);
todoDAO.saveTodo(todo);
todo = new Todo();
todos = null;
return "todo-list?faces-redirect=true";
}
public String updateTodo() {
todoDAO.updateTodo(todo);
todo = new Todo();
todos = null;
return "todo-list?faces-redirect=true";
}
public String deleteTodo(Long id) {
todoDAO.deleteTodo(id);
todos = null;
return "todo-list?faces-redirect=true";
}
public String completeTodo(Long id) {
Todo todoToComplete = todoDAO.getTodoById(id);
todoToComplete.setCompleted(!todoToComplete.isCompleted());
todoDAO.updateTodo(todoToComplete);
todos = null;
return "todo-list?faces-redirect=true";
}
// Getter und Setter für 'todo'
public Todo getTodo() {
return todo;
}
public void setTodo(Todo todo) {
this.todo = todo;
}
}

View file

@ -0,0 +1,43 @@
package com.todoapp.bean;
import com.todoapp.dao.UserDAO;
import com.todoapp.model.User;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.io.Serializable;
import java.util.List;
@Named
@SessionScoped
public class UserBean implements Serializable {
@Inject
private UserDAO userDAO;
private User user = new User();
private List<User> users;
public List<User> getAllUsers() {
if (users == null) {
users = userDAO.getAllUsers();
}
return users;
}
public String saveUser() {
userDAO.saveUser(user);
user = new User();
users = null; // Reset the list to force a refresh
return "user-list?faces-redirect=true";
}
// Getter und Setter für 'user'
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}

View file

@ -0,0 +1,134 @@
package com.todoapp.dao;
import com.todoapp.model.Todo;
import com.todoapp.util.HibernateUtil;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
@ApplicationScoped
public class TodoDAO {
public void saveTodo(Todo todo) {
Transaction transaction = null;
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
transaction = session.beginTransaction();
session.persist(todo);
transaction.commit();
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
}
}
public void updateTodo(Todo todo) {
Transaction transaction = null;
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
transaction = session.beginTransaction();
session.merge(todo);
transaction.commit();
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
}
}
public void deleteTodo(Long id) {
Transaction transaction = null;
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
transaction = session.beginTransaction();
Todo todo = session.get(Todo.class, id);
if (todo != null) {
session.remove(todo);
}
transaction.commit();
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
}
}
public Todo getTodoById(Long id) {
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
return session.get(Todo.class, id);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public List<Todo> getAllTodos() {
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
return session.createQuery("FROM Todo", Todo.class).list();
} catch (Exception e) {
e.printStackTrace();
return List.of();
}
}
public List<Todo> getTodosByUser(Long userId) {
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
Query<Todo> query = session.createQuery(
"FROM Todo t WHERE t.user.id = :userId ORDER BY t.completed, t.targetDate",
Todo.class
);
query.setParameter("userId", userId);
return query.list();
} catch (Exception e) {
e.printStackTrace();
return List.of();
}
}
public List<Todo> getCompletedTodosByUser(Long userId) {
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
Query<Todo> query = session.createQuery(
"FROM Todo t WHERE t.user.id = :userId AND t.completed = true",
Todo.class
);
query.setParameter("userId", userId);
return query.list();
} catch (Exception e) {
e.printStackTrace();
return List.of();
}
}
public List<Todo> getIncompleteTodosByUser(Long userId) {
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
Query<Todo> query = session.createQuery(
"FROM Todo t WHERE t.user.id = :userId AND t.completed = false ORDER BY t.targetDate",
Todo.class
);
query.setParameter("userId", userId);
return query.list();
} catch (Exception e) {
e.printStackTrace();
return List.of();
}
}
}

View file

@ -0,0 +1,136 @@
package com.todoapp.dao;
import com.todoapp.model.User;
import com.todoapp.util.HibernateUtil;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.List;
import java.util.Optional;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
@ApplicationScoped
public class UserDAO {
public void saveUser(User user) {
Transaction transaction = null;
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
transaction = session.beginTransaction();
session.persist(user);
transaction.commit();
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
}
}
public void updateUser(User user) {
Transaction transaction = null;
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
transaction = session.beginTransaction();
session.merge(user);
transaction.commit();
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
}
}
public void deleteUser(Long id) {
Transaction transaction = null;
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
transaction = session.beginTransaction();
User user = session.get(User.class, id);
if (user != null) {
session.remove(user);
}
transaction.commit();
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
}
}
public User getUserById(Long id) {
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
return session.get(User.class, id);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public Optional<User> getUserByUsername(String username) {
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
Query<User> query = session.createQuery(
"FROM User WHERE username = :username",
User.class
);
query.setParameter("username", username);
User user = query.uniqueResult();
return Optional.ofNullable(user);
} catch (Exception e) {
e.printStackTrace();
return Optional.empty();
}
}
public List<User> getAllUsers() {
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
return session.createQuery("FROM User", User.class).list();
} catch (Exception e) {
e.printStackTrace();
return List.of();
}
}
public boolean usernameExists(String username) {
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
Query<Long> 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();
return false;
}
}
public boolean emailExists(String email) {
try (
Session session = HibernateUtil.getSessionFactory().openSession()
) {
Query<Long> 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();
return false;
}
}
}

View file

@ -0,0 +1,110 @@
package com.todoapp.model;
import jakarta.persistence.*;
import java.io.Serializable;
import java.time.LocalDate;
@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")
private LocalDate targetDate;
private boolean completed;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
// Konstruktoren
public Todo() {}
public Todo(
String title,
String description,
LocalDate 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 LocalDate getTargetDate() {
return targetDate;
}
public void setTargetDate(LocalDate 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;
}
@Override
public String toString() {
return (
"Todo{" +
"id=" +
id +
", title='" +
title +
'\'' +
", targetDate=" +
targetDate +
", completed=" +
completed +
'}'
);
}
}

View file

@ -0,0 +1,98 @@
package com.todoapp.model;
import jakarta.persistence.*;
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)
private String password;
@Column(nullable = false)
private String email;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Todo> 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<Todo> getTodos() {
return todos;
}
public void setTodos(List<Todo> 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 + '\'' +
'}';
}
}

View file

@ -0,0 +1,44 @@
package com.todoapp.util;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter(urlPatterns = { "/todos/*", "/users/*" })
public class AuthFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Initialisierung
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpSession session = httpRequest.getSession(false);
boolean isLoggedIn = (session != null && session.getAttribute("userId") != null);
boolean isLoginRequest = httpRequest.getRequestURI().contains("/auth/login");
boolean isRegisterRequest = httpRequest.getRequestURI().contains("/auth/register");
boolean isResourceRequest = httpRequest.getRequestURI().contains("/css/") ||
httpRequest.getRequestURI().contains("/js/");
if (isLoggedIn || isLoginRequest || isRegisterRequest || isResourceRequest) {
chain.doFilter(request, response);
} else {
httpResponse.sendRedirect(httpRequest.getContextPath() + "/auth/login");
}
}
@Override
public void destroy() {
// Aufräumen
}
}

View file

@ -0,0 +1,109 @@
//package com.todoapp.util;
//
//import org.hibernate.SessionFactory;
//import org.hibernate.boot.Metadata;
//import org.hibernate.boot.MetadataSources;
//import org.hibernate.boot.registry.StandardServiceRegistry;
//import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
//import org.hibernate.cfg.Configuration;
//
//public class HibernateUtil {
// private static StandardServiceRegistry registry;
// private static SessionFactory sessionFactory;
//
// public static SessionFactory getSessionFactory() {
// if (sessionFactory == null) {
// try {
// // Create registry
// registry = new StandardServiceRegistryBuilder()
// .configure()
// .build();
//
// // Create MetadataSources
// MetadataSources sources = new MetadataSources(registry);
//
// // Create Metadata
// Metadata metadata = sources.getMetadataBuilder().build();
//
// // Create SessionFactory
// sessionFactory = metadata.getSessionFactoryBuilder().build();
//
// } catch (Exception e) {
// e.printStackTrace();
// if (registry != null) {
// StandardServiceRegistryBuilder.destroy(registry);
// }
// }
// }
// return sessionFactory;
// }
//
// public static void shutdown() {
// if (registry != null) {
// StandardServiceRegistryBuilder.destroy(registry);
// }
// }
//}
package com.todoapp.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
public class HibernateUtil {
private static SessionFactory sessionFactory;
public static 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 SessionFactory getSessionFactory() {
// if (sessionFactory == null) {
// try {
// // Create Configuration
// Configuration configuration = new Configuration();
//
// // Set JNDI DataSource
// Context ctx = new InitialContext();
// DataSource ds = (DataSource) ctx.lookup("jdbc/PostgresDataSource");
// configuration.setProperty("hibernate.connection.datasource",
// "jdbc/PostgresDataSource");
//
// //// Load hibernate.cfg.xml
// // configuration.configure("hibernate.cfg.xml");
//
// // Build SessionFactory
// sessionFactory = configuration.buildSessionFactory();
//
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// return sessionFactory;
// }
public static void shutdown() {
if (sessionFactory != null) {
sessionFactory.close();
}
}
}

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<server description="OpenLiberty Todo App Server">
<featureManager>
<feature>servlet-6.0</feature>
<feature>pages-3.1</feature>
<feature>jdbc-4.2</feature>
<feature>jndi-1.0</feature>
<feature>monitor-1.0</feature>
<feature>faces-4.0</feature>
<feature>websocket-2.1</feature>
</featureManager>
<httpEndpoint id="defaultHttpEndpoint"
httpPort="9080"
httpsPort="9443" />
<applicationManager autoExpand="true" />
<dataSource id="PostgresDataSource" jndiName="jdbc/PostgresDataSource">
<jdbcDriver libraryRef="PostgresLib" />
<properties.postgresql serverName="${POSTGRES_HOST}"
portNumber="${POSTGRES_PORT}"
databaseName="${POSTGRES_DB}"
user="${POSTGRES_USER}"
password="${POSTGRES_PASSWORD}" />
</dataSource>
<library id="PostgresLib">
<fileset dir="${server.config.dir}/lib" includes="postgresql-*.jar" />
</library>
</server>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
version="3.0">
<persistence-unit name="todoPU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>com.todoapp.model.User</class>
<class>com.todoapp.model.Todo</class>
<properties>
<property name="jakarta.persistence.jdbc.driver" value="org.postgresql.Driver"/>
<property name="jakarta.persistence.jdbc.url" value="jdbc:postgresql://postgresdb:5432/todo_db"/>
<property name="jakarta.persistence.jdbc.user" value="postgres"/>
<property name="jakarta.persistence.jdbc.password" value="postgres"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- JNDI connection settings -->
<property name="hibernate.connection.datasource">jdbc/PostgresDataSource</property>
<!-- Select our SQL dialect -->
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<!-- Echo the SQL to stdout -->
<property name="hibernate.show_sql">true</property>
<!-- Set the current session context -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- Entity mapping -->
<mapping class="com.todoapp.model.User"/>
<mapping class="com.todoapp.model.Todo"/>
</session-factory>
</hibernate-configuration>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-facesconfig_4_0.xsd"
version="4.0">
</faces-config>

View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<display-name>Todo Application</display-name>
<welcome-file-list>
<welcome-file>login.xhtml</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>jakarta.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<context-param>
<param-name>jakarta.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<error-page>
<error-code>404</error-code>
<location>/error.xhtml</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error.xhtml</location>
</error-page>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>

View file

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="jakarta.faces.html"
xmlns:f="jakarta.faces.core">
<h:head>
<meta charset="UTF-8"/>
<title>Fehler</title>
<h:outputStylesheet name="./css/style.css"/>
</h:head>
<h:body>
<div class="container">
<h1>Oops! Es ist ein Fehler aufgetreten</h1>
<div class="alert alert-danger">
<p>Etwas ist schiefgelaufen. Bitte versuchen Sie es später erneut.</p>
<p>Fehlercode: #{facesContext.externalContext.responseStatusCode}</p>
</div>
<div class="form-group">
<h:link outcome="/todos" value="Zurück zur Startseite" styleClass="btn"/>
</div>
</div>
</h:body>
</html>

View file

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="jakarta.faces.html"
xmlns:f="jakarta.faces.core">
<h:head>
<meta charset="UTF-8"/>
<title>Login</title>
<h:outputStylesheet name="./css/style.css"/>
</h:head>
<h:body>
<div class="container">
<h1>Login</h1>
<h:messages globalOnly="true" styleClass="alert alert-danger"/>
<h:form>
<div class="form-group">
<h:outputLabel for="username" value="Benutzername:"/>
<h:inputText id="username" value="#{authBean.user.username}" required="true"/>
</div>
<div class="form-group">
<h:outputLabel for="password" value="Passwort:"/>
<h:inputSecret id="password" value="#{authBean.user.password}" required="true"/>
</div>
<div class="form-group">
<h:commandButton value="Anmelden" action="#{authBean.login}" styleClass="btn"/>
</div>
</h:form>
<p>Noch keinen Account? <h:link outcome="register" value="Registrieren"/></p>
</div>
</h:body>
</html>

View file

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="jakarta.faces.html"
xmlns:f="jakarta.faces.core">
<h:head>
<meta charset="UTF-8"/>
<title>Registrierung</title>
<h:outputStylesheet name="./css/style.css"/>
</h:head>
<h:body>
<div class="container">
<h1>Registrierung</h1>
<h:messages globalOnly="true" styleClass="alert alert-danger"/>
<h:form>
<div class="form-group">
<h:outputLabel for="username" value="Benutzername:"/>
<h:inputText id="username" value="#{authBean.user.username}" required="true"/>
</div>
<div class="form-group">
<h:outputLabel for="email" value="E-Mail:"/>
<h:inputText id="email" value="#{authBean.user.email}" required="true"/>
</div>
<div class="form-group">
<h:outputLabel for="password" value="Passwort:"/>
<h:inputSecret id="password" value="#{authBean.user.password}" required="true"/>
</div>
<div class="form-group">
<h:outputLabel for="confirmPassword" value="Passwort bestätigen:"/>
<h:inputSecret id="confirmPassword" value="#{authBean.confirmPassword}" required="true"/>
</div>
<div class="form-group">
<h:commandButton value="Registrieren" action="#{authBean.register}" styleClass="btn"/>
</div>
</h:form>
<p>Bereits registriert? <h:link outcome="login" value="Anmelden"/></p>
</div>
</h:body>
</html>

View file

@ -0,0 +1,246 @@
/* Allgemeine Stile */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
background-color: #f4f4f4;
color: #333;
}
.container {
max-width: 900px;
margin: 0 auto;
padding: 20px;
}
h1 {
margin-bottom: 20px;
text-align: center;
color: #333;
}
/* Header und Navigation */
header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #ddd;
}
.user-info {
display: flex;
align-items: center;
gap: 20px;
}
.logout-btn {
background-color: #f44336;
color: white;
padding: 5px 10px;
border-radius: 4px;
text-decoration: none;
}
.logout-btn:hover {
background-color: #d32f2f;
}
/* Formulare */
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.form-group button {
background-color: #4caf50;
color: white;
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.form-group button:hover {
background-color: #45a049;
}
/* Alerts */
.alert {
padding: 10px;
margin-bottom: 15px;
border-radius: 4px;
}
.alert-danger {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.alert-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
/* Links */
a {
color: #007bff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* Buttons */
.btn {
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: 400;
line-height: 1.42857143;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
border: 1px solid transparent;
border-radius: 4px;
text-decoration: none;
}
.btn:hover {
text-decoration: none;
}
.add-btn {
background-color: #4caf50;
color: white;
}
.add-btn:hover {
background-color: #45a049;
}
.edit-btn {
background-color: #2196f3;
color: white;
}
.edit-btn:hover {
background-color: #0b7dda;
}
.delete-btn {
background-color: #f44336;
color: white;
}
.delete-btn:hover {
background-color: #d32f2f;
}
.complete-btn {
background-color: #ff9800;
color: white;
}
.complete-btn:hover {
background-color: #e68a00;
}
.cancel-btn {
background-color: #9e9e9e;
color: white;
}
.cancel-btn:hover {
background-color: #7e7e7e;
}
.save-btn {
background-color: #4caf50;
color: white;
}
.save-btn:hover {
background-color: #45a049;
}
/* Todo-Liste */
.todo-actions {
margin-bottom: 20px;
text-align: right;
}
.todo-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
.todo-table th,
.todo-table td {
padding: 10px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.todo-table th {
background-color: #f2f2f2;
}
.todo-table tr:hover {
background-color: #f5f5f5;
}
.todo-table tr.completed {
background-color: #f0f0f0;
color: #666;
}
.status {
padding: 3px 8px;
border-radius: 12px;
font-size: 12px;
}
.status.completed {
background-color: #4caf50;
color: white;
}
.status.pending {
background-color: #ff9800;
color: white;
}
.no-todos {
text-align: center;
padding: 20px;
background-color: #f9f9f9;
border-radius: 4px;
margin-top: 20px;
}

View file

@ -0,0 +1,46 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="jakarta.faces.html"
xmlns:f="jakarta.faces.core">
<f:metadata>
<f:viewAction action="#{todoBean.loadTodo}" />
</f:metadata>
<h:head>
<meta charset="UTF-8"/>
<title>#{todoBean.todo.id == null ? 'Neue Aufgabe' : 'Aufgabe bearbeiten'}</title>
<h:outputStylesheet name="./css/style.css"/>
</h:head>
<h:body>
<div class="container">
<h1>#{todoBean.todo.id == null ? 'Neue Aufgabe' : 'Aufgabe bearbeiten'}</h1>
<h:form>
<h:inputHidden value="#{todoBean.todo.id}"/>
<div class="form-group">
<h:outputLabel for="title" value="Titel:"/>
<h:inputText id="title" value="#{todoBean.todo.title}" required="true"/>
</div>
<div class="form-group">
<h:outputLabel for="description" value="Beschreibung:"/>
<h:inputTextarea id="description" value="#{todoBean.todo.description}" rows="5"/>
</div>
<div class="form-group">
<h:outputLabel for="targetDate" value="Fälligkeitsdatum:"/>
<h:inputText id="targetDate" value="#{todoBean.todo.targetDate}" required="false">
<f:convertDateTime type="localDate" pattern="yyyy-MM-dd"/>
</h:inputText>
</div>
<div class="form-group">
<h:commandButton value="Speichern"
action="#{todoBean.saveOrUpdateTodo}"
styleClass="btn save-btn"/>
<h:link outcome="todo-list" value="Abbrechen" styleClass="btn cancel-btn"/>
</div>
</h:form>
</div>
</h:body>
</html>

View file

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="jakarta.faces.html"
xmlns:f="jakarta.faces.core"
xmlns:ui="jakarta.faces.facelets">
<h:head>
<meta charset="UTF-8"/>
<title>Meine Aufgaben</title>
<h:outputStylesheet name="./css/style.css"/>
</h:head>
<h:body>
<div class="container">
<header>
<h1>Meine Aufgaben</h1>
<div class="user-info">
<span>Hallo, #{sessionScope.username}!</span>
<h:form>
<h:commandLink action="#{authBean.logout}" value="Abmelden" styleClass="logout-btn"/>
</h:form>
</div>
</header>
<div class="todo-actions">
<h:link outcome="todo-form" value="Neue Aufgabe" styleClass="btn add-btn"/>
</div>
<h:panelGroup rendered="#{empty todoBean.todosByUser}">
<div class="no-todos">
<p>Keine Aufgaben vorhanden. Erstellen Sie eine neue Aufgabe!</p>
</div>
</h:panelGroup>
<h:dataTable value="#{todoBean.todosByUser}" var="todo" styleClass="todo-table"
rendered="#{not empty todoBean.todosByUser}">
<h:column>
<f:facet name="header">Titel</f:facet>
#{todo.title}
</h:column>
<h:column>
<f:facet name="header">Beschreibung</f:facet>
#{todo.description}
</h:column>
<h:column>
<f:facet name="header">Fälligkeitsdatum</f:facet>
<h:outputText value="#{todo.targetDate}">
<f:convertDateTime type="localDate" pattern="yyyy-MM-dd"/>
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">Status</f:facet>
<h:outputText value="Erledigt" rendered="#{todo.completed}" styleClass="status completed"/>
<h:outputText value="Offen" rendered="#{not todo.completed}" styleClass="status pending"/>
</h:column>
<h:column>
<f:facet name="header">Aktionen</f:facet>
<h:link outcome="todo-form" value="Bearbeiten" styleClass="btn edit-btn">
<f:param name="id" value="#{todo.id}"/>
</h:link>
<h:form>
<h:commandLink action="#{todoBean.deleteTodo(todo.id)}" value="Löschen"
styleClass="btn delete-btn"
onclick="return confirm('Möchten Sie diese Aufgabe wirklich löschen?')"/>
<h:commandLink action="#{todoBean.completeTodo(todo.id)}" value="#{todo.completed ? 'Als offen markieren' : 'Als erledigt markieren'}"
styleClass="btn complete-btn"/>
</h:form>
</h:column>
</h:dataTable>
</div>
</h:body>
</html>

View file

@ -1,18 +0,0 @@
FROM mcr.microsoft.com/devcontainers/java:1-21-bullseye
ARG INSTALL_MAVEN="false"
ARG MAVEN_VERSION=""
ARG INSTALL_GRADLE="false"
ARG GRADLE_VERSION=""
RUN apt-get update && apt-get upgrade -y && \
apt-get install -y \
git
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
RUN mkdir -p /workspaces/${localWorkspaceFolderBasename} && chown -R vscode:vscode /workspaces/${localWorkspaceFolderBasename}
ENTRYPOINT ["/bin/bash"]

View file

@ -1,9 +0,0 @@
{
"name": "Jakarta EE Todo App & PostgreSQL",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspaces",
"extensions": ["vscjava.vscode-java-pack", "redhat.vscode-xml"],
"forwardPorts": [9080],
"remoteUser": "vscode"
}

View file

@ -1,32 +0,0 @@
version: "3.8"
volumes:
postgres-data:
services:
app:
container_name: javadev
build:
context: .
dockerfile: Dockerfile
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: postgres
POSTGRES_HOSTNAME: postgresdb
volumes:
- ..:/workspace
command: sleep infinity
network_mode: service:db
stdin_open: true
db:
container_name: postgresdb
image: postgres:latest
restart: unless-stopped
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: postgres

View file

@ -1,49 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>jakarta-servlet-todo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<jakartaee.version>10.0.0</jakartaee.version>
<hibernate.version>6.0.0.Final</hibernate.version>
<postgresql.version>42.3.1</postgresql.version>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>${jakartaee.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,36 +0,0 @@
package com.example.dao;
import com.example.model.Todo;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import java.util.List;
public class TodoDAO {
@PersistenceContext
private EntityManager em;
@Transactional
public void create(Todo todo) {
em.persist(todo);
}
public List<Todo> findAll() {
return em.createQuery("SELECT t FROM Todo t", Todo.class).getResultList();
}
@Transactional
public void update(Todo todo) {
em.merge(todo);
}
@Transactional
public void delete(Long id) {
Todo todo = em.find(Todo.class, id);
if (todo != null) {
em.remove(todo);
}
}
}

View file

@ -1,43 +0,0 @@
package com.example.model;
import jakarta.persistence.*;
@Entity
@Table(name = "todos")
public class Todo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private boolean 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 boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
}

View file

@ -1,40 +0,0 @@
package com.example.servlet;
import com.example.dao.TodoDAO;
import com.example.model.Todo;
import jakarta.inject.Inject;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@WebServlet("/todos")
public class TodoServlet extends HttpServlet {
@Inject
private TodoDAO todoDAO;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
List<Todo> todos = todoDAO.findAll();
req.setAttribute("todos", todos);
req.getRequestDispatcher("/index.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String title = req.getParameter("title");
boolean completed = "on".equals(req.getParameter("completed"));
Todo todo = new Todo();
todo.setTitle(title);
todo.setCompleted(completed);
todoDAO.create(todo);
resp.sendRedirect(req.getContextPath() + "/todos");
}
}

View file

@ -1,19 +0,0 @@
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
version="3.0">
<persistence-unit name="todoPU">
<class>com.example.model.Todo</class>
<properties>
<property name="jakarta.persistence.jdbc.driver" value="org.postgresql.Driver"/>
<property name="jakarta.persistence.jdbc.url" value="jdbc:postgresql://db:5432/tododb"/>
<property name="jakarta.persistence.jdbc.user" value="todouser"/>
<property name="jakarta.persistence.jdbc.password" value="todopass"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>

View file

@ -1,9 +0,0 @@
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>JakartaEE Todo App</display-name>
</web-app>

View file

@ -1,36 +0,0 @@
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>Todo App</title>
</head>
<body>
<h1>Todo App</h1>
<form method="post" action="todos">
<label for="title">Title:</label>
<input type="text" id="title" name="title" required />
<label for="completed">Completed:</label>
<input type="checkbox" id="completed" name="completed" />
<button type="submit">Add Todo</button>
</form>
<h2>Todos</h2>
<ul>
<%
List<com.example.model.Todo> todos = (List<com.example.model.Todo>) request.getAttribute("todos");
if (todos != null) {
for (com.example.model.Todo todo : todos) {
%>
<li><%= todo.getTitle() %> - <%= todo.isCompleted() ? "Completed" : "Pending" %></li>
<%
}
}
%>
</ul>
</body>
</html>