Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
ff626d7
contract
hohwille Nov 21, 2022
3c1d8cb
consolidated properties
fbueltho Nov 21, 2022
a1a5e9b
add random activity endpoint which calls The Bored API
fbueltho Nov 21, 2022
e54beca
Added exercise for inter service calls
magail Nov 21, 2022
6453afe
add resilience/fault tolerance when calling The Bored API
fbueltho Nov 21, 2022
a799625
Added more exercise for inter service calls
magail Nov 21, 2022
50122da
Updated exercise description 7 and 8
magail Nov 22, 2022
a17327f
Fixed source listing
magail Nov 22, 2022
170b1d7
Updated README.adoc
magail Nov 22, 2022
5a42602
added OpenAPI exercise description
fbueltho Nov 22, 2022
e414def
update Fault Tolerance exercise description
fbueltho Nov 22, 2022
ece566e
updated inter-service call exercise description
fbueltho Nov 22, 2022
0bc56ef
adjuested openapi exercise
Nov 22, 2022
c109fd5
Merge branch 'solution/complete' of https://github.com/devonfw-traini…
Nov 22, 2022
b4c88c4
added example to readme
Nov 22, 2022
67fde59
Update 3-logic.asciidoc
hohwille Nov 22, 2022
a83cf2f
Update 3-logic.asciidoc
hohwille Nov 22, 2022
f30f583
Update 1-setup.asciidoc
hohwille Nov 22, 2022
54f957d
no numbering
hohwille Nov 22, 2022
6fcbe76
remove service and validation
hohwille Nov 22, 2022
a2f4476
remove logic
hohwille Nov 22, 2022
4ed132b
updated service exercise documentation
fbueltho Nov 23, 2022
dbfe84e
test: add more data access tests
fbueltho Nov 29, 2022
6a4aa41
test: force WireMock to start on random port
fbueltho Nov 29, 2022
23e9de5
build: help Eclipse (m2e) activate annotation processing by default
fbueltho Nov 29, 2022
2a7ea40
chore: upgrade Quarkus
fbueltho Nov 29, 2022
7ff1cfc
migrated to jakarta EE
hohwille Aug 7, 2023
b76bfa8
migrated full solution to jakarta EE
hohwille Sep 25, 2023
6e7f7db
MapStruct fix for JakartaEE
hohwille Sep 25, 2023
7e9b74d
Map-Struct update
hohwille Sep 25, 2023
7b96d36
enable legacy naming due to hibernate major update that changes seque…
hohwille Sep 25, 2023
0100de0
fixed sequences and mapstruct generation
hohwille Sep 25, 2023
b69de4e
Update setup.asciidoc
magail Oct 16, 2023
0444690
Updated to Java 21 and some dependencies, fixed pom, updated document…
magail Jan 15, 2024
cc68fd5
Updated to Java 21 and some dependencies, fixed pom, updated document…
magail Jan 18, 2024
206d99a
upgrade and jakartaEE refactoring
hohwille Jan 25, 2024
c0d4ebd
fix
hohwille Jan 25, 2024
520ae15
Added DELETE methid to fix CORS error
Jan 25, 2024
e63a0db
Added DELETE methid to fix CORS error
Jan 25, 2024
f80172b
Added DELETE methid to fix CORS error
Jan 25, 2024
345bd55
Updated to Java 21 and some dependencies, fixed pom, updated document…
magail Jan 15, 2024
8fa5164
Replaced javax. with jakarta. imports
magail Oct 16, 2023
df01da4
Updated the devon ide scripts path
Jan 25, 2024
ea7eddb
Updated the devon ide scripts path
Jan 25, 2024
4a5f85d
Updated the devon ide scripts path
Jan 25, 2024
19bb335
updated InjectMock import to io.quarkus.test.InjectMock
Jan 30, 2024
1264b0c
updated InjectMock import to io.quarkus.test.InjectMock
Jan 30, 2024
b0f3185
added openapi.yaml
Jan 30, 2024
277058e
deleted open api properties
Jan 30, 2024
b9a7a73
added openapi.yaml
Jan 30, 2024
d9582be
deleted open api properties
Jan 30, 2024
3323e78
deleted openapi properties
Jan 30, 2024
02ef9e1
deleted openapi properties
Jan 30, 2024
d7b93e4
deleted properties from inter-service exercise
Jan 31, 2024
211a04a
deleted properties from inter-service exercise
Jan 31, 2024
8783da3
Add Ollama Docker configuration and integrate it via API call
caladek Mar 12, 2025
1cc8a61
extend use case for adding random activity so that also multiple acti…
caladek Mar 12, 2025
a5a976a
add unit tests to cover new use cases that were introduced together w…
caladek Mar 12, 2025
9ac12df
add remaining unit tests to cover new use cases that were introduced …
caladek Mar 12, 2025
f5e0987
adapt and extend description of exercises to match usage of Ollama in…
caladek Mar 12, 2025
62753b8
Merge pull request #11 from cagergen/feature/integrate-ollama-as-bore…
tahoell Mar 21, 2025
bb4578c
remove not required distinction between existing and not-existing Oll…
caladek Mar 21, 2025
d188cba
Merge pull request #14 from cagergen/feature/integrate-ollama-as-bore…
magail Mar 24, 2025
395605f
Merge branch 'main' into solution/dataaccess
CalvinMende Apr 2, 2025
8e3b99f
fix: add back relevant files after main branch merge
CalvinMende Apr 2, 2025
9d77787
Merge branch 'main' into solution/dataaccess
CalvinMende Apr 2, 2025
cbaa37b
fix: fixed files after merge
CalvinMende Apr 2, 2025
0f777ec
Merge branch 'solution/dataaccess' into solution/logic
CalvinMende Apr 2, 2025
ec45374
fix: fixed files after merge
CalvinMende Apr 2, 2025
e3bf2f9
feat: add logic for frontend
CalvinMende Apr 2, 2025
8769212
Merge branch 'solution/logic' into solution/service
CalvinMende Apr 2, 2025
ff21109
fix: fixed files after merge
CalvinMende Apr 2, 2025
e9d2c55
feat: add service functionality for frontend
CalvinMende Apr 2, 2025
62565e9
fix: fixed files after merge
CalvinMende Apr 2, 2025
a66e2db
Merge branch 'solution/logic' into solution/service
CalvinMende Apr 2, 2025
2d1c344
Merge branch 'solution/service' into solution/complete
CalvinMende Apr 2, 2025
9bf37a8
fix: fix test
CalvinMende Apr 2, 2025
3a541ca
Merge branch 'solution/service' into solution/complete
CalvinMende Apr 2, 2025
cb18835
fix: fixed files after merge
CalvinMende Apr 2, 2025
7ade1fa
Merge branch 'solution/logic' into solution/service
CalvinMende Apr 2, 2025
8b4ac36
fix: tests
CalvinMende Apr 2, 2025
670261f
fix: ollama not working in docker compose
CalvinMende Apr 2, 2025
05aec4c
Merge branch 'solution/service' into solution/complete
CalvinMende Apr 2, 2025
77d21ed
Merge branch 'main' into solution/dataaccess
CalvinMende Apr 2, 2025
a81dd77
Merge branch 'solution/dataaccess' into solution/logic
CalvinMende Apr 2, 2025
08a3735
Merge branch 'solution/logic' into solution/service
CalvinMende Apr 2, 2025
dc051a1
Merge branch 'solution/service' into solution/complete
CalvinMende Apr 2, 2025
85c4aef
chore: delete boredapi
CalvinMende Apr 2, 2025
fca6e94
Merge branch 'solution/logic' into solution/service
CalvinMende Apr 2, 2025
87c6214
Merge branch 'solution/service' into solution/complete
CalvinMende Apr 2, 2025
d41f631
Add interceptor for auth
Sascha-droid Mar 27, 2025
56a9434
Fix Tests
Sascha-droid Mar 31, 2025
6456294
Fix Cors-Issues
Sascha-droid Apr 2, 2025
04ab6d8
Clean up
Sascha-droid Apr 3, 2025
02d94f0
fix: ollama twice in docker compose
CalvinMende Apr 7, 2025
9a02ca6
Add diagramm
Sascha-droid Apr 10, 2025
f3b1f84
Merge pull request #23 from CalvinMende/solution/complete
magail Jun 17, 2025
0dae1e8
Merge branch 'feature/security-keycloak' into keycloak-interceptor-fo…
magail Mar 13, 2026
c858936
Merge pull request #24 from Sascha-droid/keycloak-interceptor-for-main
magail Mar 13, 2026
56aa686
Merge remote-tracking branch 'origin/solution/complete' into feature/…
magail Mar 16, 2026
9ce8ac3
Added solution to keycloak, some sonar-fixes and reformatting
magail Mar 16, 2026
edc4227
Deleted quarkus app from docker-compose
Mar 17, 2026
49ddb47
fixed keycloak setup and OIDC integration
hohwille Mar 18, 2026
2dd76d4
fix auth for test
hohwille Mar 19, 2026
c1b37df
fixed keycloak setup for loign and logout and corrected smaller issue…
Copilot-Group2023 Jul 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
536 changes: 293 additions & 243 deletions backend/pom.xml

