This is an automated email from the ASF dual-hosted git repository.

dspavlov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite-teamcity-bot.git


The following commit(s) were added to refs/heads/master by this push:
     new 8210c550 IGNITE-28631: migrate TC Bot to Java 17, Gradle 9.5, Ignite 
2.18 and Jetty 12(#208)
8210c550 is described below

commit 8210c55035b1f5660c1de5f03934d992fd39555a
Author: ignitetcbot <[email protected]>
AuthorDate: Fri May 8 02:06:33 2026 +0300

    IGNITE-28631: migrate TC Bot to Java 17, Gradle 9.5, Ignite 2.18 and Jetty 
12(#208)
    
    Fix WAR/static resource serving, logging/runtime issues, login redirects 
and launcher compatibility after migration.
    Co-authored-by: Dmitriy Pavlov <[email protected]>
---
 .gitignore                                         |   2 +
 README.md                                          |  65 +++------
 build.gradle                                       |  68 +++++----
 docs/install.md                                    |  97 +++++++++++++
 gradle/wrapper/gradle-wrapper.properties           |   2 +-
 ignite-tc-helper-web/build.gradle                  |  56 ++++----
 .../org/apache/ignite/ci/tcbot/ITcBotBgAuth.java   |   6 +-
 .../ignite/ci/tcbot/TcBotGeneralService.java       |   2 +
 .../java/org/apache/ignite/ci/web/Launcher.java    |  10 +-
 .../ignite/ci/web/StaticResourceServlet.java       | 106 ++++++++++++++
 .../org/apache/ignite/ci/web/model/Version.java    |   3 +
 .../src/main/webapp/WEB-INF/web.xml                |  22 +--
 .../src/main/webapp/js/common-1.6.js               |  25 +++-
 ignite-tc-helper-web/src/main/webapp/js/prs-1.2.js |   6 +-
 .../src/main/webapp/js/testfails-2.2.js            |  11 +-
 ignite-tc-helper-web/src/main/webapp/login.html    |   4 +-
 .../tcbot/engine/board/BoardServiceTest.java       | 156 ++++++++++++++-------
 jetty-launcher/build.gradle                        |  75 +++++-----
 jetty-launcher/src/main/resources/logback.xml      |  21 +++
 .../org/apache/ignite/migrate/Transformer.java     |   6 +-
 tcbot-common/build.gradle                          |  24 ++--
 .../common/conf/IDataSourcesConfigSupplier.java    |   6 +-
 .../ignite/tcbot/common/conf/IGitHubConfig.java    |   8 +-
 .../tcbot/common/conf/IJiraServerConfig.java       |  16 +--
 .../ignite/tcbot/common/conf/ITcServerConfig.java  |  18 +--
 .../interceptor/MonitoredTaskInterceptor.java      |   2 +-
 tcbot-engine/build.gradle                          |  14 +-
 .../ignite/tcbot/engine/conf/ITcBotConfig.java     |  28 ++--
 tcbot-github-ignited/build.gradle                  |   6 +-
 tcbot-github/build.gradle                          |  16 +--
 tcbot-jira-ignited/build.gradle                    |   6 +-
 tcbot-jira/build.gradle                            |  10 +-
 tcbot-notify/build.gradle                          |   6 +-
 tcbot-persistence/build.gradle                     |   8 +-
 tcbot-server-node/build.gradle                     |  51 +++----
 tcbot-teamcity-ignited/build.gradle                |  10 +-
 tcbot-teamcity/build.gradle                        |  10 +-
 37 files changed, 645 insertions(+), 337 deletions(-)

diff --git a/.gitignore b/.gitignore
index 5d5f5b7f..276b8f61 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,5 @@
 # virtual machine crash logs, see 
http://www.java.com/en/download/help/error_hotspot.xml
 hs_err_pid*
 /conf/branches-prom.json
+/ignite-tc-helper-web/ignite/**
+/ignite-tc-helper-web/src/test/tmp/**
\ No newline at end of file
diff --git a/README.md b/README.md
index db96155f..5ca5ed51 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,24 @@ Local code can be set up using IntelliJ IDEA and Gradle 
project import.
 For local development, run `org.apache.ignite.ci.web.Launcher.main()` from the 
project root.
 The launcher starts Jetty on `http://localhost:8080/` and serves static web 
resources directly from
 `ignite-tc-helper-web/src/main/webapp`.
+When running this main class directly from an IDE on Java 17, use the same 
module options as the
+`igniteJava17JvmArgs` Gradle property:
+
+```
+-XX:+IgnoreUnrecognizedVMOptions
+--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED
+--add-exports=java.base/sun.nio.ch=ALL-UNNAMED
+--add-exports=java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED
+--add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED
+--add-exports=java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED
+--add-opens=java.base/java.io=ALL-UNNAMED
+--add-opens=java.base/java.lang=ALL-UNNAMED
+--add-opens=java.base/java.nio=ALL-UNNAMED
+--add-opens=java.base/java.time=ALL-UNNAMED
+--add-opens=java.base/java.util=ALL-UNNAMED
+--add-opens=java.base/java.util.concurrent=ALL-UNNAMED
+--add-opens=java.base/sun.nio.ch=ALL-UNNAMED
+```
 
 The bot creates its working directory at `~/.ignite-teamcity-helper` by 
default. The directory contains
 runtime data and local configuration files. The location can be changed with 
the
@@ -54,50 +72,9 @@ Please install following components for development using 
IntelliJ IDEA
 * Apply [Code Inspection 
Profile](https://cwiki.apache.org/confluence/display/IGNITE/Coding+Guidelines#CodingGuidelines-C.CodeInspection)
 * Configure [IDEA 
Codestyle](https://cwiki.apache.org/confluence/display/IGNITE/Coding+Guidelines#CodingGuidelines-A.ConfigureIntelliJIDEAcodestyle)
 
-### Build
-A build can be done using following commands
-- gradle clean
-- gradle build
-
-It is recommended to use Java 11 for development.
-
-It may be required to install 
-[Java Cryptography Extension JCE Unlimited Strength Jurisdiction Policy Files 
8 
Download](https://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html)
-because the Bot uses strong AES cryptography, but default java distribution 
may limit AES256 usage.
-
-
-Resulting distribution can be found in 
projectRoot\jetty-launcher\build\distributions.
-Distribution will contain start script in \bin folder.
-
-### Running in production
-Production mode is started from the `jetty-launcher` distribution. Build the 
distribution first:
-
-```
-gradle clean build
-```
-
-Unpack the archive from `jetty-launcher/build/distributions` on the target 
host. The production launcher
-is `org.apache.ignite.ci.TcHelperJettyLauncher`; it starts the same web 
application from the packaged WAR.
-The generated start scripts set the default working directory to `../work` via
-`-Dteamcity.helper.home=../work`, so place production `branches.json` and 
other required configuration
-files into that `work` directory, or override `teamcity.helper.home` with the 
desired production path.
-
-When the bot is installed as a service, start or restart `tc-bot-service` 
after deploying a new build or
-changing configuration:
-
-```
-systemctl start tc-bot-service
-systemctl restart tc-bot-service
-systemctl status tc-bot-service
-```
-
-After `tc-bot-service` is up, open the production bot URL, log in with 
TeamCity credentials, and click
-`Authorize Server` in the top menu. This step is required for background 
operations that need TeamCity
-access, including background checks, queue checks, build triggering, JIRA 
notifications, and cleanup.
-
-Server authorization is not stored in `branches.json`; it is taken from the 
authenticated user session and
-kept by the running bot process. Re-authorize the server after each service 
restart, deployment, or process
-crash.
+### Build and installation
+Build, production installation, Linux service setup, and Windows 
production-check commands are documented in
+[Build and installation](docs/install.md).
 
 ### Internal Design
 Main bot logic is placed in [ignite-tc-helper-web](ignite-tc-helper-web) 
module. 
diff --git a/build.gradle b/build.gradle
index 86f017f2..bf8b7aee 100644
--- a/build.gradle
+++ b/build.gradle
@@ -15,15 +15,8 @@
  * limitations under the License.
  */
 
-buildscript {
-    repositories {
-        maven {
-            url "https://plugins.gradle.org/m2/";
-        }
-    }
-    dependencies {
-        classpath 
"gradle.plugin.nl.javadude.gradle.plugins:license-gradle-plugin:0.14.0"
-    }
+plugins {
+    id "com.github.hierynomus.license" version "0.16.1" apply false
 }
 
 subprojects {
@@ -38,43 +31,70 @@ subprojects {
 }
 
 allprojects {
-    apply plugin: 'java'
+    apply plugin: 'java-library'
 
     java {
         toolchain {
-            languageVersion = JavaLanguageVersion.of(11)
+            languageVersion = JavaLanguageVersion.of(17)
         }
     }
 
     ext {
 
-        jettyVer = '9.4.12.v20180830'
+        jettyVer = '12.1.8'
 
-        ignVer = '2.17.0'
+        ignVer = '2.18.0'
 
-        guavaVer = '26.0-jre'
+        guavaVer = '33.6.0-jre'
 
         //dual-licensed under the EPL v1.0 (Category B list.) and the LGPL 2.1 
(category X).
-        logbackVer = '1.2.3'
+        logbackVer = '1.5.32'
 
         // MIT/X11 license, category A;
-        slf4jVer = '1.7.25'
-
-        gsonVer = '2.8.2'
-
-        junitVer = '4.12'
-        mockitoVer = '2.22.0'
+        slf4jVer = '2.0.17'
+
+        gsonVer = '2.14.0'
+
+        junitVer = '4.13.2'
+        mockitoVer = '5.23.0'
+        jerseyVer = '2.48'
+        guiceVer = '5.1.0'
+        snappyVer = '1.1.10.8'
+        jaxbVer = '2.3.9'
+        mailVer = '1.6.2'
+        checkerQualVer = '4.0.0'
+        jsr305Ver = '3.0.2'
+
+        igniteJava17JvmArgs = [
+            "-XX:+IgnoreUnrecognizedVMOptions",
+            "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
+            "--add-exports=java.base/sun.nio.ch=ALL-UNNAMED",
+            
"--add-exports=java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED",
+            
"--add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED",
+            
"--add-exports=java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED",
+            "--add-opens=java.base/java.io=ALL-UNNAMED",
+            "--add-opens=java.base/java.lang=ALL-UNNAMED",
+            "--add-opens=java.base/java.nio=ALL-UNNAMED",
+            "--add-opens=java.base/java.time=ALL-UNNAMED",
+            "--add-opens=java.base/java.util=ALL-UNNAMED",
+            "--add-opens=java.base/java.util.concurrent=ALL-UNNAMED",
+            "--add-opens=java.base/sun.nio.ch=ALL-UNNAMED"
+        ]
     }
 
     repositories {
         //uncomment for RC testing
-        maven {
-            url 
"https://repository.apache.org/content/repositories/orgapacheignite-1552/";
-        }
+        //    maven {
+        //      url 
"https://repository.apache.org/content/repositories/orgapacheignite-1552/";
+        //    }
 
         mavenCentral()
         mavenLocal()
     }
+
+    tasks.withType(JavaExec).configureEach {
+        jvmArgs igniteJava17JvmArgs
+    }
 }
 
 /*
diff --git a/docs/install.md b/docs/install.md
new file mode 100644
index 00000000..690a2f3a
--- /dev/null
+++ b/docs/install.md
@@ -0,0 +1,97 @@
+# Build and installation
+
+Use Java 17. Build everything through the Gradle wrapper:
+
+```
+./gradlew clean build --no-daemon
+./gradlew :jetty-launcher:clean :jetty-launcher:distZip --no-daemon
+./gradlew :tcbot-server-node:clean :tcbot-server-node:distZip --no-daemon
+```
+
+On Windows use the same commands with `gradlew.bat`:
+
+```
+gradlew.bat clean build --no-daemon
+gradlew.bat :jetty-launcher:clean :jetty-launcher:distZip --no-daemon
+gradlew.bat :tcbot-server-node:clean :tcbot-server-node:distZip --no-daemon
+```
+
+The web distribution is 
`jetty-launcher/build/distributions/jetty-launcher.zip`. It contains `bin`, 
`lib`, and
+`war/ignite-tc-helper-web.war`.
+
+## Linux service
+
+Unpack the distribution and put production config into `work`:
+
+```
+sudo mkdir -p /opt/ignite-teamcity-bot/releases /opt/ignite-teamcity-bot/work
+sudo unzip -oq jetty-launcher.zip -d /opt/ignite-teamcity-bot/releases
+sudo ln -sfn /opt/ignite-teamcity-bot/releases/jetty-launcher 
/opt/ignite-teamcity-bot/current
+sudo cp branches.json /opt/ignite-teamcity-bot/work/branches.json
+```
+
+`/etc/systemd/system/tc-bot-service.service`:
+
+```
+[Unit]
+Description=Ignite TeamCity Bot
+After=network-online.target
+
+[Service]
+Type=simple
+User=tc-bot
+Group=tc-bot
+WorkingDirectory=/opt/ignite-teamcity-bot/current/bin
+Environment="JAVA_HOME=/usr/lib/jvm/java-17"
+Environment="JETTY_LAUNCHER_OPTS=-Dteamcity.helper.home=/opt/ignite-teamcity-bot/work"
+ExecStart=/opt/ignite-teamcity-bot/current/bin/jetty-launcher
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
+```
+
+Start it:
+
+```
+sudo systemctl daemon-reload
+sudo systemctl enable tc-bot-service
+sudo systemctl restart tc-bot-service
+sudo systemctl status tc-bot-service
+```
+
+Use generated `bin/jetty-launcher`; it already contains the Java 17 module 
options required by Ignite and Guice.
+
+## Windows production check
+
+Use a separate clean checkout. Set `PR_REF` only for PR checks:
+
+```
+set "REPO=C:\Tmp\ignite-teamcity-bot-check"
+set "DIST=C:\Tmp\tc-bot-prod-check"
+set "PR_REF="
+rem set "PR_REF=pull/200/head"
+
+if not exist "%REPO%\.git" git clone 
https://github.com/apache/ignite-teamcity-bot.git "%REPO%" || exit /b 1
+cd /d "%REPO%" || exit /b 1
+git fetch origin master || exit /b 1
+git switch master || exit /b 1
+git reset --hard origin/master || exit /b 1
+git clean -fdx || exit /b 1
+
+if not "%PR_REF%"=="" (
+    git branch -D pr-check 2>NUL
+    git fetch origin %PR_REF%:refs/heads/pr-check || exit /b 1
+    git switch pr-check || exit /b 1
+)
+
+call gradlew.bat clean build --no-daemon || exit /b 1
+call gradlew.bat :jetty-launcher:clean :jetty-launcher:distZip --no-daemon || 
exit /b 1
+
+powershell -NoProfile -ExecutionPolicy Bypass -Command "Remove-Item 
-LiteralPath '%DIST%' -Recurse -Force -ErrorAction SilentlyContinue; 
Expand-Archive -LiteralPath 
'jetty-launcher\build\distributions\jetty-launcher.zip' -DestinationPath 
'%DIST%' -Force" || exit /b 1
+powershell -NoProfile -ExecutionPolicy Bypass -Command "New-Item -ItemType 
Directory -Force -Path '%DIST%\jetty-launcher\work' | Out-Null" || exit /b 1
+copy /Y "conf\branches.json" "%DIST%\jetty-launcher\work\branches.json" || 
exit /b 1
+
+cd /d "%DIST%\jetty-launcher\bin" || exit /b 1
+call jetty-launcher.bat
+```
diff --git a/gradle/wrapper/gradle-wrapper.properties 
b/gradle/wrapper/gradle-wrapper.properties
index 37fc91e3..167dcad1 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
diff --git a/ignite-tc-helper-web/build.gradle 
b/ignite-tc-helper-web/build.gradle
index 90109273..5a0fd934 100644
--- a/ignite-tc-helper-web/build.gradle
+++ b/ignite-tc-helper-web/build.gradle
@@ -20,37 +20,34 @@ apply plugin: 'war'
 
 // https://www.apache.org/legal/resolved.html#category-a
 dependencies {
-    compile (project(":tcbot-engine"));
+    implementation project(":tcbot-engine")
     implementation project(':migrator')
 
-    compile group: 'org.apache.ignite', name: 'ignite-core', version: ignVer
-    compile group: 'org.apache.ignite', name: 'ignite-slf4j', version: ignVer
-    compile group: 'org.apache.ignite', name: 'ignite-direct-io', version: 
ignVer
+    implementation group: 'org.apache.ignite', name: 'ignite-core', version: 
ignVer
+    implementation group: 'org.apache.ignite', name: 'ignite-slf4j', version: 
ignVer
+    implementation group: 'org.apache.ignite', name: 'ignite-direct-io', 
version: ignVer
 
-    compile group: 'org.slf4j', name: 'slf4j-api', version: slf4jVer
-    compile group: 'org.slf4j', name: 'jul-to-slf4j', version: slf4jVer
+    implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4jVer
+    implementation group: 'org.slf4j', name: 'jul-to-slf4j', version: slf4jVer
 
-    compile group: 'ch.qos.logback', name: 'logback-core', version: logbackVer
-    compile group: 'ch.qos.logback', name: 'logback-classic', version: 
logbackVer
+    implementation group: 'ch.qos.logback', name: 'logback-core', version: 
logbackVer
+    implementation group: 'ch.qos.logback', name: 'logback-classic', version: 
logbackVer
 
-    compile group: 'org.eclipse.jetty', name: 'jetty-util', version: jettyVer
-    compile group: 'org.eclipse.jetty', name: 'jetty-server', version: jettyVer
-    compile group: 'org.eclipse.jetty', name: 'jetty-webapp', version: jettyVer
+    implementation group: 'org.eclipse.jetty', name: 'jetty-util', version: 
jettyVer
+    implementation group: 'org.eclipse.jetty', name: 'jetty-server', version: 
jettyVer
+    implementation group: 'org.eclipse.jetty.ee8', name: 'jetty-ee8-webapp', 
version: jettyVer
 
-    // problematic    def jerseyVer = '2.27'
-    // Newer version is  def jerseyVer = '2.28';  Current:    def jerseyVer = 
'2.25.1'
-    def jerseyVer = '2.28'
-    compile group: 'org.glassfish.jersey.containers', name: 
'jersey-container-servlet-core', version: jerseyVer
+    implementation group: 'org.glassfish.jersey.containers', name: 
'jersey-container-servlet-core', version: jerseyVer
 
-    compile group: 'org.glassfish.jersey.core', name: 'jersey-client', 
version: jerseyVer
+    implementation group: 'org.glassfish.jersey.core', name: 'jersey-client', 
version: jerseyVer
     //
-    compile group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: 
 jerseyVer
+    implementation group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', 
version:  jerseyVer
 
-    compile group: 'org.glassfish.jersey.media', name: 
'jersey-media-json-jackson', version: jerseyVer
-    compile group: 'org.glassfish.jersey.media', name: 
'jersey-media-multipart', version: jerseyVer
+    implementation group: 'org.glassfish.jersey.media', name: 
'jersey-media-json-jackson', version: jerseyVer
+    implementation group: 'org.glassfish.jersey.media', name: 
'jersey-media-multipart', version: jerseyVer
 
-    testCompile group: 'junit', name: 'junit', version: junitVer
-    testCompile group: 'org.mockito', name: 'mockito-core', version: mockitoVer
+    testImplementation group: 'junit', name: 'junit', version: junitVer
+    testImplementation group: 'org.mockito', name: 'mockito-core', version: 
mockitoVer
 }
 
 processResources {
@@ -59,13 +56,14 @@ processResources {
     }
 }
 
+war {
+    into('WEB-INF/classes/static') {
+        from 'src/main/webapp'
+        exclude 'WEB-INF/**'
+    }
+}
+
 test {
     // set JVM arguments for the test JVM(s)
-    jvmArgs "-XX:+IgnoreUnrecognizedVMOptions",
-        "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
-        "--add-exports=java.base/sun.nio.ch=ALL-UNNAMED",
-        "--add-exports=java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED",
-        "--add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED",
-        
"--add-exports=java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED",
-        "--illegal-access=permit"
-}
\ No newline at end of file
+    jvmArgs igniteJava17JvmArgs
+}
diff --git 
a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/ITcBotBgAuth.java
 
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/ITcBotBgAuth.java
index 66bc42a3..cc0a7958 100644
--- 
a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/ITcBotBgAuth.java
+++ 
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/ITcBotBgAuth.java
@@ -24,13 +24,13 @@ import org.jetbrains.annotations.Nullable;
  */
 public interface ITcBotBgAuth {
     /** */
-    public void setServerAuthorizerCreds(ITcBotUserCreds creds);
+    void setServerAuthorizerCreds(ITcBotUserCreds creds);
 
     /** */
-    @Nullable public ITcBotUserCreds getServerAuthorizerCreds();
+    @Nullable ITcBotUserCreds getServerAuthorizerCreds();
 
     /** */
-    public default boolean isServerAuthorized() {
+    default boolean isServerAuthorized() {
         return getServerAuthorizerCreds() != null;
     }
 }
diff --git 
a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotGeneralService.java
 
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotGeneralService.java
index ba9c9675..9955d8a0 100644
--- 
a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotGeneralService.java
+++ 
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotGeneralService.java
@@ -21,6 +21,7 @@ import javax.inject.Provider;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.ci.web.model.Version;
 import org.apache.ignite.lang.IgniteProductVersion;
+import org.eclipse.jetty.server.Server;
 
 /**
  * Service for general requests processing, which are not related to 
builds/JIRA/GitHub.
@@ -34,6 +35,7 @@ public class TcBotGeneralService {
      */
     public Version version() {
         Version ver = new Version();
+        ver.serverVer = Server.getVersion();
 
         try {
             IgniteProductVersion ignProdVer = igniteProvider.get().version();
diff --git 
a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/Launcher.java 
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/Launcher.java
index c9c00b47..bd86fcfd 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/Launcher.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/Launcher.java
@@ -26,7 +26,7 @@ import org.apache.ignite.ci.db.TcHelperDb;
 import org.apache.ignite.tcbot.common.conf.TcBotSystemProperties;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.ee8.webapp.WebAppContext;
 
 /**
  * Launcher for server note and web application: all in one.
@@ -61,7 +61,7 @@ public class Launcher {
             File webResDir = new File(webApp);
             Preconditions.checkState(webResDir.exists(),
                 "Resource directory [" + webResDir.getAbsolutePath() + "] does 
not exist");
-            ctx.setDescriptor(ctx + "/WEB-INF/web.xml");
+            ctx.setDescriptor(webApp+"/WEB-INF/web.xml");
             ctx.setResourceBase(webResDir.getAbsolutePath());
             ctx.setContextPath("/");
             ctx.setParentLoaderPriority(true);
@@ -70,8 +70,10 @@ public class Launcher {
             ctx.setContextPath("/");
             String war = "../war/ignite-tc-helper-web.war";
             File file = new File(war);
-            Preconditions.checkState(file.exists(), "War file can not be found 
[" + file.getCanonicalPath() + "]");
-            ctx.setWar(war);
+            File warFile = file.getCanonicalFile();
+            Preconditions.checkState(warFile.exists(), "War file can not be 
found [" + warFile + "]");
+
+            ctx.setWar(warFile.toURI().toString());
         }
         srv.setHandler(ctx);
 
diff --git 
a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/StaticResourceServlet.java
 
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/StaticResourceServlet.java
new file mode 100644
index 00000000..5a373881
--- /dev/null
+++ 
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/StaticResourceServlet.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.ci.web;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Serves static UI resources from the webapp classpath.
+ */
+public class StaticResourceServlet extends HttpServlet {
+    /** Static resources classpath root. */
+    private static final String STATIC_ROOT = "static/";
+
+    private static String resourcePath(HttpServletRequest req) {
+        String ctx = req.getContextPath();
+        String uri = req.getRequestURI();
+
+        String path = uri;
+
+        if (ctx != null && !ctx.isEmpty() && uri.startsWith(ctx))
+            path = uri.substring(ctx.length());
+
+        if (path.startsWith("/"))
+            path = path.substring(1);
+
+        path = URLDecoder.decode(path, StandardCharsets.UTF_8);
+
+        if (path.contains("..") || path.contains("\\") || path.startsWith("/"))
+            return path;
+
+        String normalized = URI.create("/" + path).normalize().getPath();
+
+        if (normalized.startsWith("/"))
+            normalized = normalized.substring(1);
+
+        return normalized;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void doGet(HttpServletRequest req, HttpServletResponse 
resp)
+        throws ServletException, IOException {
+        String path;
+
+        try {
+            path = resourcePath(req);
+        }
+        catch (IllegalArgumentException e) {
+            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
+            return;
+        }
+
+        if (path.isEmpty())
+            path = "index.html";
+
+        if (path.contains("..") || path.contains("\\") || 
path.startsWith("/")) {
+            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
+            return;
+        }
+
+        if (path.endsWith("/"))
+            path += "index.html";
+
+        String resPath = STATIC_ROOT + path;
+
+        try (InputStream in = 
Thread.currentThread().getContextClassLoader().getResourceAsStream(resPath)) {
+            if (in == null) {
+                resp.sendError(HttpServletResponse.SC_NOT_FOUND);
+                return;
+            }
+
+            String mimeType = getServletContext().getMimeType(path);
+
+            if (mimeType == null)
+                mimeType = URLConnection.guessContentTypeFromName(path);
+
+            if (mimeType != null)
+                resp.setContentType(mimeType);
+
+            in.transferTo(resp.getOutputStream());
+        }
+    }
+}
diff --git 
a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java
 
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java
index 36f21dac..f9524001 100644
--- 
a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java
+++ 
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java
@@ -33,6 +33,9 @@ package org.apache.ignite.ci.web.model;
     /** Java version, where Web App is running. */
     public String javaVer;
 
+    /** Server version. */
+    public String serverVer;
+
     /** TC Bot Version. */
     public String version = VERSION;
 
diff --git a/ignite-tc-helper-web/src/main/webapp/WEB-INF/web.xml 
b/ignite-tc-helper-web/src/main/webapp/WEB-INF/web.xml
index 77fdbafe..0cc119f5 100644
--- a/ignite-tc-helper-web/src/main/webapp/WEB-INF/web.xml
+++ b/ignite-tc-helper-web/src/main/webapp/WEB-INF/web.xml
@@ -1,8 +1,8 @@
-<web-app id="TcHelper" version="2.4"
-         xmlns="http://java.sun.com/xml/ns/j2ee";
+<web-app id="TcHelper" version="4.0"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee";
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
-         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
-       http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd";>
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
+                             
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd";>
     <display-name>Restful Web Application</display-name>
 
     <listener>
@@ -29,10 +29,12 @@
     </servlet-mapping>
 
     <servlet>
-        <servlet-name>default</servlet-name>
-        <init-param>
-            <param-name>useFileMappedBuffer</param-name>
-            <param-value>false</param-value>
-        </init-param>
+        <servlet-name>static-resources</servlet-name>
+        
<servlet-class>org.apache.ignite.ci.web.StaticResourceServlet</servlet-class>
     </servlet>
-</web-app>
\ No newline at end of file
+
+    <servlet-mapping>
+        <servlet-name>static-resources</servlet-name>
+        <url-pattern>/</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/ignite-tc-helper-web/src/main/webapp/js/common-1.6.js 
b/ignite-tc-helper-web/src/main/webapp/js/common-1.6.js
index c56f2413..6fed9465 100644
--- a/ignite-tc-helper-web/src/main/webapp/js/common-1.6.js
+++ b/ignite-tc-helper-web/src/main/webapp/js/common-1.6.js
@@ -58,6 +58,22 @@ function rgbToHex(r, g, b) {
     return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
 }
 
+function isLoginUrl(url) {
+    try {
+        return new URL(url, window.location.origin).pathname === "/login.html";
+    }
+    catch (e) {
+        return false;
+    }
+}
+
+function currentBackref() {
+    if (isLoginUrl(window.location.href))
+        return "/";
+
+    return window.location.href;
+}
+
 //requires element on page: <div id="loadStatus"></div>
 function showErrInLoadStatus(jqXHR, exception) {
     if (jqXHR.status === 0) {
@@ -67,8 +83,11 @@ function showErrInLoadStatus(jqXHR, exception) {
     } else if (jqXHR.status === 401) {
         $("#loadStatus").html('Unauthorized [401]');
 
+        if (window.location.pathname === "/login.html")
+            return;
+
         setTimeout(function() {
-            window.location.href = "/login.html" + "?backref=" + 
encodeURIComponent(window.location.href);
+            window.location.href = "/login.html?backref=" + 
encodeURIComponent(currentBackref());
         }, 1000);
     } else if (jqXHR.status === 403) {
         $("#loadStatus").html('Forbidden [403]');
@@ -115,6 +134,10 @@ function showVersionInfo(result) {
         res += ", Java Version: " + result.javaVer;
     }
 
+    if (isDefinedAndFilled(result.serverVer)) {
+        res += ", Server Version: " + result.serverVer;
+    }
+
     res += "<br>";
     res += "Managed by the <a 
href='https://ignite.apache.org/our-community.html'>Apache Ignite Development 
Project.</a>";
 
diff --git a/ignite-tc-helper-web/src/main/webapp/js/prs-1.2.js 
b/ignite-tc-helper-web/src/main/webapp/js/prs-1.2.js
index 8c0b59b1..831af18f 100644
--- a/ignite-tc-helper-web/src/main/webapp/js/prs-1.2.js
+++ b/ignite-tc-helper-web/src/main/webapp/js/prs-1.2.js
@@ -262,7 +262,6 @@ function formatContributionDetails(row, srvId) {
         "                <td>Build Queued</td>\n" +
         "                <td>Results ready</td>\n" +
         "                <td>JIRA comment</td>\n" +
-        //todo  "                <td>Validity check</td>\n" +
         "            </tr>\n";
 
     //icon of stage
@@ -271,7 +270,6 @@ function formatContributionDetails(row, srvId) {
         "                <th title='Suite should be triggered'><span 
class='visaStage' id='visaStage_2_" + prId + "'></span></th>\n" +
         "                <th><span class='visaStage' id='visaStage_3_" + prId 
+ "'></span></th>\n" +
          "               <th><span class='visaStage' id='visaStage_4_" + prId 
+ "'></span></th>\n" +
-        //todo validityCheck;"                <th><span class='visaStage' 
id='visaStage_5_" + prId + "'></span></th>\n" +
         "            </tr>\n";
 
     //action for stage
@@ -322,7 +320,7 @@ function formatContributionDetails(row, srvId) {
                 for (let status of result) {
                     suites.set(status.suiteId, status);
 
-                    if (isDefinedAndFilled(status.defaultBuildType) && 
status.defaultBuildType === true)
+                    if (status.defaultBuildType === true)
                         isDefault.push(status);
                     else if 
(isDefinedAndFilled(status.branchWithFinishedSuite))
                         isCompleted.push(status);
@@ -536,4 +534,4 @@ function showContributionStatus(status, prId, row, srvId, 
suiteIdSelected) {
                 }
         });
     }
-}
\ No newline at end of file
+}
diff --git a/ignite-tc-helper-web/src/main/webapp/js/testfails-2.2.js 
b/ignite-tc-helper-web/src/main/webapp/js/testfails-2.2.js
index dde6aa9e..8d7b6042 100644
--- a/ignite-tc-helper-web/src/main/webapp/js/testfails-2.2.js
+++ b/ignite-tc-helper-web/src/main/webapp/js/testfails-2.2.js
@@ -24,14 +24,11 @@ var g_srv_to_notify_git;
 
 //@param results - TestFailuresSummary
 function showChainOnServersResults(result) {
-    var minFailRateP = findGetParameter("minFailRate");
-    var minFailRate = minFailRateP == null ? 0 : parseFloat(minFailRateP);
+    var minFailRate = parseFloat(findGetParameter("minFailRate") || 0);
 
-    var maxFailRateP = findGetParameter("maxFailRate");
-    var maxFailRate = maxFailRateP == null ? 100 : parseFloat(maxFailRateP);
+    var maxFailRate = parseFloat(findGetParameter("maxFailRate") || 100);
 
-    var hideFlakyFailuresP = findGetParameter("hideFlakyFailures");
-    var hideFlakyFailures = hideFlakyFailuresP == null ? false : 
"true"===hideFlakyFailuresP;
+    var hideFlakyFailures = findGetParameter("hideFlakyFailures") === "true";
 
     return showChainResultsWithSettings(result, new Settings(minFailRate, 
maxFailRate, result.javaFlags, hideFlakyFailures));
 }
@@ -1101,4 +1098,4 @@ function drawLatestRunsBlock(state, len) {
         runColor = "#F09F00";
 
     return "<span style='background-color: " + runColor + ";  width:" + (len * 
1) + "px; height:10px; display: inline-block;'></span>";
-}
\ No newline at end of file
+}
diff --git a/ignite-tc-helper-web/src/main/webapp/login.html 
b/ignite-tc-helper-web/src/main/webapp/login.html
index 89c4bbd0..765ff835 100644
--- a/ignite-tc-helper-web/src/main/webapp/login.html
+++ b/ignite-tc-helper-web/src/main/webapp/login.html
@@ -91,7 +91,7 @@
               window.localStorage.setItem("token", data.fullToken);
          } catch (e) {}
 
-        if (isDefinedAndFilled(backref)) {
+        if (isDefinedAndFilled(backref) && !isLoginUrl(backref)) {
              window.location.href = backref;
          } else {
              window.location.href = "/";
@@ -127,4 +127,4 @@
 <div id="version"></div>
 
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git 
a/ignite-tc-helper-web/src/test/java/org/apache/ignite/tcbot/engine/board/BoardServiceTest.java
 
b/ignite-tc-helper-web/src/test/java/org/apache/ignite/tcbot/engine/board/BoardServiceTest.java
index d9cfa67e..2aec3e41 100644
--- 
a/ignite-tc-helper-web/src/test/java/org/apache/ignite/tcbot/engine/board/BoardServiceTest.java
+++ 
b/ignite-tc-helper-web/src/test/java/org/apache/ignite/tcbot/engine/board/BoardServiceTest.java
@@ -21,11 +21,12 @@ import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.internal.SingletonScope;
+import java.lang.reflect.Field;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.Ignition;
 import org.apache.ignite.ci.db.TcHelperDb;
@@ -52,6 +53,7 @@ import 
org.apache.ignite.tcignited.buildlog.BuildLogCheckResultDao;
 import org.apache.ignite.tcignited.buildref.BuildRefDao;
 import org.apache.ignite.tcignited.history.BuildStartTimeStorage;
 import org.apache.ignite.tcignited.history.SuiteInvocationHistoryDao;
+import org.apache.ignite.tcservice.model.vcs.Revision;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
@@ -79,8 +81,6 @@ public class BoardServiceTest {
     private static ChangeDao changeDao;
     private static BoardService boardService;
 
-    private static AtomicInteger defectCounter = new AtomicInteger();
-
     private final static String tc1 = "apache";
     private final static String tc2 = "private";
     private final static int tcId1 = Math.abs(tc1.hashCode());
@@ -116,10 +116,10 @@ public class BoardServiceTest {
     private static ChangeCompacted change4 = createChange(commit4);
 
     //revision
-    private static RevisionCompacted revision1 = createRevision(commit1);
-    private static RevisionCompacted revision2 = createRevision(commit2);
-    private static RevisionCompacted revision3 = createRevision(commit3);
-    private static RevisionCompacted revision4 = createRevision(commit4);
+    private static RevisionCompacted revision1;
+    private static RevisionCompacted revision2;
+    private static RevisionCompacted revision3;
+    private static RevisionCompacted revision4;
 
     //changes
     private static Map<Integer, ChangeCompacted> emptyBuildChanges = new 
HashMap<>();
@@ -130,37 +130,16 @@ public class BoardServiceTest {
     private static List<RevisionCompacted> buildRevisions2 = new ArrayList<>();
     private static List<RevisionCompacted> buildRevisions3 = new ArrayList<>();
 
-    private static FatBuildCompacted fatBuildCompacted1 = 
mock(FatBuildCompacted.class);
-    private static FatBuildCompacted fatBuildCompacted2 = 
mock(FatBuildCompacted.class);
-    private static FatBuildCompacted fatBuildCompacted3 = 
mock(FatBuildCompacted.class);
+    private static FatBuildCompacted fatBuildCompacted1;
+    private static FatBuildCompacted fatBuildCompacted2;
+    private static FatBuildCompacted fatBuildCompacted3;
     //fatBuildCompacted4 with the same revisions as the fatBuildCompacted1
-    private static FatBuildCompacted fatBuildCompacted4 = 
mock(FatBuildCompacted.class);
+    private static FatBuildCompacted fatBuildCompacted4;
 
     static {
         buildChanges1.put(1, change1);
 
         buildChanges1.put(2, change2);
-
-        buildRevisions1.add(revision1);
-        buildRevisions1.add(revision2);
-
-        buildRevisions2.add(revision3);
-        buildRevisions2.add(revision4);
-
-        buildRevisions3.add(revision3);
-        buildRevisions3.add(revision4);
-
-        when(fatBuildCompacted1.revisions()).thenReturn(buildRevisions1);
-        when(fatBuildCompacted1.id()).thenReturn(1);
-
-        when(fatBuildCompacted2.revisions()).thenReturn(buildRevisions2);
-        when(fatBuildCompacted2.id()).thenReturn(2);
-
-        when(fatBuildCompacted3.revisions()).thenReturn(buildRevisions3);
-        when(fatBuildCompacted3.id()).thenReturn(3);
-
-        when(fatBuildCompacted4.revisions()).thenReturn(buildRevisions1);
-        when(fatBuildCompacted4.id()).thenReturn(4);
     }
 
     @BeforeClass
@@ -187,6 +166,24 @@ public class BoardServiceTest {
         changeDao = injector.getInstance(ChangeDao.class);
         boardService = injector.getInstance(BoardService.class);
         IStringCompactor compactor = 
injector.getInstance(IStringCompactor.class);
+        revision1 = createRevision(compactor, commit1);
+        revision2 = createRevision(compactor, commit2);
+        revision3 = createRevision(compactor, commit3);
+        revision4 = createRevision(compactor, commit4);
+
+        buildRevisions1.add(revision1);
+        buildRevisions1.add(revision2);
+
+        buildRevisions2.add(revision3);
+        buildRevisions2.add(revision4);
+
+        buildRevisions3.add(revision3);
+        buildRevisions3.add(revision4);
+
+        fatBuildCompacted1 = createFatBuild(1, buildRevisions1);
+        fatBuildCompacted2 = createFatBuild(2, buildRevisions2);
+        fatBuildCompacted3 = createFatBuild(3, buildRevisions3);
+        fatBuildCompacted4 = createFatBuild(4, buildRevisions1);
 
         fatBuildDao.init();
         injector.getInstance(SuiteInvocationHistoryDao.class).init();
@@ -231,8 +228,8 @@ public class BoardServiceTest {
             defectsStorage.save(defect0);
         });
 
-        issuesStorage.removeOldIssues(System.currentTimeMillis(), 
Integer.MAX_VALUE);
-        defectsStorage.removeOldDefects(System.currentTimeMillis(), 
Integer.MAX_VALUE);
+        issuesStorage.removeOldIssues(Long.MAX_VALUE, Integer.MAX_VALUE);
+        defectsStorage.removeOldDefects(Long.MAX_VALUE, Integer.MAX_VALUE);
     }
 
     private static ChangeCompacted createChange(byte[] commit) {
@@ -241,10 +238,42 @@ public class BoardServiceTest {
         return change;
     }
 
-    private static RevisionCompacted createRevision(byte[] commit) {
-        RevisionCompacted revision = mock(RevisionCompacted.class);
-        when(revision.revision()).thenReturn(commit);
-        return revision;
+    private static RevisionCompacted createRevision(IStringCompactor 
compactor, byte[] commit) {
+        Revision revision = new Revision()
+            .version(hex(commit))
+            .vcsBranchName(branch1);
+
+        return new RevisionCompacted(compactor, revision);
+    }
+
+    private static FatBuildCompacted createFatBuild(int id, 
List<RevisionCompacted> revisions) {
+        FatBuildCompacted build = new FatBuildCompacted();
+
+        build.setId(id);
+        setField(build, "revisions", revisions.toArray(new 
RevisionCompacted[0]));
+
+        return build;
+    }
+
+    private static void setField(Object target, String fieldName, Object 
value) {
+        try {
+            Field field = target.getClass().getDeclaredField(fieldName);
+
+            field.setAccessible(true);
+            field.set(target, value);
+        }
+        catch (ReflectiveOperationException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private static String hex(byte[] bytes) {
+        StringBuilder res = new StringBuilder(bytes.length * 2);
+
+        for (byte b : bytes)
+            res.append(String.format("%02x", b & 0xff));
+
+        return res.toString();
     }
 
     /**
@@ -256,15 +285,15 @@ public class BoardServiceTest {
 
         boardService.issuesToDefects();
 
-        DefectCompacted defect = 
defectsStorage.load(defectCounter.incrementAndGet());
+        DefectCompacted defect = singleDefect();
 
         assertEquals(1, defect.buildsInvolved().size());
-        assertEquals((int)issue1.issueKey().getBuildId(), 
defect.buildsInvolved().get(issue1.issueKey().getBuildId()).build().id());
+        assertEquals((int)issue1.issueKey().getBuildId(), 
defect.buildsInvolved().get((int)issue1.issueKey().getBuildId()).build().id());
         assertEquals(1, defectsStorage.loadAllDefects().size());
 
         boardService.issuesToDefects();
 
-        defect = defectsStorage.load(defectCounter.get());
+        defect = singleDefect();
 
         assertEquals(1, defect.buildsInvolved().size());
         assertEquals((int)issue1.issueKey().buildId, 
defect.buildsInvolved().get((int)issue1.issueKey().buildId).build().id());
@@ -282,10 +311,10 @@ public class BoardServiceTest {
 
         boardService.issuesToDefects();
 
-        DefectCompacted defect = 
defectsStorage.load(defectCounter.incrementAndGet());
+        DefectCompacted defect = singleDefect();
 
         assertEquals(1, defect.buildsInvolved().size());
-        assertEquals((int)issue2.issueKey().buildId, 
defect.buildsInvolved().get(issue2.issueKey().buildId).build().id());
+        assertEquals((int)issue2.issueKey().buildId, 
defect.buildsInvolved().get((int)issue2.issueKey().buildId).build().id());
         assertEquals(1, defectsStorage.loadAllDefects().size());
 
     }
@@ -300,11 +329,11 @@ public class BoardServiceTest {
 
         boardService.issuesToDefects();
 
-        DefectCompacted defect = 
defectsStorage.load(defectCounter.incrementAndGet());
+        DefectCompacted defect = singleDefect();
 
         assertEquals(2, defect.buildsInvolved().size());
-        assertEquals((int)issue2.issueKey().buildId, 
defect.buildsInvolved().get(issue2.issueKey().buildId).build().id());
-        assertEquals((int)issue3.issueKey().buildId, 
defect.buildsInvolved().get(issue3.issueKey().buildId).build().id());
+        assertEquals((int)issue2.issueKey().buildId, 
defect.buildsInvolved().get((int)issue2.issueKey().buildId).build().id());
+        assertEquals((int)issue3.issueKey().buildId, 
defect.buildsInvolved().get((int)issue3.issueKey().buildId).build().id());
         assertEquals(1, defectsStorage.loadAllDefects().size());
 
     }
@@ -324,11 +353,11 @@ public class BoardServiceTest {
 
         boardService.issuesToDefects();
 
-        DefectCompacted defect = 
defectsStorage.load(defectCounter.incrementAndGet());
+        DefectCompacted defect = singleDefect();
 
         assertEquals(2, defect.buildsInvolved().size());
-        assertEquals((int)issue4.issueKey().buildId, 
defect.buildsInvolved().get(issue4.issueKey().buildId).build().id());
-        assertEquals((int)issue5.issueKey().buildId, 
defect.buildsInvolved().get(issue5.issueKey().buildId).build().id());
+        assertEquals((int)issue4.issueKey().buildId, 
defect.buildsInvolved().get((int)issue4.issueKey().buildId).build().id());
+        assertEquals((int)issue5.issueKey().buildId, 
defect.buildsInvolved().get((int)issue5.issueKey().buildId).build().id());
         assertEquals(1, defectsStorage.loadAllDefects().size());
     }
 
@@ -347,16 +376,35 @@ public class BoardServiceTest {
 
         boardService.issuesToDefects();
 
-        DefectCompacted defect1 = 
defectsStorage.load(defectCounter.incrementAndGet());
-        DefectCompacted defect2 = 
defectsStorage.load(defectCounter.incrementAndGet());
+        Collection<DefectCompacted> defects = defectsStorage.loadAllDefects();
 
-        assertEquals(2, defectsStorage.loadAllDefects().size());
+        assertEquals(2, defects.size());
+
+        DefectCompacted defect1 = defectForBuild(defects, 
issue1.issueKey().buildId);
+        DefectCompacted defect2 = defectForBuild(defects, 
issue4.issueKey().buildId);
 
         assertEquals(1, defect1.buildsInvolved().size());
-        assertEquals((int)issue1.issueKey().buildId, 
defect1.buildsInvolved().get(issue1.issueKey().buildId).build().id());
+        assertEquals((int)issue1.issueKey().buildId, 
defect1.buildsInvolved().get((int)issue1.issueKey().buildId).build().id());
 
         assertEquals(1, defect2.buildsInvolved().size());
-        assertEquals((int)issue4.issueKey().buildId, 
defect2.buildsInvolved().get(issue4.issueKey().buildId).build().id());
+        assertEquals((int)issue4.issueKey().buildId, 
defect2.buildsInvolved().get((int)issue4.issueKey().buildId).build().id());
+    }
+
+    private DefectCompacted singleDefect() {
+        Collection<DefectCompacted> defects = defectsStorage.loadAllDefects();
+
+        assertEquals(1, defects.size());
+
+        return defects.iterator().next();
+    }
+
+    private DefectCompacted defectForBuild(Collection<DefectCompacted> 
defects, long buildId) {
+        for (DefectCompacted defect : defects) {
+            if (defect.buildsInvolved().containsKey((int)buildId))
+                return defect;
+        }
+
+        throw new AssertionError("Defect for build " + buildId + " was not 
found");
     }
 
     private static class IgniteTestModule extends AbstractModule {
diff --git a/jetty-launcher/build.gradle b/jetty-launcher/build.gradle
index 5df3d7b2..e1fd2a64 100644
--- a/jetty-launcher/build.gradle
+++ b/jetty-launcher/build.gradle
@@ -18,52 +18,61 @@
 apply plugin: 'java'
 apply plugin: 'application'
 
-mainClassName = 'org.apache.ignite.ci.TcHelperJettyLauncher'
-applicationDefaultJvmArgs = ["-Dteamcity.helper.home=../work",
-                             "-Dteamcity.bot.regionsize=16", // 16g Durable 
Memory region
-                             "-Dhttp.maxConnections=30",
-                             "-server",
-                             "-Xmx16g",
-                             "-XX:+AlwaysPreTouch",
-                             "-XX:+UseG1GC",
-                             "-XX:+ScavengeBeforeFullGC",
-                             "-XX:+UseStringDeduplication",
-                             "-Djava.rmi.server.hostname=app02",
-                             "-Dcom.sun.management.jmxremote",
-                             "-Dcom.sun.management.jmxremote.port=9010",
-                             "-Dcom.sun.management.jmxremote.local.only=false",
-                             
"-Dcom.sun.management.jmxremote.authenticate=false",
-                             "-Dcom.sun.management.jmxremote.ssl=false",
-
-                             // Options for running on Java 11
-                             "-XX:+IgnoreUnrecognizedVMOptions",
-                             
"--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
-                             "--add-exports=java.base/sun.nio.ch=ALL-UNNAMED",
-                             
"--add-exports=java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED",
-                             
"--add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED",
-                             
"--add-exports=java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED",
-                             "--illegal-access=permit"]
+application {
+    mainClass = 'org.apache.ignite.ci.TcHelperJettyLauncher'
+    applicationDefaultJvmArgs = igniteJava17JvmArgs + 
["-Dteamcity.helper.home=../work",
+                                                       "-Dfile.encoding=UTF-8",
+                                                       
"-Dteamcity.bot.regionsize=16", // 16g Durable Memory region
+                                                       
"-Dhttp.maxConnections=30",
+                                                       "-server",
+                                                       "-Xmx16g",
+                                                       "-XX:+AlwaysPreTouch",
+                                                       "-XX:+UseG1GC",
+                                                       
"-XX:+ScavengeBeforeFullGC",
+                                                       
"-XX:+UseStringDeduplication",
+                                                       
"-Djava.rmi.server.hostname=app02",
+                                                       
"-Dcom.sun.management.jmxremote",
+                                                       
"-Dcom.sun.management.jmxremote.port=9010",
+                                                       
"-Dcom.sun.management.jmxremote.local.only=false",
+                                                       
"-Dcom.sun.management.jmxremote.authenticate=false",
+                                                       
"-Dcom.sun.management.jmxremote.ssl=false"]
+}
 
 distributions {
     main {
         contents {
             into('war') {
-                from { new 
File(project(':ignite-tc-helper-web').getBuildDir(), 
"libs/ignite-tc-helper-web.war")}
-
-                fileMode = 0755
+                from project(':ignite-tc-helper-web').tasks.named('war')
             }
         }
     }
 }
 
+tasks.register('prepareLocalWarRun') {
+    dependsOn tasks.named('installDist')
+
+    doLast {
+        def workDir = 
layout.buildDirectory.dir('install/jetty-launcher/work').get().asFile
+        copy {
+            from rootProject.file('conf/branches.json')
+            into workDir
+        }
+        mkdir new File(workDir, 'tcbot_logs')
+    }
+}
+
 dependencies {
-    compile (project(":ignite-tc-helper-web"))  {
+    implementation (project(":ignite-tc-helper-web"))  {
         transitive = false
     }
 
-    compile group: 'org.eclipse.jetty', name: 'jetty-util', version: jettyVer
-    compile group: 'org.eclipse.jetty', name: 'jetty-server', version: jettyVer
-    compile group: 'org.eclipse.jetty', name: 'jetty-webapp', version: jettyVer
+    implementation group: 'org.eclipse.jetty', name: 'jetty-util', version: 
jettyVer
+    implementation group: 'org.eclipse.jetty', name: 'jetty-server', version: 
jettyVer
+    implementation group: 'org.eclipse.jetty.ee8', name: 'jetty-ee8-webapp', 
version: jettyVer
+
+    implementation group: 'com.google.guava', name: 'guava', version: guavaVer
 
-    compile group: 'com.google.guava', name: 'guava', version: guavaVer
+    implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4jVer
+    implementation group: 'ch.qos.logback', name: 'logback-core', version: 
logbackVer
+    implementation group: 'ch.qos.logback', name: 'logback-classic', version: 
logbackVer
 }
diff --git a/jetty-launcher/src/main/resources/logback.xml 
b/jetty-launcher/src/main/resources/logback.xml
new file mode 100644
index 00000000..4b47ce81
--- /dev/null
+++ b/jetty-launcher/src/main/resources/logback.xml
@@ -0,0 +1,21 @@
+<configuration>
+    <property name="TCBOT_LOG_DIR" 
value="${teamcity.helper.home:-../work}/tcbot_logs"/>
+
+    <appender name="FILE" 
class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${TCBOT_LOG_DIR}/logfile-.log</file>
+        <append>true</append>
+        <encoder>
+            <pattern>%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level [%t] - 
%msg%n</pattern>
+        </encoder>
+        <rollingPolicy 
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            
<fileNamePattern>${TCBOT_LOG_DIR}/logfile-%d{yyyy-MM-dd_HH}.log</fileNamePattern>
+            <maxHistory>336</maxHistory>
+        </rollingPolicy>
+    </appender>
+
+    <logger name="org.eclipse.jetty" level="INFO"/>
+
+    <root level="INFO">
+        <appender-ref ref="FILE"/>
+    </root>
+</configuration>
diff --git a/migrator/src/main/java/org/apache/ignite/migrate/Transformer.java 
b/migrator/src/main/java/org/apache/ignite/migrate/Transformer.java
index 47863c36..7c687f8e 100644
--- a/migrator/src/main/java/org/apache/ignite/migrate/Transformer.java
+++ b/migrator/src/main/java/org/apache/ignite/migrate/Transformer.java
@@ -222,8 +222,12 @@ public final class Transformer {
 
             if (obj instanceof org.apache.ignite.internal.util.GridIntList) {
                 org.apache.ignite.internal.util.GridIntList g = 
(org.apache.ignite.internal.util.GridIntList)obj;
+                int[] ints = new int[g.size()];
 
-                return g.array();
+                for (int i = 0; i < ints.length; i++)
+                    ints[i] = g.get(i);
+
+                return ints;
             }
         }
         catch (Throwable t) {
diff --git a/tcbot-common/build.gradle b/tcbot-common/build.gradle
index 143eb5e6..570cc131 100644
--- a/tcbot-common/build.gradle
+++ b/tcbot-common/build.gradle
@@ -18,21 +18,23 @@
 apply plugin: 'java'
 
 dependencies {
-    compile group: 'com.google.guava', name: 'guava', version: guavaVer
+    api group: 'com.google.guava', name: 'guava', version: guavaVer
 
-    compile group: 'org.slf4j', name: 'slf4j-api', version: slf4jVer
+    api group: 'org.slf4j', name: 'slf4j-api', version: slf4jVer
     //Apache License Version 2.0
-    compile group: 'com.google.code.gson', name: 'gson', version: gsonVer
+    api group: 'com.google.code.gson', name: 'gson', version: gsonVer
 
-    compile group: 'javax.inject', name: 'javax.inject', version: '1'
-    compile group: 'com.google.inject', name: 'guice', version: '4.2.0'
+    api group: 'javax.inject', name: 'javax.inject', version: '1'
+    api group: 'com.google.inject', name: 'guice', version: guiceVer
+    api group: 'com.google.code.findbugs', name: 'jsr305', version: jsr305Ver
+    api group: 'org.checkerframework', name: 'checker-qual', version: 
checkerQualVer
 
-    compile group: 'org.xerial.snappy', name: 'snappy-java', version: '1.1.7.2'
+    api group: 'org.xerial.snappy', name: 'snappy-java', version: snappyVer
 
-    compile group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1'
-    compile group: 'com.sun.xml.bind', name: 'jaxb-impl', version: '2.3.0'
-    compile group: 'com.sun.xml.bind', name: 'jaxb-core', version: '2.3.0'
+    api group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1'
+    api group: 'com.sun.xml.bind', name: 'jaxb-impl', version: jaxbVer
+    api group: 'com.sun.xml.bind', name: 'jaxb-core', version: '2.3.0'
     
-    testCompile group: 'junit', name: 'junit', version: junitVer;
-    testCompile group: 'org.mockito', name: 'mockito-core', version: 
mockitoVer;
+    testImplementation group: 'junit', name: 'junit', version: junitVer;
+    testImplementation group: 'org.mockito', name: 'mockito-core', version: 
mockitoVer;
 }
diff --git 
a/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/IDataSourcesConfigSupplier.java
 
b/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/IDataSourcesConfigSupplier.java
index 68873ad6..73f3d47e 100644
--- 
a/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/IDataSourcesConfigSupplier.java
+++ 
b/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/IDataSourcesConfigSupplier.java
@@ -21,9 +21,9 @@ package org.apache.ignite.tcbot.common.conf;
  * 3rd party data sources (services/servers) configurations.
  */
 public interface IDataSourcesConfigSupplier {
-    public ITcServerConfig getTeamcityConfig(String srvCode);
+    ITcServerConfig getTeamcityConfig(String srvCode);
 
-    public IGitHubConfig getGitConfig(String srvCode);
+    IGitHubConfig getGitConfig(String srvCode);
 
-    public IJiraServerConfig getJiraConfig(String srvCode);
+    IJiraServerConfig getJiraConfig(String srvCode);
 }
diff --git 
a/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/IGitHubConfig.java
 
b/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/IGitHubConfig.java
index 8016983d..94f85d75 100644
--- 
a/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/IGitHubConfig.java
+++ 
b/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/IGitHubConfig.java
@@ -26,11 +26,11 @@ public interface IGitHubConfig {
     /**
      * @return server (service) code.
      */
-    public String code();
+    String code();
 
     /** Git branch prefix for search ticket-related TC runs in PR-less 
contributions. */
     @Nonnull
-    public String gitBranchPrefix();
+    String gitBranchPrefix();
 
     /**
      * Extracts and returns GitHub authorization token from properties.
@@ -38,13 +38,13 @@ public interface IGitHubConfig {
      * @return Null or decoded auth token for Github.
      */
     @Nullable
-    public String gitAuthTok();
+    String gitAuthTok();
 
     /**
      * @return GitHub Api URL, if specified always ends with '/'
      */
     @Nullable
-    public String gitApiUrl();
+    String gitApiUrl();
 
     default boolean isGitTokenAvailable() {
         return gitAuthTok() != null;
diff --git 
a/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/IJiraServerConfig.java
 
b/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/IJiraServerConfig.java
index ef6abec2..bf1fd298 100644
--- 
a/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/IJiraServerConfig.java
+++ 
b/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/IJiraServerConfig.java
@@ -29,45 +29,45 @@ public interface IJiraServerConfig {
     /**
      * @return Service ID or server code, internally identified, any string 
configured.
      */
-    public String getCode();
+    String getCode();
 
     /**
      * Return JIRA URL, e.g. https://issues.apache.org/jira/
      */
-    public String getUrl();
+    String getUrl();
 
     /**
      * Returns JIRA API version.
      *
      * @return JIRA API version.
      */
-    public JiraApiVersion getApiVersion();
+    JiraApiVersion getApiVersion();
 
     /**
      * JIRA project code for filtering out tickets and for adding VISA (JIRA 
comments).
      */
-    public String projectCodeForVisa();
+    String projectCodeForVisa();
 
     /**
      * @return PR name and branch name matching number prefix
      */
-    @Nullable public String branchNumPrefix();
+    @Nullable String branchNumPrefix();
 
     /**
      * Extracted JIRA basic authorization token from properties.
      *
      * @return Null or decoded auth token for JIRA.
      */
-    @Nullable public String decodedHttpAuthToken();
+    @Nullable String decodedHttpAuthToken();
 
     /**
      * @return {@code True} if JIRA authorization token is available.
      */
-    public default boolean isJiraTokenAvailable() {
+    default boolean isJiraTokenAvailable() {
         return !Strings.isNullOrEmpty(decodedHttpAuthToken());
     }
 
-    public default String restApiUrl() {
+    default String restApiUrl() {
         String jiraUrl = getUrl();
 
         if (isNullOrEmpty(jiraUrl))
diff --git 
a/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/ITcServerConfig.java
 
b/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/ITcServerConfig.java
index d35f33cc..d57bbadd 100644
--- 
a/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/ITcServerConfig.java
+++ 
b/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/conf/ITcServerConfig.java
@@ -20,7 +20,7 @@ import java.time.format.DateTimeFormatter;
 import org.checkerframework.checker.nullness.qual.NonNull;
 
 import javax.annotation.Nullable;
-import java.util.Collection; 
+import java.util.Collection;
 
 /**
  * Teamcity Server configuration.
@@ -35,44 +35,44 @@ public interface ITcServerConfig {
      * @return Another TC Server (service) config name to use settings from. 
Filled only for server aliases.
      */
     @Nullable
-    public String reference();
+    String reference();
 
     /**
      * @return ID of additional server (service code), that user should have 
access to.
      */
-    @Nullable public String additionalServiceToCheckAccess();
+    @Nullable String additionalServiceToCheckAccess();
 
     /**
      * @return Normalized Host address, ends with '/'.
      */
-    @NonNull public String host();
+    @NonNull String host();
 
     /**
      * @return Directory for downloading build logs (will contain ZIP files).
      */
-    @NonNull public String logsDirectory();
+    @NonNull String logsDirectory();
 
     /**
      * @return internal naming of default tracked branch for this server.
      */
-    @NonNull public String defaultTrackedBranch();
+    @NonNull String defaultTrackedBranch();
 
     /**
      * Suite id to be used for VISAs (to avoid search using 
defaultTrackedBranch())
      */
-    @Nullable public String defaultVisaSuiteId();
+    @Nullable String defaultVisaSuiteId();
 
     /**
      * Provides build parameters, whichi could be used for filtering builds in 
RunHist/Invocations and tagging in UI.
      *
      * @return set of build parameters specifications.
      */
-    @NonNull public Collection<? extends IBuildParameterSpec> 
filteringParameters();
+    @NonNull Collection<? extends IBuildParameterSpec> filteringParameters();
 
     /**
      * @return set of suite codes (build type IDs), failures in which should 
be threated as critical and notified.
      */
-    @NonNull public Collection<String> trustedSuites();
+    @NonNull Collection<String> trustedSuites();
 
     /**
      * @return Time as auto-triggering build is disabled. {@link 
DateTimeFormatter.ISO_LOCAL_TIME} must be used.
diff --git 
a/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/interceptor/MonitoredTaskInterceptor.java
 
b/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/interceptor/MonitoredTaskInterceptor.java
index bf8b00ea..a47b55ea 100644
--- 
a/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/interceptor/MonitoredTaskInterceptor.java
+++ 
b/tcbot-common/src/main/java/org/apache/ignite/tcbot/common/interceptor/MonitoredTaskInterceptor.java
@@ -56,7 +56,7 @@ public class MonitoredTaskInterceptor implements 
MethodInterceptor, AutoCloseabl
                 timestampForLogsSimpleDate(System.currentTimeMillis())+".log");
 
             if (!tcbotLogs.exists())
-                file.mkdirs();
+                tcbotLogs.mkdirs();
 
             fileWriter = new FileWriter(file);
         }
diff --git a/tcbot-engine/build.gradle b/tcbot-engine/build.gradle
index ac2b8397..f557ba6c 100644
--- a/tcbot-engine/build.gradle
+++ b/tcbot-engine/build.gradle
@@ -18,12 +18,12 @@
 apply plugin: 'java'
 
 dependencies {
-    compile (project(":tcbot-teamcity-ignited"));
-    compile (project(":tcbot-github-ignited"));
-    compile (project(":tcbot-jira-ignited"));
-    compile (project(":tcbot-notify"));
+    api project(":tcbot-teamcity-ignited")
+    api project(":tcbot-github-ignited")
+    api project(":tcbot-jira-ignited")
+    api project(":tcbot-notify")
 
-    testCompile group: 'junit', name: 'junit', version: junitVer
-    testCompile group: 'org.mockito', name: 'mockito-core', version: mockitoVer
+    testImplementation group: 'junit', name: 'junit', version: junitVer
+    testImplementation group: 'org.mockito', name: 'mockito-core', version: 
mockitoVer
 }
- 
\ No newline at end of file
+ 
diff --git 
a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/conf/ITcBotConfig.java
 
b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/conf/ITcBotConfig.java
index bb97a9c3..bc28739b 100644
--- 
a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/conf/ITcBotConfig.java
+++ 
b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/conf/ITcBotConfig.java
@@ -28,48 +28,48 @@ import java.util.Collection;
  */
 public interface ITcBotConfig extends IDataSourcesConfigSupplier {
     /** Default server code. */
-    public String DEFAULT_SERVER_CODE = "apache";
+    String DEFAULT_SERVER_CODE = "apache";
 
     /** Default flaky rate. */
-    public Integer DEFAULT_FLAKY_RATE = 20;
+    Integer DEFAULT_FLAKY_RATE = 20;
 
     /** Default confidence. */
-    public Double DEFAULT_CONFIDENCE = 0.95;
+    Double DEFAULT_CONFIDENCE = 0.95;
 
     /** */
-    public String primaryServerCode();
+    String primaryServerCode();
 
     /** */
-    public Integer flakyRate();
+    Integer flakyRate();
 
     /** */
-    public Double confidence();
+    Double confidence();
 
     /** */
-    public Boolean alwaysFailedTestDetection();
+    Boolean alwaysFailedTestDetection();
 
     /**
      * @return Tracked branches configuration for TC Bot.
      */
-    public ITrackedBranchesConfig getTrackedBranches();
+    ITrackedBranchesConfig getTrackedBranches();
 
     /**
      * @return list of servers (services) identifiers involved into tracked 
branches processing.
      */
-    public default Collection<String> getServerIds() {
+    default Collection<String> getServerIds() {
         return getTrackedBranches().getServerIds();
     }
 
-    public ITcServerConfig getTeamcityConfig(String srvCode);
+    ITcServerConfig getTeamcityConfig(String srvCode);
 
-    public IJiraServerConfig getJiraConfig(String srvCode);
+    IJiraServerConfig getJiraConfig(String srvCode);
 
-    public IGitHubConfig getGitConfig(String srvCode);
+    IGitHubConfig getGitConfig(String srvCode);
 
     /**
      * @return notification settings config.
      */
-    public NotificationsConfig notifications();
+    NotificationsConfig notifications();
 
-    public ICleanerConfig getCleanerConfig();
+    ICleanerConfig getCleanerConfig();
 }
diff --git a/tcbot-github-ignited/build.gradle 
b/tcbot-github-ignited/build.gradle
index e780b09f..cdd1ff42 100644
--- a/tcbot-github-ignited/build.gradle
+++ b/tcbot-github-ignited/build.gradle
@@ -18,7 +18,7 @@
 apply plugin: 'java'
 
 dependencies {
-    compile (project(":tcbot-github"));
-    compile (project(":tcbot-persistence"));
+    api project(":tcbot-github")
+    api project(":tcbot-persistence")
 }
- 
\ No newline at end of file
+ 
diff --git a/tcbot-github/build.gradle b/tcbot-github/build.gradle
index 2bacd408..d395787d 100644
--- a/tcbot-github/build.gradle
+++ b/tcbot-github/build.gradle
@@ -18,12 +18,12 @@
 apply plugin: 'java'
 
 dependencies {
-    compile (project(":tcbot-common"));
+    api project(":tcbot-common")
     // GitHub integration shares entries for pure and Ignited connection, so 
persistence module is here for interfaces/annotations
-    compile (project(":tcbot-persistence"));
+    api project(":tcbot-persistence")
     
-    testCompile group: 'junit', name: 'junit', version: junitVer
-    testCompile group: 'org.mockito', name: 'mockito-core', version: mockitoVer
+    testImplementation group: 'junit', name: 'junit', version: junitVer
+    testImplementation group: 'org.mockito', name: 'mockito-core', version: 
mockitoVer
 }
 
 
@@ -35,11 +35,5 @@ processResources {
 
 test {
     // set JVM arguments for the test JVM(s)
-    jvmArgs "-XX:+IgnoreUnrecognizedVMOptions",
-        "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
-        "--add-exports=java.base/sun.nio.ch=ALL-UNNAMED",
-        "--add-exports=java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED",
-        "--add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED",
-        
"--add-exports=java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED",
-        "--illegal-access=permit"
+    jvmArgs igniteJava17JvmArgs
 }
diff --git a/tcbot-jira-ignited/build.gradle b/tcbot-jira-ignited/build.gradle
index b8fed7fd..5070b4e7 100644
--- a/tcbot-jira-ignited/build.gradle
+++ b/tcbot-jira-ignited/build.gradle
@@ -18,7 +18,7 @@
 apply plugin: 'java'
 
 dependencies {
-    compile (project(":tcbot-jira"));
-    compile (project(":tcbot-persistence"));
+    api project(":tcbot-jira")
+    api project(":tcbot-persistence")
 }
- 
\ No newline at end of file
+ 
diff --git a/tcbot-jira/build.gradle b/tcbot-jira/build.gradle
index ca6c2cc3..ceaedf78 100644
--- a/tcbot-jira/build.gradle
+++ b/tcbot-jira/build.gradle
@@ -18,11 +18,11 @@
 apply plugin: 'java'
 
 dependencies {
-    compile (project(":tcbot-common"));
+    api project(":tcbot-common")
     // JIRA integration shares entries for pure and Ignited connection, so 
persistence module is here for interfaces/annotations
-    compile (project(":tcbot-persistence"));
+    api project(":tcbot-persistence")
     
-    testCompile group: 'junit', name: 'junit', version: junitVer
-    testCompile group: 'org.mockito', name: 'mockito-core', version: mockitoVer
+    testImplementation group: 'junit', name: 'junit', version: junitVer
+    testImplementation group: 'org.mockito', name: 'mockito-core', version: 
mockitoVer
 }
- 
\ No newline at end of file
+ 
diff --git a/tcbot-notify/build.gradle b/tcbot-notify/build.gradle
index 89966f3f..f4464588 100644
--- a/tcbot-notify/build.gradle
+++ b/tcbot-notify/build.gradle
@@ -18,9 +18,9 @@
 apply plugin: 'java'
 
 dependencies {
-    compile (project(":tcbot-common"));
+    api project(":tcbot-common")
 
-    compile group: 'javax.mail', name: 'mail', version: '1.4.7'
+    api group: 'com.sun.mail', name: 'javax.mail', version: mailVer
 
-    compile group: 'com.ullink.slack', name: 'simpleslackapi', version: '1.4.0'
+    api group: 'com.ullink.slack', name: 'simpleslackapi', version: '1.4.0'
 }
diff --git a/tcbot-persistence/build.gradle b/tcbot-persistence/build.gradle
index 82c2e63f..e619aef1 100644
--- a/tcbot-persistence/build.gradle
+++ b/tcbot-persistence/build.gradle
@@ -23,12 +23,12 @@ repositories {
 }
 
 dependencies {
-    compile (project(":tcbot-common"));
+    api project(":tcbot-common")
 
-    compile (group: 'org.apache.ignite', name: 'ignite-core', version: ignVer) 
{
+    api (group: 'org.apache.ignite', name: 'ignite-core', version: ignVer) {
         exclude group: 'org.jetbrains'
     }
 
-    implementation "org.apache.ignite:ignite-indexing:$ignVer"
+    api "org.apache.ignite:ignite-indexing:$ignVer"
 }
- 
\ No newline at end of file
+ 
diff --git a/tcbot-server-node/build.gradle b/tcbot-server-node/build.gradle
index 0bc43f85..630082fa 100644
--- a/tcbot-server-node/build.gradle
+++ b/tcbot-server-node/build.gradle
@@ -23,41 +23,44 @@ repositories {
     mavenLocal()
 }
 
-mainClassName = 'org.apache.ignite.ci.TcBotIgniteServerLauncher'
-applicationDefaultJvmArgs = ["-Dteamcity.helper.home=../work",
-                             "-Dteamcity.bot.regionsize=16", // 16g Durable 
Memory region
-                             "-server",
-                             "-Xmx2g",
-                             "-XX:+AlwaysPreTouch",
-                             "-XX:+UseG1GC",
-                             "-XX:+ScavengeBeforeFullGC",
-                             "-XX:+UseStringDeduplication",
-                             "-Djava.rmi.server.hostname=app02",
-                             "-Dcom.sun.management.jmxremote",
-                             "-Dcom.sun.management.jmxremote.port=9011",
-                             "-Dcom.sun.management.jmxremote.local.only=false",
-                             
"-Dcom.sun.management.jmxremote.authenticate=false",
-                             "-Dcom.sun.management.jmxremote.ssl=false"]
+application {
+    mainClass = 'org.apache.ignite.ci.TcBotIgniteServerLauncher'
+    applicationDefaultJvmArgs = igniteJava17JvmArgs + 
["-Dteamcity.helper.home=../work",
+                                                       "-Dfile.encoding=UTF-8",
+                                                       
"-Dteamcity.bot.regionsize=16", // 16g Durable Memory region
+                                                       "-server",
+                                                       "-Xmx2g",
+                                                       "-XX:+AlwaysPreTouch",
+                                                       "-XX:+UseG1GC",
+                                                       
"-XX:+ScavengeBeforeFullGC",
+                                                       
"-XX:+UseStringDeduplication",
+                                                       
"-Djava.rmi.server.hostname=app02",
+                                                       
"-Dcom.sun.management.jmxremote",
+                                                       
"-Dcom.sun.management.jmxremote.port=9011",
+                                                       
"-Dcom.sun.management.jmxremote.local.only=false",
+                                                       
"-Dcom.sun.management.jmxremote.authenticate=false",
+                                                       
"-Dcom.sun.management.jmxremote.ssl=false"]
+}
 
 distributions {
 
 }
 
 dependencies {
-    compile (project(":tcbot-common")) {
+    implementation (project(":tcbot-common")) {
         transitive = false
     }
 
-    compile (project(":ignite-tc-helper-web"))  {
+    implementation (project(":ignite-tc-helper-web"))  {
         transitive = false
     }
 
-    compile group: 'com.google.guava', name: 'guava', version: guavaVer
+    implementation group: 'com.google.guava', name: 'guava', version: guavaVer
 
-    compile group: 'ch.qos.logback', name: 'logback-core', version: logbackVer
-    compile group: 'ch.qos.logback', name: 'logback-classic', version: 
logbackVer
+    implementation group: 'ch.qos.logback', name: 'logback-core', version: 
logbackVer
+    implementation group: 'ch.qos.logback', name: 'logback-classic', version: 
logbackVer
 
-    compile group: 'org.apache.ignite', name: 'ignite-core', version: ignVer
-    compile group: 'org.apache.ignite', name: 'ignite-slf4j', version: ignVer
-    compile group: 'org.apache.ignite', name: 'ignite-direct-io', version: 
ignVer
-}
\ No newline at end of file
+    implementation group: 'org.apache.ignite', name: 'ignite-core', version: 
ignVer
+    implementation group: 'org.apache.ignite', name: 'ignite-slf4j', version: 
ignVer
+    implementation group: 'org.apache.ignite', name: 'ignite-direct-io', 
version: ignVer
+}
diff --git a/tcbot-teamcity-ignited/build.gradle 
b/tcbot-teamcity-ignited/build.gradle
index 30711532..1720ef52 100644
--- a/tcbot-teamcity-ignited/build.gradle
+++ b/tcbot-teamcity-ignited/build.gradle
@@ -18,10 +18,10 @@
 apply plugin: 'java'
  
 dependencies {
-    compile (project(":tcbot-teamcity"));
-    compile (project(":tcbot-persistence"));
+    api project(":tcbot-teamcity")
+    api project(":tcbot-persistence")
 
-    testCompile group: 'junit', name: 'junit', version: junitVer;
-    testCompile group: 'org.mockito', name: 'mockito-core', version: 
mockitoVer;
+    testImplementation group: 'junit', name: 'junit', version: junitVer;
+    testImplementation group: 'org.mockito', name: 'mockito-core', version: 
mockitoVer;
 }
- 
\ No newline at end of file
+ 
diff --git a/tcbot-teamcity/build.gradle b/tcbot-teamcity/build.gradle
index df805fb9..7f18daa5 100644
--- a/tcbot-teamcity/build.gradle
+++ b/tcbot-teamcity/build.gradle
@@ -19,10 +19,10 @@ apply plugin: 'java'
  
 
 dependencies {
-    compile (project(":tcbot-common"));
+    api project(":tcbot-common")
 
-    /// JAXB replacement for Java 11, API is defined in common
-    compile group: 'com.sun.xml.bind', name: 'jaxb-impl', version: '2.3.0'
-    compile group: 'com.sun.xml.bind', name: 'jaxb-core', version: '2.3.0'
+    /// JAXB replacement for Java 17, API is defined in common
+    api group: 'com.sun.xml.bind', name: 'jaxb-impl', version: jaxbVer
+    api group: 'com.sun.xml.bind', name: 'jaxb-core', version: '2.3.0'
 }
- 
\ No newline at end of file
+ 

Reply via email to