Попал в подкаст «За чашкой чая», эпизод 16. Можно слушать на любых доступных платформах.

Попал в подкаст «За чашкой чая», эпизод 16. Можно слушать на любых доступных платформах: Anchor, Apple, Yandex.

Под катом текстовая расшифровка моей части. Хотите — слушайте, хотите — читайте.

(далее…)

Внедрение вредоносной команды интерпретатора (ABAP)

Эффект старой плёнки.

Есть в SAP HCM стандартная транзакция PAR1 (Гибкие данные сотрудников). Но при внедрении у пользователей были хотелки, которые в стандарте невозможны. Скопировали транзакцию в Z-область. Допилили её так, как хотелось пользователям. Добавили инфотипы, ещё кое-что по мелочи. Дело нехитрое.

Затемнение.

Прошло десять лет.

(далее…)

Про Shared Memory Objects и немного про память, SHMA, SHMM

В обычной жизни мы не паримся с памятью в ABAP, сборщика мусора как бы нет, глобальной памяти нет.

В самом простом случае у нас есть то, что называется транзакцией (не путать с транзакцией базы данных), что можно определить как «приложение» на нашем собственно говоря сервере приложений. Транзакция всегда ассоциирована с определённой именованной программой.

И вот пока приложение работает, оно потребляет память в виде переменных, экземпляров классов, массивов (также известных как «внутренние таблицы»). И если посмотреть на стек вызовов, то он упирается во входную точку начала этой программы. Ничего ABAPовского за пределами этой точки не существует. Одно приложение не может получить доступ к данным другого приложения, только через какие-то обходные манёвры, например: через постоянное хранилище (СУБД), через взаимные блокировки и прочие костыли. Как только приложение завершает работу, вся его память высвобождается. А приложения долго не работают, потому что они уже в некоторой степени атомарны (то есть как бы уже не монолит, но ещё не микросервисы).

Есть уже случае чуть посложнее, например: есть веб-сервис, слушающий входящие соединения, или одно «приложение» вызывает другое «приложение» через SUBMIT REPORT . Но подход вы уже уловили, это и называется SAP LUW, то есть Logical Unit of Work. Получается, что стек вызовов и контекст существуют неразрывно только внутри него.

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

Один из известных костылей -оператор IMPORT/EXPORT MEMORY ID,  который позволяет перебрасывать переменные (структуры, массивы) между приложениями, но только если они существуют в одной пользовательской сессии. Например Приложение-А вызывает Приложение-Б, Приложение-Б генерирует данные, закидывает их в ячейку памяти X, после чего завершает работу, управление возвращается в Приложение-А, которое считывает данные из ячейки памяти X, профит!

Разговор был бы не так интересен, если бы мы остановились на этом. Но перепрыгнув забор, мы обнаруживаем ещё один забор!

А если мы хотим перебросить данные между разными пользовательскими сессиями?

Для примера кейс будет таким: есть чистый слушающий HTTP-сервис, в котором мы хотим организовать что-то вроде сессии.

HTTP-сессии в ABAP могут быть как stateless, так и stateful. По сути их отличие в том, что этот пресловутый SAP LUW растягивается на всю пользовательскую сессию (stateful) или только на один http-вызов (stateless). В stateful-режиме сервис работает со своими причудами, например: обновлённый код сервиса не подхватывается на лету, чтобы изменения в сервисе вступили в силу нужно завершить сессию. Так же и в обычном ABAPe: пока запущена программа со всеми диалоговыми шагами обновлённый код не подхватывается — необходимо перезайти в транзакцию. Получается в рамках HTTP необходимо сделать log-off, что очень редко случается целенаправленно в жизненных реалиях, гораздо чаще сессия умирает по таймауту.

И вот кривая дорожка сомнительных архитектурных решений привела нас к небольшому челенджу — организовать хранение данных сессии при stateless-сессии HTTP.

Окей, вызов принят. Представим типичный сценарий:

  • Пользователь первый раз заходит на страницу => нет куки сессии => выставляем новый случайный куки (надо его куда-то записать) => рендерим страницу с формой для пользователя => Всё закончилось и очистилось, развязка сезона
  • Ура новый сезон => Пользователь что-то сабмитит => открывается другая страница => есть куки сессии => (надо значения сессии достать) => проверить что пользователь насабмиттил => (положить насабмитенное в сессию) => отрендерить страницу для пользователя, что насабмитилось успешно => снова апокалипсис, миру конец
  • Пользователь чего-то тыкнул => ещё одна страница => есть куки сессии => (надо значения сессии достать) => отрендерить значения на странице => и непонятно, продлит ли пользователь этот бесконечный сериал на следующий сезон или нет

Обыкновенным ABAPом красные места можно тупо реализовать через БД или другой как-мы говорим-у нас-в-деревне «персистент сторэдж», мы же это умеем. Но мы же дошли досюда не за простыми решениями.

Существует возможность создать экземпляр объекта и положить его в какую-то суперглобальную область памяти, существующую вечно внутри сервера приложений. Соответственно, в любой момент можно попробовать взять этот объект во временное пользование (на чтение или на изменение). И это будет тот-же-самый-объект со всеми его атрибутами. Он будет существовать до перезагрузки сервера или до его ручного уничтожения через кнопку в админке (SHMM). Вот вкратце история. А под катом немного подробностей.

(далее…)

Немного NoSQL или что-то взамен SM30

