diff --git a/.github/workflows/checkBuild.yml b/.github/workflows/checkBuild.yml
index 6cf3139..800f842 100644
--- a/.github/workflows/checkBuild.yml
+++ b/.github/workflows/checkBuild.yml
@@ -18,9 +18,10 @@ jobs:
- uses: actions/checkout@v2
- name: Set up JDK 11
- uses: actions/setup-java@v1
+ uses: actions/setup-java@v2
with:
java-version: 11.x
+ distribution: 'adopt'
- name: Cache local Maven repository
uses: actions/cache@v2
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 989840b..1bb4a0a 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -13,9 +13,10 @@ jobs:
- uses: actions/checkout@v2
- name: Set up JDK 11
- uses: actions/setup-java@v1
+ uses: actions/setup-java@v2
with:
java-version: 11.x
+ distribution: 'adopt'
- name: Cache local Maven repository
uses: actions/cache@v2
diff --git a/.github/workflows/deployDockerHubDevelop.yml b/.github/workflows/deployDockerHubDevelop.yml
new file mode 100644
index 0000000..b484618
--- /dev/null
+++ b/.github/workflows/deployDockerHubDevelop.yml
@@ -0,0 +1,29 @@
+name: Deploy develop to DockerHub
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [ develop ]
+ paths-ignore:
+ - '**.md'
+
+jobs:
+ build_publish_docker:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Generate tag vars
+ id: tagvars
+ run: |
+ echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
+ echo "::set-output name=build_datetime::$(date -u +%Y%m%d-%H%M)"
+
+ - name: Builder Dockerimage and publish to Registry
+ uses: elgohr/Publish-Docker-Github-Action@master
+ with:
+ name: alb2k/fuel-filling-service
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
+ tags: "develop"
diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml
index 27c2add..8fb859e 100644
--- a/.github/workflows/gh-pages.yml
+++ b/.github/workflows/gh-pages.yml
@@ -14,9 +14,10 @@ jobs:
- uses: actions/checkout@v2
- name: Setup - Java
- uses: actions/setup-java@v1
+ uses: actions/setup-java@v2
with:
java-version: 11.0.x
+ distribution: 'adopt'
- name: Restore - Maven Cache
uses: actions/cache@v2
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 68d5404..85241b1 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -12,9 +12,10 @@ jobs:
- uses: actions/checkout@v2
- name: Set up JDK 11
- uses: actions/setup-java@v1
+ uses: actions/setup-java@v2
with:
java-version: 11.x
+ distribution: 'adopt'
- name: Cache local Maven repository
uses: actions/cache@v2
@@ -66,6 +67,26 @@ jobs:
asset_name: release.zip
asset_content_type: application/octet-stream
+ build_publish_docker:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Generate tag vars
+ id: tagvars
+ run: |
+ echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
+ echo "::set-output name=build_datetime::$(date -u +%Y%m%d-%H%M)"
+
+ - name: Builder Dockerimage and publish to Registry
+ uses: elgohr/Publish-Docker-Github-Action@master
+ with:
+ name: alb2k/fuel-filling-service
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
+ tags: "latest,master-${{ steps.tagvars.outputs.sha_short }}-${{ steps.tagvars.outputs.build_datetime }}"
+
after_release:
runs-on: ubuntu-latest
needs: [build_release_jar]
diff --git a/README.md b/README.md
index 68ffa4a..c9ff042 100644
--- a/README.md
+++ b/README.md
@@ -4,12 +4,10 @@ A helidon (microprofile) RESTful webservice with microstream.
The project represents a basic CRUD webservice where you can manage fuel fillings (of a car).
It is also shipped with a nice UI (openapi-ui) so that no external REST/HTTP client is required.
-This project was created for the [Microstream hackathon](https://hackathon.microstream.one/)
-
### Used technologies
* [Microstream](https://microstream.one/platforms/microstream-for-java/)
+* [Helidon MP](https://helidon.io/#getting-started)
* [Microprofile (config)](https://github.com/eclipse/microprofile-config)
-* [Helidon MP](https://helidon.io/#getting-started)
* [MP Health](https://github.com/eclipse/microprofile-health)
* Logging via [SLF4J](http://www.slf4j.org/) and [Apache Log4j 2](https://logging.apache.org/log4)
* [OpenApi](https://www.openapis.org/)
@@ -17,18 +15,26 @@ This project was created for the [Microstream hackathon](https://hackathon.micro
* [GitHub Actions](https://github.com/features/actions) for CI/CD
* [Heroku](https://www.heroku.com/) for hosting the demo
+### [Documentation in detail](docs/README.md)
+Documentation about this project is [available here](docs/README.md)
+
## [Demo](https://hackathon-ms-fuel-filling.herokuapp.com) [![Deployment Status](https://img.shields.io/github/workflow/status/alb2k/fuel-filling-service/Deploy%20CI?label=deployment)](https://github.com/alb2k/fuel-filling-service/actions/workflows/deploy.yml)
-The demo is hosted on heroku.
+The demo is hosted on heroku.
+It may take some seconds to start.
![openapi-ui screenshot](assets/OpenApiUI.png)
+* [OpenAPI-UI (redirection)](https://hackathon-ms-fuel-filling.herokuapp.com)
+* [OpenAPI](https://hackathon-ms-fuel-filling.herokuapp.com/openapi)
+* [Health](https://hackathon-ms-fuel-filling.herokuapp.com/health)
+
## Download [![Release Status](https://img.shields.io/github/workflow/status/alb2k/fuel-filling-service/Release%20CI?label=release)](https://github.com/alb2k/fuel-filling-service/actions/workflows/release.yml)
There are prebuilt executables, which save you from building the executable locally.
### JAR
* Check if you have Java 11 installed, if not [install it](https://adoptopenjdk.net/?variant=openjdk11&jvmVariant=hotspot)
-* Download the [latest zip from the releases](https://github.com/alb2k/fuel-filling-service/releases/latest)
-* Unzip it and run it locally with ``java -jar fuel-filling-service.jar``
+* Download the [latest zip from the releases](https://github.com/alb2k/fuel-filling-service/releases/latest) and unzip it
+* Run ``java -jar fuel-filling-service.jar``
* Open http://localhost:8080
→ you should get redirected to the OpenAPI UI
@@ -60,9 +66,17 @@ Requirements:
Requirements:
* Docker
-#### "Normal" Dockerfile
+#### Running the prebuilt image from DockerHub [![Latest docker version](https://img.shields.io/badge/docker-latest-%232684ff)](https://hub.docker.com/r/alb2k/fuel-filling-service/tags?name=latest&page=1) [![Develop docker version](https://img.shields.io/badge/docker-develop-%232684ff)](https://hub.docker.com/r/alb2k/fuel-filling-service/tags?name=develop&page=1)
+* Run the latest release using ``docker run --rm -p 8080:8080 --name fuel-filling alb2k/fuel-filling-service``
+* Stop/Remove it with ``docker stop fuel-filling``
+
+#### Building and running it
* Build the image with ``docker build -t fuel-filling .``
* Execute it with ``docker run --rm -p 8080:8080 --name fuel-filling fuel-filling``
* Stop/Remove it with ``docker stop fuel-filling``
-### Dependencies and Licenses [![dependency overview](https://img.shields.io/badge/dependency--overview-online-success?logo=apache-maven)](https://alb2k.github.io/fuel-filling-service/dependencies/) [![Apache License 2.0](https://img.shields.io/github/license/alb2k/fuel-filling-service?color=informational)](https://choosealicense.com/licenses/apache-2.0/)
+## Dependencies and Licenses [![dependency overview](https://img.shields.io/badge/dependency--overview-online-success?logo=apache-maven)](https://alb2k.github.io/fuel-filling-service/dependencies/) [![Apache License 2.0](https://img.shields.io/github/license/alb2k/fuel-filling-service?color=informational)](https://choosealicense.com/licenses/apache-2.0/)
+For the license of this project, check the [LICENSE file](LICENSE)
+A summary of all dependencies and their licenses is also available [online](https://alb2k.github.io/fuel-filling-service/dependencies/)
+
+This project was created for the [Microstream hackathon](https://hackathon.microstream.one/)
diff --git a/docs/GHActions.md b/docs/GHActions.md
new file mode 100644
index 0000000..63d2499
--- /dev/null
+++ b/docs/GHActions.md
@@ -0,0 +1,11 @@
+# GitHub Actions
+
+The following plans are used:
+| Plan | Runs when | Description |
+| --- | --- | --- |
+| [checkBuild.yml](../.github/workflows/checkBuild.yml) | Push or PullRequest on develop | Checks if the project is buildable (using ``mvn -B clean package``) |
+| [release.yml](../.github/workflows/release.yml) | Push on master | Builds a release jar and creates a new release draft on GitHub, where the jar is uploaded |
+| [deploy.yml](../.github/workflows/deploy.yml) | Push on master | Deploys the current code to [Heroku](Heroku.md) |
+| [gh-pages.yml](../.github/workflows/gh-pages.yml) | Push on master | Creates a [project-info-report about the dependencies](https://maven.apache.org/plugins/maven-project-info-reports-plugin/dependencies-mojo.html) and publishes it with [GitHub pages](https://pages.github.com/) |
+
+Results of the executed workflows occur in the [``Actions`` tab](https://github.com/alb2k/fuel-filling-service/actions)
diff --git a/docs/Helidon.md b/docs/Helidon.md
new file mode 100644
index 0000000..ea35a1c
--- /dev/null
+++ b/docs/Helidon.md
@@ -0,0 +1,41 @@
+# Helidon (Microprofile)
+
+[Helidon MP](https://helidon.io/docs/latest/#/about/02_introduction) is a [collection of Java Libaries](https://alb2k.github.io/fuel-filling-service/dependencies/#Dependency_Tree) mainly used for Java/Jakarte EE conform web/microservices.
+This project is based on [helidon-quickstart-mp](https://github.com/oracle/helidon/tree/master/examples/quickstarts/helidon-quickstart-mp).
+
+## Webservice
+![Heldion_Overview.png](https://user-images.githubusercontent.com/80211953/113520281-34e32f80-9592-11eb-8ea8-6d5d118864b2.png)
+
+The HTTP Webservice was done with [JAX-RS](https://en.wikipedia.org/wiki/Jakarta_RESTful_Web_Services) and other [Java/Jakarta EE technologies](https://en.wikipedia.org/wiki/Jakarta_EE) (see below).
+The "rest resources" (which represent the HTTP interface) are located in [src/main/java/hackathon/microstream/service/rest/resource](../src/main/java/hackathon/microstream/service/rest/resource).
+
+The integrated webserver is the by default shipped [Helidon-Webserver](https://github.com/oracle/helidon/tree/master/webserver/webserver).
+
+#### Validation
+If entities are transmitted to a method they are validated (via the ``@Valid`` annotation) using [jersey-bean-validation](https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/bean-validation.html).
+When the validation fails, a ``ConstraintViolationException`` is thrown which is automatically processed by the [ConstraintViolationExceptionMapper](../src/main/java/hackathon/microstream/service/system/ConstraintViolationExceptionMapper.java) so that it results in a better readable exception:
+```
+Unrecognized token 'trues': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
+ at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 7, column: 30]
+```
+
+#### Serialization
+For the serialization from JSON to Java objects and back [com.fasterxml.jackson](https://github.com/FasterXML/jackson) is used.
+This worked pretty good, however some tweaks had to be [applied to the defaults](../src/main/java/hackathon/microstream/service/system/ObjectMapperContextResolver.java):
+* By default LocalDate is written as array, which is not [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601) conform
+ → https://stackoverflow.com/a/28803634
+* By default ObjectMapper throws an exception, if unknown properties are supplied.
+ To be a bit less restrictive, unknown properties are allowed (easier handling of objects with / without UUID)
+
+To also automatically serialize Java objects (to JSON) in a response without the need of doing it manually with an ObjectMapper [org.glassfish.jersey.media:jersey-media-json-jackson](https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-json-jackson) is used.
+More information [available here](https://stackoverflow.com/questions/26207252/messagebodywriter-not-found-for-media-type-application-json).
+
+#### CDI
+The corresponding [service](../src/main/java/hackathon/microstream/service/provider) is injected into the rest resource via CDI.
+
+#### Microprofile
+Furthermore the integrated webserver can be configured with microprofile.
+For example, the [``server.port`` can be set](../src/main/resources/META-INF/microprofile-config.properties#L4-L6).
+
+#### Other tweaks
+* When a entity is not found, a [``NotFoundException``](../src/main/java/hackathon/microstream/dal/util/NotFoundException.java) is thrown, which is automatically catched by the [``NotFoundExceptionHandler``](../src/main/java/hackathon/microstream/service/rest/errorhandling/NotFoundExceptionHandler.java)
diff --git a/docs/Heroku.md b/docs/Heroku.md
new file mode 100644
index 0000000..76d97a0
--- /dev/null
+++ b/docs/Heroku.md
@@ -0,0 +1,74 @@
+# Heroku deployment
+The goal here was to deploy a demo online, so that you don't have to run the code locally and also have a showcase available.
+
+I used [Heroku](https://www.heroku.com/home) once before with GitHub so I choose that as my cloud platform again.
+
+## How to set it up
+The setup is based on [this guide](https://dev.to/heroku/deploying-to-heroku-from-github-actions-29ej)
+
+### Preparing Heroku
+At first: You need to [setup an account](https://signup.heroku.com/)
+
+TL;DR
+Web applications on Heroku are called [web dynos](https://www.heroku.com/dynos).
+A [free tier account on Heroku](https://www.heroku.com/pricing) grants you 550 dyno hours (and additional 450 hours if you add your credit card).
+Your dyno will automatically sleep/shut down if there is no activity in the last 30 minutes.
+
+After the account is set up, create a new app:
+![Pic](https://user-images.githubusercontent.com/80211953/111872102-e4fb4a80-898d-11eb-8151-ab41ca7a23b7.png)
+Choose your region, add a name (the app will be available under that ``https://.herokuapp.com``) and then click create app.
+
+### Deploying the project using GitHub
+Great, now we have a new app on heroku... but there isn't actually anything running, because nothing was deployed.
+
+So how can we deploy code (from GitHub)?
+There are 2 ways:
+* Setup a deployment pipeline directly in Heroku
+* Write a [GitHub actions](https://github.com/features/actions) workflow that builds and deploys the code
+
+I wanted to stay independent from Heroku and - through the fact that I already had experience with GitHub Actions - I ultimately choose those.
+
+#### Setting up secrets
+There have to be some [secrets stored for the coming GitHub Actions workflow](https://docs.github.com/en/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository):
+* ``HEROKU_APP_NAME``
+The app-name that is used on heroku e.g. ``hackathon-ms-fuel-filling``
+* ``HEROKU_API_KEY``
+The API-Key so that the app can be deployed to heroku.
+The key can be generated via multiple methods.
+I recommend adding a new "authorization" (API-Key) using https://dashboard.heroku.com/account/applications#authorizations
+
+#### Writing the workflow
+Setup a new workflow and name it e.g. [deploy.yml](../.github/workflows/deploy.yml)
+
+The workflow has to meet the following requirements:
+* Executed the workflow when a new release is created or manually
+* Build the app (as jar)
+* Deploy the app to Heroku
+
+The first two parts are pretty easy doable if you know a bit about GitHub Actions.
+The last part is a little bit more tricky:
+* GitHub Actions own virtual (linux) machines are by default equipped with the Heroku CLI.
More details are available [here](https://github.com/actions/virtual-environments#available-environments)
+* At first install [Heroku's Java plugin](https://github.com/heroku/plugin-java): ``heroku plugins:install java``
+* Deploy the jar with the deploy command ``heroku deploy:jar``
+ * Select the jar using ``target/fuel-filling-service.jar``
+ * Set the JDK to Java 11 using ``--jdk 11``.
Heroku trys to deploy by default with JDK 8.
+ * The libs folder must be included: ``--includes target/libs/``
+ * Select the app that you want to deploy to with ``--app ${{ secrets.HEROKU_APP_NAME }}``
+ * Of course Heroku also needs some type of authentication. This is done using ``HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}``
+
+In the last 2 steps you noted the use of ``${{ secret.XXX }}``.
+This is the usage of the GitHub secrets we created before.
+
+#### Using a Procfile
+There is one special case when you want to use Heroku: You have to either expose your app on Port 80 or you have to bind to Herokus ``PORT`` environment variable.
+
+This can be done easily using a [Procfile](/Procfile) in the repository root which contains ``-Dserver.port=$PORT``.
+
+#### Deploying it
+Great - now when everything was done correctly - you should be able to deploy it.
+Go to the ``Actions`` tab of your repository and run the Deployment workflow:
+![Pic](https://user-images.githubusercontent.com/80211953/111875065-137e2300-8998-11eb-9819-f83c0e04cdbc.png)
+
+After some time you should see that your app was deployed on Heroku.
+It is also useful to check the logs for problems and have a quick look at your app.
+![Pic](https://user-images.githubusercontent.com/80211953/111875837-1418b880-899c-11eb-895e-81c62ba2e5d4.png)
diff --git a/docs/Logging.md b/docs/Logging.md
new file mode 100644
index 0000000..ddbb5c9
--- /dev/null
+++ b/docs/Logging.md
@@ -0,0 +1,49 @@
+# Logging
+To protocol information about events in the app, some kind of logging is required.
+For Java therefore multiple logging frameworks exist. Some popular ones include SLF4J, LOG4J, JUL, Logback, etc.
+
+Helidon uses [JUL](https://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html) as default logger.
+
+### Using another logging framework
+However I prefer the more satisfying [SLF4J](http://www.slf4j.org/) (→ https://stackoverflow.com/a/11360517) in combination with [Log4j 2](https://logging.apache.org/log4j/2.x/).
+So an adapter was needed to redirect JUL to SLF4J...
+
+Luckily I found [this great guide about how to use Heldion with other logging frameworks](https://medium.com/helidon/helidon-logging-and-mdc-5de272cf085d) which basically tells you how to achieve that.
+
+#### TL;DR / What was done to add it to this project?
+* Added the following dependencies:
+ * [io.helidon.logging:helidon-logging-slf4j](https://mvnrepository.com/artifact/io.helidon.logging/helidon-logging-slf4j) (Helidon SLF4J integration)
+ * [org.slf4j:slf4j-api](https://mvnrepository.com/artifact/org.slf4j/slf4j-api) (logging facade)
+ * [org.slf4j:jul-to-slf4j](https://mvnrepository.com/artifact/org.slf4j/jul-to-slf4j) (JUL → SLF4J)
+ * [org.apache.logging.log4j:log4j-slf4j-impl](https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl) (SLF4J → LOG4J)
+ * [org.apache.logging.log4j:log4j-core](https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core) ("real" logging framework)
+* Added [log4j2.xml](../src/main/resources/log4j2.xml) which [configures the logging framework](https://logging.apache.org/log4j/2.x/manual/configuration.html)
+* Tweaked the [logging.properties](../src/main/resources/logging.properties) a bit so that it works correctly
+* Due to no available main entry class the SLF4JBridgeHandler for JUL is installed in the [Startup class](../src/main/java/hackathon/microstream/Startup.java#L26-L30)
+
+After everything was done the output looks like this:
+```
+"...java.exe" ... io.helidon.microprofile.cdi.Main
+[INFORMATION] 2021-04-03 20:31:47 [Thread[main,5,main]] Logging at initialization configured using classpath: /logging.properties
+[INFO ] 2021-04-03 20:31:48.476 [main] org.jboss.weld.Version - WELD-000900: 3.1.6 (Final)
+[INFO ] 2021-04-03 20:31:49.309 [main] org.jboss.weld.Bootstrap - WELD-ENV-000020: Using jandex for bean discovery
+[INFO ] 2021-04-03 20:31:49.531 [main] org.jboss.weld.Bootstrap - WELD-000101: Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously.
+[INFO ] 2021-04-03 20:31:49.679 [main] org.jboss.weld.Event - WELD-000411: Observer method [BackedAnnotatedMethod] public org.glassfish.jersey.ext.cdi1x.internal.ProcessAllAnnotatedTypes.processAnnotatedType(@Observes ProcessAnnotatedType>, BeanManager) receives events for all annotated types. Consider restricting events using @WithAnnotations or a generic type with bounds.
+[INFO ] 2021-04-03 20:31:49.694 [main] org.jboss.weld.Event - WELD-000411: Observer method [BackedAnnotatedMethod] private io.helidon.microprofile.openapi.OpenApiCdiExtension.processAnnotatedType(@Observes ProcessAnnotatedType) receives events for all annotated types. Consider restricting events using @WithAnnotations or a generic type with bounds.
+[INFO ] 2021-04-03 20:31:50.465 [main] hackathon.microstream.Startup - The application is starting...
+[INFO ] 2021-04-03 20:31:50.465 [main] hackathon.microstream.Startup - Installing org.slf4j.bridge.SLF4JBridgeHandler
+[INFO ] 2021-04-03 20:31:50.465 [main] hackathon.microstream.Startup - Installed org.slf4j.bridge.SLF4JBridgeHandler successfully
+[INFO ] 2021-04-03 20:31:50.465 [main] hackathon.microstream.Startup - Initializing DB
+[INFO ] 2021-04-03 20:31:50.465 [main] hackathon.microstream.storage.DBManager - Loading configuration
+[INFO ] 2021-04-03 20:31:51.020 [main] hackathon.microstream.storage.DBManager - BaseDirectory is 'data'
+[INFO ] 2021-04-03 20:31:51.020 [main] hackathon.microstream.storage.DBManager - Initializing storageManager
+[INFO ] 2021-04-03 20:31:51.553 [main] hackathon.microstream.storage.DBManager - Demo mode; Using demo data
+[INFO ] 2021-04-03 20:31:51.553 [main] hackathon.microstream.Startup - Initialized DB
+[INFO ] 2021-04-03 20:31:51.553 [main] hackathon.microstream.Startup - The application is started
+[INFO ] 2021-04-03 20:31:51.553 [main] io.helidon.microprofile.server.ServerCdiExtension - Registering JAX-RS Application: App
+[INFO ] 2021-04-03 20:31:52.217 [main] org.hibernate.validator.internal.util.Version - HV000001: Hibernate Validator 6.1.2.Final
+[INFO ] 2021-04-03 20:31:53.253 [nioEventLoopGroup-2-1] io.helidon.webserver.NettyWebServer - Channel '@default' started: [id: 0x245155bc, L:/0:0:0:0:0:0:0:0:8080]
+[INFO ] 2021-04-03 20:31:53.253 [main] io.helidon.microprofile.server.ServerCdiExtension - Server started on http://localhost:8080 (and all other host addresses) in 6257 milliseconds (since JVM startup).
+[INFO ] 2021-04-03 20:31:53.268 [features-thread] io.helidon.common.HelidonFeatures - Helidon MP 2.2.1 features: [CDI, Config, Health, JAX-RS, Open API, Server]
+...
+```
diff --git a/docs/Microstream.md b/docs/Microstream.md
new file mode 100644
index 0000000..14dac6e
--- /dev/null
+++ b/docs/Microstream.md
@@ -0,0 +1,21 @@
+# Microstream
+
+[Microstream](https://microstream.one/) is a storage/persistence solution which uses Graphs and binary files for it's storage.
+
+The project was mainly written based on the [Microstream Getting Started Guide](https://manual.docs.microstream.one/data-store/getting-started)
+
+#### TL;DR / What was done to add it to this project?
+* Added [the dependencies](https://manual.docs.microstream.one/data-store/getting-started#prerequisites) to the [pom.xml](../pom.xml)
+* Added a [root instance / DBContext class](../src/main/java/hackathon/microstream/storage/DBContext.java), where all objects that should be persisted are attached to (more information available [here](https://manual.docs.microstream.one/data-store/getting-started#the-root-instance))
+* Added a [DB Manager](../src/main/java/hackathon/microstream/storage/DBManager.java), which manages (cares about initializing, reading, saving, shutdown ...) and holds the DBContext
+ * There also exist a demo mode → if enabled all data is overriden by the defaults on init
+ The demo mode is managed by [microprofile-config.properties](../src/main/resources/META-INF/microprofile-config.properties#L2)
+* Added [microstream-storage.properties](../src/main/resources/microstream-storage.properties)
+* Added a [DAL Layer](../src/main/java/hackathon/microstream/dal) which uses the DB Manager
+
+
+The app by default writes the storage / binary files to the ``data`` folder.
+
+
+### Notes
+* Microstream doesn't have any built in ID management. I used [UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier) for that.
diff --git a/docs/OpenAPI.md b/docs/OpenAPI.md
new file mode 100644
index 0000000..6aca878
--- /dev/null
+++ b/docs/OpenAPI.md
@@ -0,0 +1,25 @@
+# OpenAPI
+
+[OpenAPI](https://www.openapis.org/) is a standardized specification to describe REST-conform APIs.
+
+Helidon supports OpenAPI.
+For more information read [this guide](https://medium.com/helidon/project-helidon-and-openapi-54a1fadc75b1).
+
+## OpenAPI UI
+To test the app without the requirement of an external HTTP Client, you can simply add [OpenAPI UI](https://github.com/microprofile-extensions/openapi-ext/blob/main/openapi-ui/README.md) (derived from [Swagger UI](https://swagger.io/tools/swagger-ui/)).
+
+#### What was done to add it to this project?
+* Added the [dependency](https://mvnrepository.com/artifact/org.microprofile-ext.openapi-ext/openapi-ui) to the pom.xml
+* Annotated the Rest-Endpoints (e.g. ``@Operation`` or ``@APIResponse``)
+* Added a custom application class for more information → [App.java](../src/main/java/hackathon/microstream/service/system/App.java)
+* Customized the UI further with [microprofile-config.properties](../src/main/resources/META-INF/microprofile-config.properties#L9-L11)
+* Added a [RootResource](../src/main/java/hackathon/microstream/service/rest/resource/RootResource.java) which redirects to the OpenAPI UI endpoint when trying to get '/'
+
+I also highly recommend reading the ["Getting Started" page of OpenAPI-UI](https://github.com/microprofile-extensions/openapi-ext/blob/main/openapi-ui/README.md#getting-started), because it explains everything that was done in detail.
+
+
+Note: Using the app locally sometimes throws ``
+[WARN ] 2021-04-03 20:00:00.787 [helidon-2] io.helidon.webserver.RequestRouting - Default error handler: Response wasn't successfully sent.
+java.util.concurrent.CompletionException: io.helidon.webserver.SocketClosedException: Response channel is closed!
+`` when calling the root endpoint ('/').
+This problem does not occur when it is deployed on Heroku.
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..01a5a58
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,21 @@
+# Docs
+Documentation about this project
+
+## Details
+Each part / Used technology of the project has it's own documentation:
+1. [Helidon](Helidon.md)
+Webservice, CDI, Configuration with Microprofile and more
+2. [Microstream](Microstream.md)
+Graph based binary data storage
+3. [OpenAPI (+UI)](OpenAPI.md)
+Standardized specification to describe REST-conform APIs + UI
+4. [Logging](Logging.md)
+5. [GitHub Actions](GHActions.md)
+Continuous Integration (CI) / Continuous Delivery (CD)
+6. [Heroku](Heroku.md)
+How to host a demo online
+
+
+## Overview
+A quick overview how the project is designed to work:
+![Overview](https://user-images.githubusercontent.com/80211953/112724270-d7583e80-8f12-11eb-9506-5e62c647f98d.png)
diff --git a/pom.xml b/pom.xml
index 10598b7..e8eadaf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,9 +18,9 @@
UTF-8
- 2.2.1
+ 2.3.1
- 1.7.30
+ 1.7.31
2.14.1
@@ -84,18 +84,23 @@
jakarta.activation
jakarta.activation-api
+
+
org.glassfish.jersey.media
jersey-media-json-jackson
+
org.glassfish.jersey.ext
jersey-bean-validation
+
com.fasterxml.jackson.datatype
jackson-datatype-jsr310
+
org.jboss
jandex
@@ -103,11 +108,12 @@
true
-
+
org.microprofile-ext.openapi-ext
openapi-ui
1.1.4
+ runtime
@@ -144,12 +150,13 @@
${org.apache.logging.log4j.version}
-
+
jakarta.annotation
jakarta.annotation-api
1.3.5
+
commons-beanutils
commons-beanutils
@@ -178,7 +185,7 @@
org.apache.maven.plugins
maven-dependency-plugin
- 3.1.2
+ 3.2.0
org.apache.maven.plugins
@@ -203,7 +210,7 @@
org.jboss.jandex
jandex-maven-plugin
- 1.0.8
+ 1.1.0
org.codehaus.mojo
@@ -216,12 +223,12 @@
io.helidon.build-tools
helidon-maven-plugin
- 2.1.3
+ 2.2.1
io.helidon.build-tools
helidon-cli-maven-plugin
- 2.1.3
+ 2.2.1
@@ -243,7 +250,6 @@
true
true
runtime
- test
@@ -292,7 +298,7 @@
org.jmdns
jmdns
- 3.5.6
+ 3.5.7
diff --git a/src/main/java/hackathon/microstream/Startup.java b/src/main/java/hackathon/microstream/Startup.java
index 23e1a58..8e52151 100644
--- a/src/main/java/hackathon/microstream/Startup.java
+++ b/src/main/java/hackathon/microstream/Startup.java
@@ -11,6 +11,11 @@
import javax.enterprise.context.Initialized;
import javax.enterprise.event.Observes;
+/**
+ * Handles the startup and shutdown
+ *
+ * @see https://stackoverflow.com/a/11476587
+ */
@ApplicationScoped
public class Startup {
private final static Logger LOG = LoggerFactory.getLogger(Startup.class);
diff --git a/src/main/java/hackathon/microstream/dal/FillingRepository.java b/src/main/java/hackathon/microstream/dal/FillingRepository.java
index 3c87664..eb1e602 100644
--- a/src/main/java/hackathon/microstream/dal/FillingRepository.java
+++ b/src/main/java/hackathon/microstream/dal/FillingRepository.java
@@ -27,7 +27,7 @@ public List getAll() {
* @return
* @throws NotFoundException
*/
- public Filling getById(UUID id) {
+ public DBFilling getById(UUID id) {
if(id == null)
throw new IllegalArgumentException("id can't be null");
@@ -48,7 +48,7 @@ public Filling getById(UUID id) {
* @param filling
* @return
*/
- public Filling add(Filling filling) {
+ public DBFilling add(Filling filling) {
if(filling == null)
throw new IllegalArgumentException("filling can't be null");
@@ -72,13 +72,13 @@ public Filling add(Filling filling) {
* @return
* @throws NotFoundException
*/
- public Filling update(UUID id, Filling filling) {
+ public DBFilling update(UUID id, Filling filling) {
if(id == null)
throw new IllegalArgumentException("id can't be null");
if(filling == null)
throw new IllegalArgumentException("filling can't be null");
- Filling dbFilling = getById(id);
+ var dbFilling = getById(id);
// Update the properties in the bean
try {
diff --git a/src/main/java/hackathon/microstream/service/provider/FillingService.java b/src/main/java/hackathon/microstream/service/provider/FillingService.java
index e3776e8..c856f30 100644
--- a/src/main/java/hackathon/microstream/service/provider/FillingService.java
+++ b/src/main/java/hackathon/microstream/service/provider/FillingService.java
@@ -26,7 +26,7 @@ public List getAll() {
* @return
* @throws NotFoundException
*/
- public Filling getById(UUID id) {
+ public DBFilling getById(UUID id) {
return this.fillingRepository.getById(id);
}
@@ -35,7 +35,7 @@ public Filling getById(UUID id) {
* @param filling
* @return
*/
- public Filling add(Filling filling) {
+ public DBFilling add(Filling filling) {
return this.fillingRepository.add(filling);
}
@@ -45,7 +45,7 @@ public Filling add(Filling filling) {
* @return
* @throws NotFoundException
*/
- public Filling update(UUID id, Filling filling) {
+ public DBFilling update(UUID id, Filling filling) {
return this.fillingRepository.update(id, filling);
}
diff --git a/src/main/java/hackathon/microstream/service/system/ConstraintViolationExceptionMapper.java b/src/main/java/hackathon/microstream/service/system/ConstraintViolationExceptionMapper.java
index 9d43bda..b3a73fe 100644
--- a/src/main/java/hackathon/microstream/service/system/ConstraintViolationExceptionMapper.java
+++ b/src/main/java/hackathon/microstream/service/system/ConstraintViolationExceptionMapper.java
@@ -1,12 +1,14 @@
package hackathon.microstream.service.system;
-import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import java.util.stream.Collectors;
+/**
+ * Makes ConstraintViolationExceptions pretty
+ */
@Provider
public class ConstraintViolationExceptionMapper implements ExceptionMapper {
@Override
diff --git a/src/main/java/hackathon/microstream/service/system/ObjectMapperContextResolver.java b/src/main/java/hackathon/microstream/service/system/ObjectMapperContextResolver.java
index 4e08ba8..2f4f0ec 100644
--- a/src/main/java/hackathon/microstream/service/system/ObjectMapperContextResolver.java
+++ b/src/main/java/hackathon/microstream/service/system/ObjectMapperContextResolver.java
@@ -8,6 +8,23 @@
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
+/**
+ * The default provider for a configured {@link ObjectMapper}
+ *
+ * Configuration:
+ *
+ * -
+ * By default LocalDate is written as array, which is not ISO-8601 conform
+ * https://stackoverflow.com/a/28803634
+ *
+ * -
+ * By default ObjectMapper throws an exception, if unknown properties are supplied.
+ * To be a bit less restrictive, unknown properties are allowed (easier handling of objects with / without UUID)
+ *
+ *
+ *
+ *
+ */
@Provider
public class ObjectMapperContextResolver implements ContextResolver {
private final ObjectMapper MAPPER;
@@ -17,6 +34,7 @@ public ObjectMapperContextResolver() {
// Make LocalDate & Co to Strings (and vice versa)
MAPPER.registerModule(new JavaTimeModule());
MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
diff --git a/src/main/java/hackathon/microstream/storage/DBManager.java b/src/main/java/hackathon/microstream/storage/DBManager.java
index 1dc0993..e2a6e57 100644
--- a/src/main/java/hackathon/microstream/storage/DBManager.java
+++ b/src/main/java/hackathon/microstream/storage/DBManager.java
@@ -9,6 +9,9 @@
import java.time.LocalDate;
+/**
+ * Manages DB access (Microstream)
+ */
public class DBManager {
private static final Logger LOG = LoggerFactory.getLogger(DBManager.class);
diff --git a/src/main/resources/META-INF/microprofile-config.properties b/src/main/resources/META-INF/microprofile-config.properties
index a56e089..7f845df 100644
--- a/src/main/resources/META-INF/microprofile-config.properties
+++ b/src/main/resources/META-INF/microprofile-config.properties
@@ -1,5 +1,4 @@
# Application properties.
-#app.greeting=Hello
db.demoMode=true
# Microprofile server properties
@@ -8,4 +7,4 @@ server.host=0.0.0.0
# OpenAPI-UI
openapi.ui.title=Fuel filling webservice
-openapi.ui.swaggerHeaderVisibility=hidden
\ No newline at end of file
+openapi.ui.swaggerHeaderVisibility=hidden