diff --git a/modules/ROOT/images/grafana_stack_overview smaller.jpg b/modules/ROOT/images/grafana_stack_overview smaller.jpg new file mode 100644 index 0000000..7776984 Binary files /dev/null and b/modules/ROOT/images/grafana_stack_overview smaller.jpg differ diff --git a/modules/ROOT/images/kubectl_get-svc-namespace.gif b/modules/ROOT/images/kubectl_get-svc-namespace.gif new file mode 100644 index 0000000..dc8b8cf Binary files /dev/null and b/modules/ROOT/images/kubectl_get-svc-namespace.gif differ diff --git a/modules/ROOT/images/show_deployed_services.png b/modules/ROOT/images/show_deployed_services.png new file mode 100644 index 0000000..cb17247 Binary files /dev/null and b/modules/ROOT/images/show_deployed_services.png differ diff --git a/modules/ROOT/images/structure_application.svg b/modules/ROOT/images/structure_application.svg new file mode 100644 index 0000000..6c9f485 --- /dev/null +++ b/modules/ROOT/images/structure_application.svg @@ -0,0 +1,4 @@ + + + +RuntimeExceptionBusinessExceptionSecurityExceptionExceptionmessage: Stringcause: Throwable<<enum>>ErrorCodeBOOKING_NOT_FOUND INVITED_GUEST_NOT_FOUNDINVITED_GUEST_ALREADY_INVITED+ getErrorCode+ getHttpCodeValidationException
message already in Exception
message alread...
message can be set to "Forbidden" statically 
Code is null
HTTPCode is always 403
message can be set to...
Usually the cause is a validationFramework 

the message can be derived from that already in Exception
Usually the cause is...
we do not necessarily need an own exception for every case. Instead the errorCodes can be used to differentiate
we do not necessarily...
calls
calls

JUnit

JUnit
call via http

restclient
or
mockMVC
call via h...
TestCase 4 
Given a booking with known id should be returned from RestService

When an error occurs (each error is a own test case and the errors are thrown via the mock)

