Удаленная отладка в IntelliJ Idea

Временно «отдыхаю» от котлино-реакта. Перебросили меня на проект по доработкам отчетов на софтверной жире. Это такие плагины для Atlassian Jira. У нас они сейчас на 8 джаве с приправой из Atlassian SDK и с фронтом на Anguar-5.

Отчеты на Atlassian SDK это такая штука, где вы будете использовать циклы-в-цикле и никуда от этого не деться, благодаря такой «замечательной» штуке как Active Object. Фишка в чем — тут нет join-ов! Поэтому таблицы содержат избыточные ключи, для связок. Это значит что под капотом это выглядит примерно так: select * from table1 where table2_id  in (id1, id2 … idN) facepalm.jpg

Спасибо, что хоть на 8й джаве, где есть небольшое спасение от foreach{ foreach { foreach{}}} благодаря стримам (stream).

Вот сижу, рефакторю, мучаюсь от отсутствия отладки.  А потом думаю, а чего я собственно? Тема-то наверняка избитая. Списался с админом, погуглил решение и бинго! А вот и не бинго а жопа. Потому что все решения для дебагга касаются запуска томката при помощи батника catalina.bat, а у нас он запущен как сервис.

Делать нечего, будем скрещивать ежа с ужом. В каталоге bin томката лежит кроме catalina.bat еще service.bat, которым собственно и запускают сервис (в нем нет параметров для удаленной отладки).

В нем, где-нибудь в начале надо поставить сеттеры, где localhost:8000 — это адрес сервера и порт для отладки (рекомендуется 8000). Для локальной жиры так и оставить localhost.

set JPDA=jpda
set JPDA_TRANSPORT=dt_socket
set JPDA_ADDRESS=localhost:8000
set JPDA_SUSPEND=n
set JPDA_OPTS=-agentlib:jdwp=transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%

Затем, где идет создание и запуск сервера надо найти параметр —JvmOptions и в него добавить «%JPDA_OPTS%; Например так:

--JvmOptions "%JPDA_OPTS%; -Dcatalina.home=%CATALINA_HOME%;-Dcatalina.base=%CATALINA_BASE%;-D%ENDORSED_PROP%=%CATALINA_HOME%\endorsed;-Djava.io.tmpdir=%CATALINA_BASE%\temp;-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager;-Djava.util.logging.config.file=%CATALINA_BASE%\conf\logging.properties;%JvmArgs%" ^

Потом, когда админ присылает радующую глаз картинку:

Идем в Idea и создаем настройку для удаленной отладки. Для этого в Run|Debug конфигурации прописываем адрес и порт сервера из переменной JPDA_ADDRESS.

Готово, вы великолепны!

p.s.

Как удалить аккаунт Amazon AWS

Вот только что удалил аккаунт, пользовался почти год, но всегда помнил, что в феврале карета превратится в тыкву придет час расплаты в виде $300).

Официальные требования к закрытию аккаунта на амазоне суть которых сводится к простому — заплати бабло и спи спокойно.

На странице биллинга у меня $0.00, потому что я хитрая жопа пользовался бесплатным сервером на год (AWS Free Usage Tier)

Переходим в панель AWS Management Console в Sevices-EC2:

В панели EC2 пробегаемся по всей боковой менюшке и удаляем instance, security group, Key Pairs и т.д. Надо вспомнить где и что вводил/создавал — это нужно все грохнуть и потом получиться закрыть аккаунт.

Когда все интстансы перевели в статус: terminated идем в My account:

И мотаем вниз до красной кнопки Close account. Ставим галку и жмем кнопку.

Готово. Ждем письмо с подтверждением smile

Fat jar и автозапуск web-приложения

Открываю новую рубрику — «рецепты». Во время поиска работы написал я небольшое веб-приложение и для его демонстрации выложил на «бесплатный» сервер амазона. Как получить себе такой сервер, можно почитать на хабре, а я хочу рассказать как используя SpringBoot и его fat jar можно легко установить свое приложение на линуксовый сервер. На примере моего приложения carservice. Нам понадобится доступ к серверу (любому, не обязательно у амазона), на который надо залить наш jar-ник.

  1. Проверить работает ли приложение, выполнив команду: java -jar /home/ubuntu/carservice.jar
  2. Создать файл carservice.service в папке /ets/systemd/system и прописать в нем следующее:
  3. [Unit]
    Description=carservice
    After=syslog.target
    
    [Service]
    User=carservice
    ExecStart=/home/ubuntu/carservice.jar
    SuccessExitStatus=143
    
    [Install]
    WantedBy=multi-user.target
    
  4. Стартовать сервис: service carservice start
  5. Проверить статус: systemctl -l status carservice
  6. Перезагрузить сервер.
  7. Все готово — вы великолепны! smile

Автосчетчик номера сборки (autoincrement build version)

Стоит важная задача государственной важности — нарисовать формочку «О программе» smile где водится всякая инфа о версиях компонент. И оказалось, что у нас нет версии билда. Это не проблема, ведь есть готовое решение. Как раз у спрингбута есть готовый класс, который предоставляет версию. Но не тут-то было.

Версию он не возвращает от слова совсем.

Начинаем опять воевать с грэдлом.

Сначала создаем файлик version.properties и кладем его в корень проекта. Затем делаем таску в корневом gradle.build для автоинкримента при каждом билде.

task('increaseBuildVersion')  {
   def versionPropsFile = project.rootProject.file('version.properties')
   if (versionPropsFile.canRead()) {
       def versionProps = new Properties()
       versionProps.load(new FileInputStream(versionPropsFile))

       def code = versionProps['build_version'].toInteger()+1

       versionProps['build_version'] = code.toString()
       versionProps.store(versionPropsFile.newWriter(), null)
   } else {
       throw new GradleException("Could not read version.properties!")
   }
}.dependsOn('build')

Затем (если gradle.build несколько, то в тот, который в спрингбутовом контексте лежит) дописываем скриптик, чтобы его (спрингбута) класс увидел наши проперти:

ext{
   def versionPropsFile = project.rootProject.file('version.properties')
   def versionProps = new Properties()
   versionProps.load(new FileInputStream(versionPropsFile))
   build_version=versionProps['build_version']
}
springBoot{
   buildInfo {
       properties {
           additional = [
                   'product_name': "$product_name",
                   'product_version': "$product_version",
                   'build_version': "$project.ext.build_version",
                   'java_version': "$java_version"
           ]
       }
   }
}

Ну а далее пишем сервис, который все это добро будет выдавать:

@Service
class AboutInfoService(
    private val buildProperties: BuildProperties?) {

    fun getAboutInfo() = AboutInfo.create {
        it[productName] = buildProperties?.get("product_name")
        it[productVersion] = buildProperties?.get("product_version")
        it[buildNumber] = buildProperties?.get("build_version")
        it[javaVersion] = buildProperties?.get("java_version")
    }
}

Все замечательно работает, версия инкрементиццо, все казалось бы хорошо, но пришла беда откуда не ждали…

Что, блять, делать, если несколько разработчиков одновременно начнут коммитить свою version.properties?!

Восприимчивость Gradle к командной строке

Возникла острая необходимость в ядре для тестов использовать различные БД, и для этого нужно, чтобы gradle передавал конфиг в spingboot. Решение, казалось, лежало на поверхности — использовать параметры командной строки: -Dparameter=value, но как оказалось, это не работает для грэдла (хотя отдельно для исполнения тестов работает).

После гугления нашлось такое решение:

test {
    //https://www.credera.com/blog/technology-insights/java/gradle-profiles-for-multi-project-spring-boot-applications/
    // В аргументах грэдлу передавать через -P
    // -Pproperty.name=value
    // без парамаметров будет браться конфиг из application.properties

    project.ext.applyPropertyIfExists = { propertyKey ->
        if(project.hasProperty(propertyKey)) {
            systemProperties[propertyKey] = project.getProperty(propertyKey)
        }
    }
    applyPropertyIfExists('spring.profiles.active')
    applyPropertyIfExists('spring.datasource.url')
    applyPropertyIfExists('spring.datasource.username')
    applyPropertyIfExists('spring.datasource.password')
}

 

Пока я на испытательном сроке, то прежде чем закомитить, надо показать код сеньору.

Он посмотрел и сказал — норм, но надо сделать проще ))

test {
    //для -Dproperty=value
    systemProperties(System.getProperties())
}
Когда позвали на помощь сеньора.