При разработке часто бывает необходимость вести некоторые настройки или конфигурации.

На первом уровне это хардкод внутри программы:

  • написанный напрямую в тексте программы;
  • вынесенный в константы или блок инициализации;
  • оформленный в виде статического атрибута глобального класса.

Хардкод вполне оправдан, если:

  • в течении всего жизненного цикла эти настройки не будут меняться;
  • не будет кейсов, когда именение настройки может быть сделано без изменения связанного исходного кода.

Вторым уровнем может возникнуть классическая настроечная таблица + сгенерированное ведение в транзакции SM30. Впрочем тут тоже можно сделать шаг вправо и влево:

  • кучу таблиц можно объединить в кластер SM34;
  • ведение можно привязать к коду транзакции;
  • ведение можно добавить в SPRO;
  • таблицы могут быть зависимые/независимые от манданта;
  • таблицы могут быть частью настройки или вестить непосредственно в продуктивной системе.

Такие настройки уже вполне можно передать в руки настройщиков и пользователей.

Доходит иногда до смешного, даже в стандарте можно наблюдать целые настроечные таблицы в виде одной колонки («Активировать»), соответственно в таблице только одна строка со значеним — флажок стоит или нет.

Третьим уровнем могут часто возникать разные велосипеды для организации небольших настроек.

Часто можно заметить, что в системе есть много разных стандартных узкоспециализированных хранилищ, которые можно использовать в собственных корыстных целях, например:

  • глобальные переменные STVARV;
  • рабочие списки (OB55 и похожие);
  • версии баланса (почему бы и не);
  • деривация (ну если уж так);
  • кастомные атрибуты объектов, если позволено.

Но некоторые приходят к мысли, что следует разработать некоторое универсальное хранилище настроек (Z-разработка), вроде большой таблицы в стиле ключ-значение или даже нечто похожего на реестр Windows, навскидку:

Добро пожаловать под кат, представляю вам свой велосипед, там есть пара занятных моментов. Почему свой? Да потому что у остальных есть фатальный недостаток, очевидно. (далее…)

LOOP AT SCREEN, работа с экраном — новая идея

Устав от простыней в стиле:

я решил обкатать новый подход.

Первым делом декларативное определение, например:

Применение на практике сводится к следующему:

Пробую пока покатать-проверить, насколько такой подход применим в условиях, приближенным к боевым. Нужно больше мест применения, чтобы определить какой подход даст лучшую читаемость, компактность и проверяемость. И потихотньку можно добавить мяса.

(далее…)

Про перекладывание плитки

Раннее утро. Птички чирикают.

Сижу, никого не трогаю, читаю утреннюю газету.

SAP вещает:

сморите как всё было плохо, какое всё разное, инконсистенси, то да сё,  фу-фу-фу

вот мы вам сделаем Fiori 3, там будет всё такое консистентное, прям ух-ты-красота

современные технологии, двадцать первый век, а о цене давайте не будем говорить

И неожиданно нахлынуло чувство какой-то органичной целостности этого мира.
(далее…)

Телега впереди лошади

Простая иллюстрация подхода при передаче параметра  в виде ссылки:

Чего можно таким добиться?

Например: это маленькое ухищрение позволяет однократно указывать обрабатываемую таблицу. Это делает код чуть менее многословным. Альтернатива:

Насколько это красиво? Чисто и просто… Впрочем, есть тут некоторая неочевидность. Мы запускаем метод в классе без указния локальной переменной в качестве параметра, а эта локальная переменная меняется.

Если собирать ошибки внутри вспомогательного класса, а потом их получать вместе, то становится сложнее прекратить обработку немедленно. Выглядит как-то корявенько:

Пример реализации:

(далее…)

Достигая невозможного, пьеса

Все события и герои вымышлены. Любые совпадения с реальными личностями случайны.

Акт первый

Консультант: Нам нужно массово списать материалы, но так чтобы в оборотах это было отражено в особом периоде.

Старый фрилансер: Ну дело житейское, разработчика своего попросите.

Консультант: Мы спрашивали, но он сказал, что это невозможно.

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

Консультант: Посмотри, пожалуйста!

Старый фрилансер: Не вопрос, но задачку-то поставь. (далее…)

Быстрый способ получить данные из XML-файла

Как это часто бывает, есть много разных способов решить одну и ту же задачу. И на этот счет тоже есть варианты:

  • Разбирать текст руками или регулярными выражениями:  плохой подход сходу, не обсуждается
  • CL_XML_DOCUMENT: классический подход, но несколько муторный на мой вкус
  • CALL TRANSFORMATION: стандартный подход, но меня всегда смущает небходимость наличия отдельного артефакта. Кроме того, если работать с этим раз в год, то синтаксис трансформаций выпадает из памяти и необходимо снова обращаться к примерам и/или документации

Новый для меня способ — использовать функциональный модуль SMUM_XML_PARSE, у которого очень простой синтаксис: (далее…)

Грязные трюки при работе с ALV

Дано: есть вывод отчета в ALV, в котором стандартными средствами включен расчет среднего значения.

Итоговое среднее значение стандартно расчитывается так:

( 30 + 40 + 50 + 0 ) / 4 = 30

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

Соответственно от заказчика есть задание: требуется выводить среднее значение, не учитывающее пустые (нулевые) значения:

Один из способов решения задачи — грязно вторгнуться во внутреннюю кухню ALV. Приступим?

(далее…)