Then the service returns a corresponding httpCode and a JSON message containing a message, the ERROR_CODE and a UUID
TestCase 4...
<<RestControler>>BookingManagementRestService- ucManageBooking: UcManageBooking+ createBooking(BookingTo): BookingTo+ getBooking(id):BookingTo<<Component>>UcManageBooking- ucManageBooking: UcManageBooking+ createBooking(BookingTo): BookingTo+ getBooking(id):BookingTo
mock UcManageBooking, and throw exception on call
See mockito for reference
https://www.baeldung.com/mockito-exceptions
mock UcMan...
abstractApplicationExceptioncode: ErrorCodeuuid: UUIDBookingToidmodificationCounterinvitedGuestsInvitedGuestToidmodificationCounteremail: String1*
invitedGuests
invitedGuests
Text is not SVG - cannot display
\ No newline at end of file diff --git a/modules/ROOT/pages/cross_cutting/exceptions.adoc b/modules/ROOT/pages/cross_cutting/exceptions.adoc index 5a715ad..6f6a5d2 100644 --- a/modules/ROOT/pages/cross_cutting/exceptions.adoc +++ b/modules/ROOT/pages/cross_cutting/exceptions.adoc @@ -1,6 +1,15 @@ :imagesdir: ../images = Exceptions +[NOTE] +==== + +This article needs further clarification. Help the project by contributing. + +https://github.capgemini.com/CG-Europe-ABL/devonfw/issues/16 + +==== + == Business exceptions === Use a common base class @@ -8,4 +17,35 @@ * All business exceptions should inherit a common abstract base class so they can be distinguished from technical exceptions. ** Should be inheriting `RuntimeException` ** Should store the cause `Exception` -** Should contain a reason enum. The enum is project individual and contains values like `Conflict`, `NotFound` and `IncorrectInput`. This enum can later be used to decide how to inform the user about the problem e.g. via HTTP status codes when building REST apis. \ No newline at end of file +** Should contain a reason enum. The enum is project individual and contains values like `Conflict`, `NotFound` and `IncorrectInput`. This enum can later be used to decide how to inform the user about the problem e.g. via HTTP status codes when building REST apis. + + +== Exception handler +* the central entry point for exceptions in the application +* is annotated with `@ExceptionHandler` at each method which handles the exceptions +* each method has a return value of `ResponseEntity` +* within the method, the values for HTTPStatus, ErrorCode, Message and UUID are set +* the class is globally annotated with `@ControllerAdvice` +* for each type of Exception there should be a handler method to managet the exception + +Example code exception handler: +[source,java] +---- +@ControllerAdvice +public class CustomControllerAdvice { + + @ExceptionHandler(BusinessException.class) + public ResponseEntity handleBusinessException(Exception ex) { + ... + +---- + +== ErrorResponse class +* `ErrorResponse`: The ErrorResponse class returns the error object to the client. +* mainly consists of constructors with a different number of parameters that provides the information about the class variables (message, HTTPStatus code etc.). + +== More information + +https://www.tutorialspoint.com/spring_boot/spring_boot_exception_handling.htm[Spring Boot - Exception Handling (Tutorial),window=_blank] + +https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc[Official Documentation: Spring Boot Exception Handling, window=_blank] diff --git a/modules/ROOT/pages/cross_cutting/tracing.adoc b/modules/ROOT/pages/cross_cutting/tracing.adoc index fe15b50..f11553b 100644 --- a/modules/ROOT/pages/cross_cutting/tracing.adoc +++ b/modules/ROOT/pages/cross_cutting/tracing.adoc @@ -4,7 +4,142 @@ == Correlation ID -Most of the time it is handy to find all the log messages created as a result of a user action, service request or message. To achieve this it is best practice to a a correlation ID to every message. A correlation ID is a number or string that is the same for all log messages of a user action, service request or message but different for different user actions, service requests or messages. With that it is possible to filter the log messages. This uniqueness can be achieved by using a `UUID` as correlation ID. +Most of the time it is handy to find all the log messages created as a result of a user action, service request or message. To achieve this it is best practice to add a correlation ID to every message. A correlation ID is a number or string that is the same for all log messages of a user action, service request or message but different for different user actions, service requests or messages. With that it is possible to filter the log messages. This uniqueness can be achieved by using a `UUID` as correlation ID. The correlation ID is generated when a new user action, service request or message occurs. In distributed systems it is important to pass the correlation ID to called service to be able to find the log messages for the complete system. -For HTTP calls this is usually done in the header field `X-Correlation-Id`. For messaging systems like queues or service busses there is usually a special file in the message header to store the correlation ID. \ No newline at end of file +For HTTP calls this is usually done in the header field `X-Correlation-Id`. For messaging systems like queues or service busses there is usually a special file in the message header to store the correlation ID. + +== Observability + +=== How to set up an observability stack with Grafana, Prometheus, Tempo and Loki + +This short guide shows how to build an observability stack using Grafana, Prometheus, Tempo and Loki. + +*The prerequisite* is a spring boot app that is previously containerized with docker. + +You also need Kubernetes installed, which comes with Rancher Desktop. + +Helm should also be installed. + +==== Overview +The following graphic shows how a Grafana stack is usually built up. Several services are used for this. + + +image:grafana_stack_overview smaller.jpg["Overview Grafana-stack",scalewidth="80%",align="center"] + +Build up the following folder-structure in your application-project: + +[subs=+macros] +---- +├──/project-folder +| ├──/kubernetes +| ├─────/loki +| | └──/loki.yaml +| ├─────/prometheus-grafana +| | └──/chart.yaml +| | └──/values.yaml +| ├─────/promtrail +| | └──/promtrail.yaml +| ├─────/springboot-app +| | └──/springboot-app.yaml +| ├─────/tempo +| | └──/tempo.yaml +| └──/springboot-app +| ├──/source +| | └──/main +| | └──/test +| └──... + +---- + +We add the charts we need for our services and update the Helm repository: + +[source,shell] +helm repo add grafana https://grafana.github.io/helm-charts +helm repo update + +After we have done this, we will create a namespace for the observability tools. To do so, go to the shell and type in: +[source,shell] +kubectl create ns observability + +==== Install Promtail and Loki + +To install Promtail, run: +[source,shell] +cd promtail +helm upgrade --install promtail grafana/promtail -n observability -f promtail.yaml + +Deploy Loki inside the cluster: +[source,shell] +helm upgrade --install loki grafana/loki-distributed -n observability + +We are interested in the `loki-loki-distributed-gateway` service by Loki in order to get the logs send. `Grafana` also needs that as a datasource. + +==== Installing Tempo + +To install Tempo as a prerequisite we need to install minio. This is a tool that helps us to simulate an AWS Object Storage s3 service. + +[source,shell] +cd ../tempo +kubectl apply -f minio.yaml + +To deploy Tempo, run: +[source,shell] +helm upgrade --install tempo grafana/tempo-distributed -n observability -f tempo.yaml + +==== Install Prometheus and Grafana + +[source,shell] +helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +helm repo update + +after that, run: +[source,shell] +cd ../prometheus-grafana +helm dependency update +helm upgrade --install kube-prometheus-stack -n observability . + +==== checking the deployments +The following command + +[source,shell] +helm ls -n observability + +will show us this + +image::show_deployed_services.png["list with deployed services kubernetes",scaledwidth="80%",align="center"] + +[source,shell] +kubectl get svc -n observability + +will give us a list with all deployed services on our created namespace which we called `observability`. + +image::kubectl_get-svc-namespace.gif["list all services in kubernetes namespace",scalewidth="85%",align="center"] + +After done this, we have to deploy our Springboot-Application on Kubernetes. For that, go to the springboot-app/ directory and run the kubectl command: +[source,shell] +cd ../springboot-app +kubectl apply -f springboot-app.yaml + +NOTE: You can name the yaml-file anything you like, e.g.: deployment.yaml + +After successul deployment on Kubernetes, point out the external ip-address, on which the springboot-app run with: +[source,shell] +kubectl get deploy,svc,cm -l app=springboot-app + +and obtain out the port for Grafana running: +[source,shell] +kubectl get svc -n observability + +==== Testing the endpoints + +The Springboot-Application can be tested via the URL: +http://EXTERNAL-IP:8080/ENDPOINT + +Grafana should be accessed with: +http://EXTERNAL-IP:PORT-FOR-GRAFANA + + +== References +* link:https://opentelemetry.io/docs/instrumentation/java/[OpenTelemetry with Java] +* link:https://grafana.com/docs/[Grafana Docs] \ No newline at end of file diff --git a/modules/ROOT/pages/integration/rest_exception_handling.adoc b/modules/ROOT/pages/integration/rest_exception_handling.adoc index 3beae4a..7f7d038 100644 --- a/modules/ROOT/pages/integration/rest_exception_handling.adoc +++ b/modules/ROOT/pages/integration/rest_exception_handling.adoc @@ -1,4 +1,14 @@ = RESTful Exception Handling +// https://github.capgemini.com/CG-Europe-ABL/devonfw/issues/16 + +[NOTE] +==== + +This article needs further clarification. Help the project by contribution. + +https://github.capgemini.com/CG-Europe-ABL/devonfw/issues/16 + +==== This article gives advice on a proper exception handling in the service layer. It is important to catch all exceptions and map them to a meaningful result and not pass implementation details to the outside. @@ -118,5 +128,62 @@ E.g.: adding a property that is false on default and when set to true the detail |=== +== Concrete Steps + +=== Library + +* Use Jakarta EE ExceptionMapper +* Add a provider for spring boot? Or also for quarkus? + +=== Identify exceptions of groups + +* If exceptionmapper can handle mutliple specific exceptions, +** then add one exception handler per group that has a specific handling +* ELSE +** use a single exceptionmapper to catch everything. +** Create group specific handler that contain a handling method and a list of exception classes +** The exceptionmapper needs to identify (inject) all possible handlers and check for their ability to handle certain classes (performance?) +** It then delegates the exceptions dynamically + +=== Create the response + +* Create response factory that can create responses based on multiple input values. + +== Examples + +=== The structure of exceptions + +*The structure of exceptions can be described as follows:* + +First of all, this following picture shows a general structure of an application with exceptions. + +[#application-structure] +image::structure_application.svg["structure-application",scaledwidth="80%",align="center"] + + +* specific exception classes are derived from the ApplicationException class - such as BusinessException, ValidationException or SecurityException. + +* `ErrorCode` itself is defined as an enum and contains a set of internal constants that are mapped to HttpStatus codes (like 400, 404, or 500 etc.). + +==== Business Exception +* HTTP status code depends on the reason of the exception. Default: 400 (BAD REQUEST) +* Response Body consists of: a message, ErrorCode (a specific code) and UUID +* is thrown when a business rule within our application is violated (e.g. error, a compliance failure or if a requested data was not found - 404) + +==== Technical Exception +* HTTP status code is 500 (Internal Server Error) +* Response Body consists of: a message, ErrorCode (a specific code) and UUID +* is thrown when something goes wrong +* technical exception is usually derived from Java’s RuntimeException +* Example: Java’s built-in `IllegalArgumentException` + +=== Example application + +[NOTE] +==== +In this article, we need to add a link to the example-application that clarifies the workflow of exception-handling + +==== +// TODO: add the link to the example-application in github // TODO: Provide an example using all four exception types and an exception mapper