Large diffs are not rendered by default.

75 changes: 7 additions & 68 deletions backend/src/main/docker/Dockerfile.jvm
Original file line number Diff line number Diff line change
@@ -1,80 +1,20 @@
####
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
#
# Before building the container image run:
#
# ./mvnw package
# Before building the docker image run:
# mvn package
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/code-with-quarkus-jvm .
# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/backend .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus-jvm
# docker run -i --rm -p 8080:8080 quarkus/backend
#
# If you want to include the debug port into your docker image
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
# when running the container
# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5005
#
# Then run the container using :
#
# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus-jvm
#
# This image uses the `run-java.sh` script to run the application.
# This scripts computes the command line to execute your Java application, and
# includes memory/GC tuning.
# You can configure the behavior using the following environment properties:
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
# in JAVA_OPTS (example: "-Dsome.property=foo")
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
# used to calculate a default maximal heap memory based on a containers restriction.
# If used in a container without any memory constraints for the container then this
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
# of the container available memory as set here. The default is `50` which means 50%
# of the available memory is used as an upper boundary. You can skip this mechanism by
# setting this value to `0` in which case no `-Xmx` option is added.
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
# is used to calculate a default initial heap memory based on the maximum heap memory.
# If used in a container without any memory constraints for the container then this
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
# is used as the initial heap size. You can skip this mechanism by setting this value
# to `0` in which case no `-Xms` option is added (example: "25")
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
# This is used to calculate the maximum value of the initial heap memory. If used in
# a container without any memory constraints for the container then this option has
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
# here. The default is 4096MB which means the calculated value of `-Xms` never will
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
# when things are happening. This option, if set to true, will set
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
# true").
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
# (example: "20")
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
# (example: "40")
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
# (example: "4")
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
# previous GC times. (example: "90")
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
# contain the necessary JRE command-line options to specify the required GC, which
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
# accessed directly. (example: "foo.example.com,bar.example.com")
# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/mvp-quarkus-jvm
#
###
FROM registry.access.redhat.com/ubi8/openjdk-21:1.18
Expand All @@ -93,5 +33,4 @@ USER 185
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"

ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]

ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.example.app.general.common.security;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Response;

