Если у вас не работает Spring BootJar

от автора

Проблема с загрузкой Spring Boot Jar

image

Сталкивались ли вы с проблемой запуска нового загрузочного архива Spring Boot?

Вообще, новация в этом направлении уже не первая, стандартов особых нет. Поэтому многие огребают проблемы и решают их на форумах и стек-оверфлоу.
Если вы также столкнулись с проблемой, я помогу её решить. В таком случае читаем дальше.

Итак, проблем с BootJar на самом деле хватает. Особенно учитывая, что уже минимум три версии формата поменялось.

Здесь я расскажу о часто встречающемся случае с потерей ресурсов. Конкретнее в моём случае — с потерей JSP шаблонов. Почему JSP? Мне так привычнее, проект я по-быстрому начал с них, и не думал, что будут такие проблемы.

Итак структура проекта (стандартный веб):

/src/main/     java/     resources/         static/             some.html         public/     webapp/         WEB-INF/jsp             index.jsp

По заявлению создателей BootJar / BootWar, jsp не поддерживается толком в новом BootJar формате. Но совместимость должна быть в BootWar. На это я и надеялся, когда ваял код. Пока ваял, никаких проблем — всё запускается, всё работает, как обычно, в общем. BootRun отрабатывает, только опции успевай подставлять.

Проблема пришла, откуда не ждали: когда пришло время разворачиваться на Амазоне, тогда и вылезла.

Итак, дубль раз. Чуть ли не в первый раз запускаю задачу BootJar. Ярхив готов, деплоим… Готово! Сигнала нет, сыпет ошибки 302 + 404 (это авторизация не находит вью). Но это пока не понятно.
Отключаем Секурити — всё равно ничего не находит, кроме голимой статики, и то не всей, а только из webjars. ???

Дубль два. Догадываясь о несовместимости jsp и BootJar, пакуем BootWar. Деплоим… Не работает. Не помню точно, но примерно то же самое в результате.

Хм, странно. Запускаем BootJar локально — всё работает. Чудеса.

Выяснилось: Spring Boot слишком умный, он при запуске из каталога разработки даже релизного ярника всё равно все тащит из каталога разработки. Из другого запускаем — перестаёт работать. Фух! Ну хоть отлаживать можно.

Что и делам. Выясняется — ресурсы BootJar запакованные не из библиотек (webjars), а из проекта, не включаются в перечисление, и это, оказывается, по дизайну! Подробности здесь.

Вернее не так — есть спец-ресурсы в каталогах типа static/, public/. И они вроде находятся, если объявить. Но jsp не видит в упор хоть тресни. И дело не в том, что не там лежат. Оказалось, что Томкат (в нашем плохом случае), грузит jsp особым механизмом после редиректа. И сами jsp можно загрузить без рендеринга, если правильно задать их положение в настройках
spring.resources.static-locations

Но это нас не устраивает.
Оказалось, что при использования встроенного томката, ресурсы вью он грузит отдельно и в первую очередь своей старой встроенной логикой, которую Спрингисты настраивать не научились. А этой логике нужен либо архив Вар, либо он же распакованный (почему кстати при разработке нормально отрабатывает расположение webapp/), либо ресурсы из библиотек, которые прекрасно видны, если правильно упакованы в изначальных либах — нужно чтоб лежали в META-INF/resources, как в стандарте сервлетов. Последнее работает даже внутри BootJar. Удивительно.

Почему не сработал распакованный архив? Ё-маё, на амазоне он распакуется черти-куда, и приложение про это место ничего не знает, если не сказать. Но хардкодить пути — плохая привычка. Сам ярник запускается затем безо всякой распаковки, хотя вроде права позволяют распаковать прям на месте. Ну да ладно, способ пролетел.

Почему не работает способ с Вар-архивом? Ё-маё, Амазон решил, что лучше меня знает, что я буду грузить именно в яр-формате, хотя интерфейс заявлет о готовности съесть варник. Он этот варник переименовывает в ярник, умник такой. А Томкат не умничает, он смотрит расширение: не Вар — ну тогда, извините, это не мой случай.

В итоге корень деплоя не находится. И ресурсы оттуда тоже. Ресурсы из статики не грузятся, потому что ищутся в корне и ресурсных либах, а не в classpath.

Ладно, проблема понятна. Решение?

Было три варианта.

  1. Сделать свой spring-загрузчик ресурсов. Вариант отпал, поскольку, как я уже сказал, Томкат отрабатывает jsp до их запуска.
  2. Прокинуть загрузку в Томкат. Стал прикидывать: надо расширить контекст spring-а, прокинуть пути в контекст Томката, там ещё раза два переложить — непонятно, насколько сложно, и можно ли без изменения самого томката. Спрингисты не осилили, и я не хочу.
  3. Вариант попроще — пакуем ресурсы в ресурсную либу в BootJar.

Вот о нём подробнее. Пихать всё в отдельный модуль, как предлагали здесь, не хотелось.
Делаем отдельной задачей в Gradle.
Создаём конфигурацию.

sourceSets {     jsp {         resources.source(sourceSets.main.resources);         resources.srcDirs += ['src/main/webapp'];     }     jmh {         .. ..     } }

Сама задача

task jsp(type: Jar, description: 'JSP Packaging') {     archiveBaseName = 'jsp'     group = "build"     def art = sourceSets.jsp.output     from(art) {         exclude('META-INF')         into('META-INF/resources/')     }     from(art) {         include('META-INF/*')         into('/')     }     dependsOn(processJspResources) }

Задача processJspResources уже создана, её не надо делать. Ставим всё в зависимость и пакуем:

bootJar {     dependsOn(jsp)     bootInf.with {         from(jsp.archiveFile) {             include('**/*.jar')         }         into('lib/')     } }

Как добавить другим способом (прямым), не нашёл — подключить в зависимости конфиг jspImplementation самого же проекта — нельзя, а хотелось бы. Но если все же из другого модуля забирать, то вот так ещё делаем:

artifacts {     jspImplementation jsp }

Всё, теперь имеем ресурсную либу, которую по всем стандартам томкат должен грузить, и он грузит. Запускаем, как BootJar.

ссылка на оригинал статьи https://habr.com/ru/post/535246/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *