Минутка просвещения

Читать в телеге. Когда-то там были посты не только от меня.

Нюансы sendmail

Испокон веков одним из стандартов общения операционной системы с пользователем была электронная почта. Я не настолько старый, чтобы это застать, но во времена, когда работать надо было через терминал на сервере, где кроме тебя еще куча пользователей, и личных компов не было, это наверно было удобно.

Работает это довольно просто — глобально настраивается почтовый сервер, а дальше отдаешь команду вроде

echo "hello" | sendmail general@kenobi.sw

И все, письмо ушло адресату. Звучит весьма клево, но внимательный читатель должен был триггернуться на “глобально”.

Недавно выяснилось, что сервер, где такое настроено, выжирает дневной лимит писем в 50к за сутки, хотя предполагаемая нагрузка — 10 писем в день. Виртуалка выключена админами, и в голове мечутся мысли: взломали? Но кому такое надо? Может, ПО, использующее sendmail, сошло с ума?

Однако после разбирательств выяснилось, что столько писем слал… cron. По умолчанию поведение системы такое, что любой stdout от него шлется пользователю на почту. Но на какую почту, ее же наверно надо задать? Разумеется, тут все предусмотрено: на почту user@host, и плевать, что она не существует. А как себя ведет себя с письмом на несуществующий адрес почтовый сервер? Правильно, пытается послать до посинения.

А надо было всего лишь отредактировать /etc/sysconfig/crond, чтобы отключить отправку на почту и включить редирект в syslog:

CRONDARGS=-s -m off

рестартнуть crond и почистить очередь:

exim -bp | exiqgrep -i | xargs exim -Mrm

Вы все еще любите глобальные настройки? Тогда мы идем к вам:)

Ссылка

Факторизация чисел

Иногда складывается ощущение, что в командной строке есть вообще все. Как import antigravity.

Наткнулся недавно абсолютно случайно на команду для факторизации чисел:

$ factor 1224234
1224234: 2 3 3 3 3 3 11 229
$ factor 24342342342342342342342342534512312123
24342342342342342342342342534512312123: 11 43053238739605007 51400086909245973599
$ factor 243423423423423423423423425345123121241
factor: ‘243423423423423423423423425345123121241’ is too large
Ссылка

BPMN-нотация и Camunda

Business Process Model and Notation — еще один способ описания алгоритма или процесса в виде схемы. Разрабатывался с прицелом на то, чтобы нотация была понятна и бизнесу, и разработчикам, и даже менеджерам. TLDR в виде cheatsheet.

Продолжением этой идеи являются BPMN-движки: чтобы схема была не только в документации, но по ней генерировалсь и само управление процессом, вместо какого-нибудь самопального конечного автомата. Звучит неплохо: аналитик нарисовал, разработчик интегрировал где надо, и все готово. Реальность, конечно, немного посложнее, и код все равно надо будет писать. Как и с любым фреймворком, нужно потратить некоторое время на его изучение, за абстракции придется платить производительностью, а при столкновении с ограничениями придется выдумывать костыли. Однако разработку это неплохо ускоряет и плюшка в лице наглядности довольно значимая.

Один из самых популярных BPMN-движков сейчас — это Camunda. Оперсорс, Java, REST API, внутренний API, web-морда для админки — вот, наверно, основные ключи к успеху в текущих реалиях индустрии.

Подробнее про все это можно почитать, например, в блоге о BPMN.

Ссылка

Баг горячих клавиш Xorg

У Xorg есть противный баг: горячие клавиши работают при нажатии, а не отжатии. Я его особо не замечал, потому что в большинстве приложений это не заметно (хотя было что-то подобное лет 10 назад в Firefox).

Однако когда постоянно начал использовать Slack, приложение которого написано на поганом электроне, то этот баг начал прям дико бесить: при переключении раскладки клавиатуры окно теряло фокус. По классике, есть открытый баг в трекере электрона, и даже не один. С одной стороны, вроде проблема не совсем в нем, но с другой — UX это портит конкретно, тем более, что проблема поправима.

Поправить баг на уровне системы можно установкой PPA.

Ссылка

Как запустить что-то после всех тестов

Например, проверку покрытия или отправку отчета куда-нибудь.

Очевидный вариант — сделать кастомную задачу в gradle/sbt/maven, которая оборачивает или перезаписывает старую задачу test. Но если писать таск неохота, то есть и другой вариант, если у вас JUnit5.

Можно сделать кастомный TestExecutionListener, переопределив testPlanExecutionFinished. Чтобы его обнаружил JUnit, добавить полное имя класса в src/test/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener. Такая штука полезна для составления отчета по покрытию, например.

Но, как всегда, есть нюанс: ассерты работать не будут, потому что в движке все вызовы листенеров обернуты в try-catch. Но если нельзя, но очень хочется, то можно пойти на совсем грязный трюк: запустить движок тестов внутри движка тестов:

val launcherConfig = LauncherConfig.builder().enableTestExecutionListenerAutoRegistration(false).build()
val launcher = LauncherFactory.create(launcherConfig)

val request = LauncherDiscoveryRequestBuilder.request()
        .selectors(selectClass(CoverageTest::class.java))
        .build()

launcher.execute(request)

Тут еще отключается обнаружение листенеров, чтобы это не превратилось в бесконечную матрешку. Звучит как извращение, но чего только не сделаешь, лишь бы не писать кастомный таск для maven…

UPD: таск все-таки пришлось написать, с использованием exec-maven-plugin на фазе test, потому что вложенный движок тестов иногда не успевает отрапортовать о результатах до того, как основной завершил свою работу.

Ссылка

Поиск пересечения файлов

Нашел прикольный способ найти пересечение двух файлов:

grep -Fxf file1 file2

выдаст строки, которые есть в обоих файлах, но с нюансом, что из второго файла останутся потенциальные дубликаты (которые можно отсеять через | sort | uniq).

Принцип работы довольно прост: берем паттерны из file1 (-f file1), говорим, что это строки, а не регулярки (-F), и ищем точное совпадение строк (-x) в file2. В интернете пишут, что это быстрее, чем приколы с сортироками и однострочниками на питоне.

Мне это пригодилось без -x, чтобы поискать в логах данные из CSV.

Ссылка

Типы дат java.time

С java.time жить можно, хотя Joda Time у автора получился поудобнее. Памятка про отличия различных типов. TLDR: LocalDateTime с большой вероятностью бэкэндщику не нужен, а OffsetDateTime покроет большинство бытовых кейсов.

Ссылка

Поиск неактуальных веток git

Продолжаю прибираться в проекте. На этот раз цель — избавиться от никому не нужных веток, которые были уже вмержены или забыты.

Главный помощник в этом деле — команда git ls-remote --heads [repoUrl], которая позволяет получить список веток репозитория без его клонирования. Удобно, когда тебе нужно 5+ реп обработать.

Со списком веток на руках можно сделать запрос к баг-трекеру, чтобы получить статус соответствующего тикета (если ветка содержит его идентификатор, разумеется). Мне не повезло и у меня Jira, хорошо хоть к ней есть подобие клиента. А дальше — закрываем ручками то, что в финальном статусе (можно автоматизировать и удаление, но как-то стремно). Полностью скрипт можно посмотреть в gist.

Если надо почистить локальные ветки, которые уже были удалены в удаленном репозитории — есть плагин, который это делает в пару кликов (VCS → Git → Delete old branches…). Правда он скорее всего будет одноразовый: вы же будете держать репу в чистоте после генеральной уборки?

Ссылка

Про Slack

Долгое время я скептически относился к слаку, считая, что телега лучше. И для личного общения/ботов/перекидывания стикерами она все равно лучше. Но для работы слак оказался удобнее. В мае моя команда на него перешла и возвращаться в телегу вряд ли будет.

  1. Киллер-фича — это треды. Особенно, когда в чате дохрена людей и тебе не нужно читать ВСЕ. Вдобавок, когда обсуждается несколько тем одновременно, тележный чат быстро превращается в помойку. 100 непрочитанных сообщений в секунду будут читать только психи. Чаты на 100+ человек в телеге даже более-менее на 1 тему — это рак. Я вообще не понимаю, какая от них польза, особенно когда туда заходят новые люди с типовыми вопросами. Конечно, к тредам надо приучать, но как показала практика, через пару недель игры в злого полицейского останутся только совсем отъявленные “не такие как все”.
  2. Настоящее отключение уведомлений. Не показывать сереньким, не показывать сереньким в архиве, а просто не показывать никак. Полезно для всяких чатов поддержки: понадобилось тебе что-то, зашел, спросил, в треде тебе ответили (и на это приходят уведомления разумеется), потом об этом чате забыл.
  3. Настоящие имена. Мелочь, но до переезда были случаи, когда пишешь кому-то в личку по рабочему вопросу и даже его пола не знаешь, не то что имени.
  4. Выключил рабочий комп = выключил работу. Никаких переписок в полночь о том, что надо бы завтра какую-нибудь фигню сделать.

Но по стикерам иногда скучаешь… Радужные попугаи — это круто, но огромную рожу Гарольда уже не вбросишь.

Ссылка

Дебри рефлексии

Картинка с двумя стульями у меня родилась не просто так. Довольно типичная рабочая ситуация, в которую я вляпался на прошлой неделе. Ближе к концу я уже сел на третий стул пулл-реквестов и сделал их аж три штуки, но расскажу о дебрях, в которые зашел, пытаясь решить проблему через рефлексию.

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

@Override
String exampleMethod(){
    String firstPart = privateMethodToModify();
    String secondPart = super.exampleMethod();
    String thirdPart = anotherPrivateMethod();
    return firstPart + secondPart + thirdPart;
}

Первая реакция будет: “Чё тут делать-то? Отнаследуйся да переопредели!”. Но есть нюанс: super будет уже не тот. А беспокоить дедушек в java нельзя.

Ок, может тогда получим указатели на exampleMethod и anotherPrivateMethod, и в месте вызова ручками все подергаем? Однако полиморфизм будет суров, и exampleMethod вызовется из дочернего класса, даже если указатель вы получали у родителя.

Что же делать? Сдаться и скопипастить дочерний класс, изменив в нем что нужно? Скорее всего да, но если вы хотите сидеть на пиках до конца, то есть еще один способ — MethodHandles. Это новый API, который не является заменой рефлексии, а дополнением к ней, в основном для всяких низкоуровневых манипуляций для разработчиков языков под JVM. С ним можно вызвать родительский exampleMethod для ребенка:

MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Child.class, MethodHandles.lookup());
MethodHandle baseHandle = lookup.findSpecial(Parent.class, "exampleMethod", MethodType.methodType(String.class), Child.class);
return (String) baseHandle.invoke(child);

Но, как всегда, есть нюансы. Во-первых, если java меньше 9 версии, то придется еще и рефлексию использовать, чтобы в приватные методы залезть (выглядит стремно). Во-вторых, в kotlin это работать из коробки пока не будет без специальных флагов, но в 1.4 эти флаги включат по умолчанию. 100% интеропа такие 100%.

Посмотреть примеры подробнее можно github. Познакомиться с API подробнее — тут, а узнать, зачем оно вообще нужно — тут.

Ссылка