diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2c4f62fe980be9e92490513aa202de22b455a48b..c067424c3db31ce05c9d056fd039fd340d641a27 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,21 +5,61 @@ cache:
   paths:
     - .maven/
 stages:
-  - check
+  - lint
   - build
   - test
   - analyze
   - package
   - release
   - deploy
+
 before_script:
   - export NG_CLI_ANALYTICS="false"
   - export MAVEN_USER_HOME=`pwd`/.maven
 
-nohttp:
-  stage: check
+pmd:
+  stage: lint
+  cache: {}
+  allow_failure: true
+  when: always
+  before_script:
+    - 'export VERSION="6.40.0"'
+    - 'wget "https://github.com/pmd/pmd/releases/download/pmd_releases%2F$VERSION/pmd-bin-$VERSION.zip"'
+    - 'jar -xf "pmd-bin-$VERSION.zip"'
+    - 'chmod u+x "pmd-bin-$VERSION/bin/run.sh"'
+  script:
+    - '"pmd-bin-$VERSION/bin/run.sh" pmd -d src/main/java -f textcolor -R pmd_rules.xml 2>&1 | tee pmd.log'
+  needs: []
+  artifacts:
+    when: on_failure
+    paths:
+      - pmd.log
+
+checkstyle:
+  stage: lint
   script:
     - ./mvnw -ntp checkstyle:check -Dmaven.repo.local=$MAVEN_USER_HOME
+  needs: []
+
+prettier:
+  stage: lint
+  cache: {}
+  script:
+    - npm install
+    - npm run prettier:check
+  allow_failure: true
+  needs: []
+
+eslint:
+  stage: lint
+  cache: {}
+  script:
+    - npm install
+    # eslint exits 0 on warnings, this is a workaround to exit 1 when warnings are present.
+    - 'npm run lint | tee lint.log'
+    - "! grep -qE '✖ [0-9]+ problem' lint.log"
+  allow_failure: true
+  needs: []
 
 maven-compile:
   stage: build
@@ -30,18 +70,16 @@ maven-compile:
       - target/classes/
       - target/generated-sources/
     expire_in: 1 day
+  needs: []
 
 maven-test:
   # DinD service is required for Testcontainers
   services:
     - docker:20-dind
-
   variables:
     # Instruct Testcontainers to use the daemon of DinD.
     DOCKER_HOST: 'tcp://docker:2375'
     DOCKER_TLS_CERTDIR: ''
-  #  before_script:
-  #    - docker info
   stage: test
   script:
     - ./mvnw -ntp test -P-webapp -Dmaven.repo.local=$MAVEN_USER_HOME -Dspring.profiles.active=testcontainers || FAILED=true
@@ -55,23 +93,27 @@ maven-test:
       - target/surefire-reports
       - target/failsafe-reports
       - target/site
+    when: always
     expire_in: 30 day
   allow_failure: true
+  needs: []
 
 frontend-test:
   stage: test
+  cache: {}
   script:
     - npm install
-    - npm test || FAILED=true
-    - ls -Rl target/test-results
+    - npm test
   artifacts:
     reports:
       junit: target/test-results/TESTS-results-jest.xml
     paths:
       - target/test-results
       - target/jacoco
+    when: always
     expire_in: 1 day
   allow_failure: true
+  needs: []
 
 sonar-analyze:
   stage: analyze
diff --git a/pmd_rules.xml b/pmd_rules.xml
new file mode 100644
index 0000000000000000000000000000000000000000..49cc155acfcf869645c5c99d46329871bafefa05
--- /dev/null
+++ b/pmd_rules.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+
+<ruleset name="Custom Rules"
+    xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
+
+    <description>
+        Rules for the PMD linter
+    </description>
+
+    <rule ref="category/java/bestpractices.xml">
+        <exclude name="GuardLogStatement"/>
+        <exclude name="UseVarargs"/>
+    </rule>
+    <rule ref="category/java/codestyle.xml">
+        <exclude name="AtLeastOneConstructor"/>
+        <exclude name="OnlyOneReturn"/>
+        <exclude name="ShortClassName"/>
+        <exclude name="UselessParentheses"/>
+        <!-- does not work properly with annotation imports -->
+        <exclude name="UnnecessaryImport"/>
+
+        <!-- should be included, removed to find other lints -->
+        <exclude name="MethodArgumentCouldBeFinal"/>
+        <exclude name="LocalVariableCouldBeFinal"/>
+        <exclude name="FieldNamingConventions"/>
+        <exclude name="ShortVariable"/>
+        <exclude name="LongVariable"/>
+        <exclude name="UseUnderscoresInNumericLiterals"/>
+        <exclude name="CommentDefaultAccessModifier"/>
+        <exclude name="BooleanGetMethodName"/>
+    </rule>
+    <rule ref="category/java/design.xml">
+        <exclude name="LawOfDemeter"/>
+        <exclude name="DataClass"/>
+        <exclude name="ExcessiveImports"/>
+        <!-- seems to be broken in its default config -->
+        <exclude name="LoosePackageCoupling"/>
+    </rule>
+    <rule ref="category/java/documentation.xml">
+        <exclude name="CommentSize"/>
+        <exclude name="CommentRequired"/>
+        <exclude name="UncommentedEmptyConstructor"/>
+    </rule>
+    <rule ref="category/java/errorprone.xml">
+        <exclude name="BeanMembersShouldSerialize"/>
+        <exclude name="AvoidLiteralsInIfCondition"/>
+    </rule>
+    <rule ref="category/java/multithreading.xml"></rule>
+    <rule ref="category/java/performance.xml">
+        <exclude name="RedundantFieldInitializer"/>
+        <exclude name="TooFewBranchesForASwitchStatement"/>
+        <exclude name="AvoidInstantiatingObjectsInLoops"/>
+        <exclude name="AvoidFileStream"/>
+    </rule>
+    <rule ref="category/java/security.xml"></rule>
+
+</ruleset>
+