Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Сборка в один jar

На сервере нет Maven и исходников — туда нужно положить готовую программу. Удобнее всего собрать исполняемый jar со всеми зависимостями внутри (его называют fat jar или uber jar). Такой файл запускается одной командой java -jar, а Jetty едет внутри него — отдельный веб-сервер на машине не нужен.

Почему обычного jar мало

Команда mvn package и без плагинов создаёт jar, но в нём только ваши классы. Javalin, Jetty, Jackson и H2 лежат в локальном репозитории Maven на вашем компьютере, а на сервере их нет. Плагин maven-shade-plugin распаковывает все зависимости и складывает их в один файл вместе с вашим кодом.

Плагин

Добавьте плагин в раздел plugins файла pom.xml:

<!-- pom.xml -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.6.2</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.example.blog.Main</mainClass>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>
ЧастьЗачем
phase packageПлагин срабатывает при mvn package
ManifestResourceTransformerЗаписывает главный класс в манифест jar, иначе java -jar не знает, что запускать
ServicesResourceTransformerОбъединяет служебные файлы META-INF/services, через которые Jetty и драйверы находят свои части

Порт из окружения

На сервере порт часто задают переменной окружения. Сделайте так, чтобы main читал PORT и брал 8080 по умолчанию:

// src/main/java/com/example/blog/Main.java
public static void main(String[] args) {
    var repo = new PostRepository(Database.create("jdbc:h2:./blog"));
    int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "8080"));
    createApp(repo).start(port);
}

Сборка и запуск

mvn package

Maven выполнит тесты и соберёт файл target/blog-1.0.0.jar размером около 12 МБ. Во время сборки плагин печатает много предупреждений Discovered module-info.class и overlapping resource — для нашего приложения они безопасны. Запустите jar:

java -jar target/blog-1.0.0.jar
[main] INFO io.javalin.Javalin - Javalin started in 176ms \o/
[main] INFO io.javalin.Javalin - Listening on http://localhost:8080/

На другом порту: PORT=9000 java -jar target/blog-1.0.0.jar. На сервер достаточно скопировать этот файл и установить Java 25. Файл базы blog.mv.db появится в папке, из которой запущена команда.

Что дальше

Чтобы приложение работало постоянно и перезапускалось после сбоев, его запускают как службу (systemd в Linux) или в Docker-контейнере на основе образа с Java, например eclipse-temurin:25-jre. В обоих случаях команда запуска та же — java -jar blog-1.0.0.jar.

Частая ошибка

Рядом с нужным файлом плагин оставляет original-blog-1.0.0.jar — обычный jar без зависимостей и без главного класса. Если запустить его, Java сразу сообщит:

no main manifest attribute, in target\original-blog-1.0.0.jar

А если указать класс вручную через java -cp, программа упадёт на первом же классе Javalin: NoClassDefFoundError: io/javalin/rendering/FileRenderer. Запускайте файл без приставки original-.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.