@Path("/app")
public class AppEntryResource {
@GET
public Response entry() {
return Response.ok("OK - logged in").build();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
public class ApplicationAccessControlConfig {

/** Unique identifier of this application used as namespace prefix. */
public static final String APP_ID = "demo";
public static final String APP_ID = "app";

/**
* The namespace prefix build from {@link #APP_ID} and prepended to every permission to avoid name-clashing of
* permissions with other applications within the same application landscape in identity & access management (IAM).
* The namespace prefix build from {@link #APP_ID} and prepended to every permission to avoid name-clashing of permissions with other applications within the
* same application landscape in identity & access management (IAM).
*/
private static final String PREFIX = APP_ID + ".";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.example.app.general.common.security;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

@Path("/auth")
public class AuthLogoutResource {


@ConfigProperty(name = "quarkus.oidc.auth-server-url")
String authServerUrl;

@ConfigProperty(name = "app.frontend-client-id")
String frontendClientId;

@ConfigProperty(name = "app.logout-redirect")
String appLogoutRedirect;

@GET
@Path("/logout")
public Response logout(@QueryParam("idToken") String idToken) {
if (idToken == null || idToken.isBlank()) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Missing idToken")
.build();
}

String logoutUrl =
authServerUrl +
"/protocol/openid-connect/logout" +
"?client_id=" + encode(frontendClientId) +
"&id_token_hint=" + encode(idToken) +
"&post_logout_redirect_uri=" + encode(appLogoutRedirect);

return Response.status(Response.Status.FOUND)
.header("Location", logoutUrl)
.build();
}

private String encode(String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package org.example.app.general.common.security.logout;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder;
import org.jboss.resteasy.reactive.NoCache;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.IDToken;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.util.TokenUtil;

import java.net.URI;

public class GlobalLogoutEndpoint {
private final KeycloakSession session;
private final AuthenticationManager.AuthResult auth;

public GlobalLogoutEndpoint(KeycloakSession session) {
this.session = session;
this.auth = authenticateSession(session);
}

private AuthenticationManager.AuthResult authenticateSession(KeycloakSession session) {
AuthenticationManager.AuthResult authResult =
new AppAuthManager.BearerTokenAuthenticator(session).authenticate();

if (authResult == null) {
authResult = new AppAuthManager()
.authenticateIdentityCookie(session, session.getContext().getRealm());
}
return authResult;
}

@GET
@Path("")
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response initiateGlobalLogout() {
if (auth == null || auth.getUser() == null) {
return Response.status(Response.Status.UNAUTHORIZED).build();
}

RealmModel realm = session.getContext().getRealm();
ClientModel client = session.clients().getClientByClientId(realm, "frontend");
session.getContext().setClient(client);

URI redirectURI = buildLogoutRedirectURI(realm, client);
return Response.seeOther(redirectURI).build();
}

private URI buildLogoutRedirectURI(RealmModel realm, ClientModel client) {
URI baseRedirectURI = UriBuilder.fromUri(session.getContext().getAuthServerUrl())
.path(client.getBaseUrl())
.build();

IDToken idToken = generateIdToken(client);

return UriBuilder.fromUri(session.getContext().getAuthServerUrl())
.path("/realms/%s/protocol/openid-connect/logout".formatted(realm.getName()))
.queryParam("post_logout_redirect_uri", baseRedirectURI)
.queryParam("client_id", client.getClientId())
.queryParam("id_token_hint", session.tokens().encode(idToken))
.build();
}

private IDToken generateIdToken(ClientModel client) {
ClientSessionContext clientSessionCtx = DefaultClientSessionContext
.fromClientSessionScopeParameter(
auth.getSession().getAuthenticatedClientSessionByClient(client.getId()),
session
);

IDToken idToken = new IDToken();
idToken.id(KeycloakModelUtils.generateId());
idToken.type(TokenUtil.TOKEN_TYPE_ID);
idToken.subject(auth.getUser().getId());
idToken.audience(client.getClientId());
idToken.issuedNow();
idToken.issuedFor(client.getClientId());
idToken.issuer(auth.getToken().getIssuer());
idToken.setNonce(clientSessionCtx.getAttribute(OIDCLoginProtocol.NONCE_PARAM, String.class));
idToken.setSessionId(auth.getToken().getSessionId());
idToken.exp(auth.getToken().getExp());

return idToken;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.example.app.general.common.security.logout;

import org.keycloak.models.KeycloakSession;
import org.keycloak.services.resource.RealmResourceProvider;

public class GlobalLogoutResourceProvider implements RealmResourceProvider {
private final KeycloakSession session;

public GlobalLogoutResourceProvider(KeycloakSession session) {
this.session = session;
}

@Override
public Object getResource() {
return new GlobalLogoutEndpoint(session);
}

@Override
public void close() {
// NOOP
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.example.app.general.common.security.logout;

import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.services.resource.RealmResourceProviderFactory;

public class GlobalLogoutResourceProviderFactory implements RealmResourceProviderFactory {
public static final String PROVIDER_ID = "global-logout";

@Override
public RealmResourceProvider create(KeycloakSession session) {
return new GlobalLogoutResourceProvider(session);
}

@Override
public void init(org.keycloak.Config.Scope config) {
// NOOP
}

@Override
public void postInit(KeycloakSessionFactory factory) {
// NOOP
}

@Override
public void close() {
// NOOP
}

@Override
public String getId() {
return PROVIDER_ID;
}
}
61 changes: 61 additions & 0 deletions backend/src/main/java/org/example/app/task/common/TaskItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.example.app.task.common;

import java.time.LocalDateTime;

import org.example.app.general.common.ApplicationEntity;

/**
* {@link ApplicationEntity} for a single task item.
*/
public interface TaskItem extends ApplicationEntity {

/**
* @return the title of this task item. Gives a brief summary to describe what to do or buy.
*/
String getTitle();

/**
* @param title new value of {@link #getTitle()}.
*/
void setTitle(String title);

/**
* @return {@code true} if this task is completed (done), {@code false} otherwise.
*/
boolean isCompleted();

/**
* @param completed new value of {@link #isCompleted()}.
*/
void setCompleted(boolean completed);

/**
* @return {@code true} if this task is starred (marked as favorite), {@code false} otherwise.
*/
boolean isStarred();

/**
* @param starred new value of {@link #isStarred()}.
*/
void setStarred(boolean starred);

/**
* @return the deadline as until this task shall be {@link #isCompleted() completed}.
*/
LocalDateTime getDeadline();

/**
* @param deadline the new value of {@link #getDeadline()}.
*/
void setDeadline(LocalDateTime deadline);

/**
* @return the {@link TaskList#getId() primary key} of the owning {@link TaskList}.
*/
Long getTaskListId();

/**
* @param taskListId the new value of {@link #getTaskListId()}.
*/
void setTaskListId(Long taskListId);
}
Loading