All streams
Search
Write a publication
Pull to refresh
2
0
Alexander A. Kropotin @ololx

Java Developer

Send message

Подавал через госуслуги, но приходил отказ по причине проблем взаимодействия межведомственных сервисов; но была назначена дата приема для разъяснения отказа. Вот на этом приеме мой представитель передал распечатанные (не заверенные) сканы документов, которые я прислал в электронном виде и снял меня с учета без вопросов.
Как выяснилось - они (мой военкомат) специально шлют отказ, чтобы все приходили лично и разбираться так проще)

Я пользовался - сделал там зеркала парочки реп с Github. Некоторые элементы интерфейса имеют странный перевод («опасность» например, в настройках репозитория); Но зато, зеркалирование там бесплатное - (по крайней мере на данный момент), тех поддержка отвечает на письма, интерфейс по сути как дословно переведенный GitHub. В целом для моих задач - отличный сервис.

Позвольте вмешаться, про инъекции с вами согласен, но не в этом конкретном примере. У автора метод принимает коллекцию из различных Long и скорее всего в этом случае не найдется никакого long значения = «1);DROP DATABASE;--».

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

class AnalyzeTaskListener implements TaskListener {
    
    private final TestMethodTreePathScanner testMethodTreePathScanner;

    AnalyzeTaskListener() {}

    @Override
    public void started(TaskEvent taskEvent) {

    }

    @Override
    public void finished(final TaskEvent e) {
        if (e.getKind() != TaskEvent.Kind.ANALYZE) {
            return;
        }

	CompilationUnitTree compilationUnit = e.getCompilationUnit();
        if (compilationUnit == null) {
	    return;
        }

        compilationUnit.accept(testMethodTreePathScanner, null);
    }
}

Благодарю за хороший ответ. Точно также, как это реализовано у Вас сейчас, с небольшими изменениями. Вы создаете новый TaskListener в методе init() Вашего процессора; я лишь хотел показать, что инициализировать TaskListener можно иначе, тогда AbstractProcessor не нужен. К сожалению сейчас не обладаю достаточным временем, чтобы написать что-то конкретное по Вашему примеру. Но, в общих словах:
Сейчас логика поиска нужных элементов по аннотирпованным классам и реализация проверки у Вас реуализована в SwitchExhaustiveCheckerProcessor и TestMethodTreePathScanner
Можете просто объединить эту логику в одном Вашем TestMethodTreePathScanner - это конечно очень некрасиво и лучше бы потом отрефакторить и разбить ответственности, но зато позволит быстрее опробовать другой подход. 
Далее можно в самом AnalyzeTaskListener имплементить визитера TestMethodTreePathScanner к каждому элементу и все должно быть ОК, хотя чую, что с первого раза все не заведется, учитывая что пока предлдожение абстрактное.

class AnalyzeTaskListener implements TaskListener {
    
private final SwitchExhaustiveCheckerProcessor processor;

    AnalyzeTaskListener() {}

    @Override
    public void started(TaskEvent taskEvent) {

    }

    @Override
    public void finished(final TaskEvent e) {
        if (e.getKind() != TaskEvent.Kind.ANALYZE) {
            return;
        }

	CompilationUnitTree compilationUnit = event.getCompilationUnit();
        if (compilationUnit == null) {
	    return;
        }

        compilationUnit.accept(scanningVisitor, null);
    }
}

А в самом плагине уже проинициализировать AnalyzeTaskListener как писали ранее. Если нужны какие-нибудь утилитные класссы, наподобии тех , что используются у Вас в процессоре или визитере, то можно в классе плагина их проинициализировать и передать через конструктор AnalyzeTaskListener далее.
Например ``

…
@Override
    public void init(JavacTask task, String... args) {
        final Context context = ((BasicJavacTask)task).getContext();
        Log log = Log.instance(context);
    }
...

Тут есть хорошие примеры кода для такого подхода - https://annimon.com/article/2626

Приветствую!
Спасибо за статью.
Интересная проблема и решение через обработку аннотаций)
В свое время тоже столкнулся с подобной проблемой поведения AnnotationProcessor и реализовал через такой же хак, чтобы достучатся до переменных внутри методов в одном пет проекте - https://ololx.github.io/cranberry/cranberry-statement/.

Позвольте мне немного дополнить статью и задать вопрос.

В Java 8+ можно зарегистрировать кастомный TaskListener через свою реализацию com.sun.source.util.Plugin; В вашем случае это выглядело бы следующим образом:
1 - Написать свою реализацию Plugin вместо SwitchExhaustiveCheckerProcessor

public class SwitchExhaustiveChecker implements Plugin {

    @Override
    public String getName() {
        return "SwitchExhaustiveCheckerPlugin";
    }

    @Override
    public void init(JavacTask task, String... args) {
	task.addTaskListener(new AnalyzeTaskListener());
    }
}

2 - Зарегистрировать свой плагин в …/src/main/resources/META-INF/services/com.sun.source.util.Plugin ( аналогично тому, как Вы регистрировали свой процессор)

ru.hixon.switchexhaustivenesschecker.SwitchExhaustiveCheckerPlugin

3 - Для реализации такого плагина потребуется еще указать зависимость на JDK tools в Вашем pom.xml

<dependency>
    <groupId>com.sun</groupId>
    <artifactId>tools</artifactId>
    <version>1.8.0</version>
    <scope>system</scope>
    <systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>

После чего можно использовать Ваш плагин, подключив его к проекту и указав аргумент в maven-compiler-plugin

<plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>${version}</version>
          <configuration>
            <compilerArgs>
              <arg>-Xplugin:SwitchExhaustiveCheckerPlugin</arg>
            </compilerArgs>
          </configuration>
        </plugin>

Это пример подобного плагина, который я смог найти на github (надеюсь, что авто рне будет против за то, что выложил лине) - https://github.com/moditect/deptective

Вопрос: Рассматривали ли Вы описанный мной способ реализации Вашей задачи? Если - да, то почему остановились на своем варианте - чем он показался Вам выигрышней?

Скорее всего ничего, как и с автомобилями, которые были выпущены и куплены ранее под стандарты ЕВРО-2, и эксплуатируются при текущем ЕВРО-5.

Все куда проще - именно продюсера на spring-cloud-stream который вызывался непосредственно после успешной манипуляции с сущностью; написал ДТО по образу того, что есть в debezium (поля T before, T after + метод, который по соотношению значений полей определяет тип события - delete, update и create). Что хорошего в такой реализации - можно по необходимости Google Protobuff применить.

Получается при возникновении события в сервисе (например создании сущности) создается инстанс ДТО, с before = null, after = объект; ДТО поститься в кафку; ну а далее уже консьюмеры отрабатывают. На тот момент это было быстрее, чем разбираться с debezium до конца.

Спасибо за статью, очень полезная. Года 2 назад уже рассматривал debezium, с СУБД mysql все работало хорошо по мануалам, но с postgresql не получилось никак реализовать CDC. Поэтому писал реализацию CDC уже сам под каждый конкретный сервис - у сервиса (master data) пишется продюсер для кафки, а у зависимых (slave) сервисов уже консьюмеры.

Но благодаря Вашей статье решил попробовать debezium снова, тем более за два года могло многое измениться)

Недавно наткнулся еще и на embedded реализацию debezium и начал изучать этот вариант. Если интересно начало проекта тут - https://github.com/innopolis-university-java-team/change-data-capture-instances.

Да, при помощи open tracing, настроил отправку данных трассировки на развернутый сервис Jaeger. А сам Jaeger подключил как datasource в Graphana; по мимо Jaeger просто собираем статы с prometheus и loki.

Пока разбирался, сделал небольшой пет проект на эту тему для себя еще; если интересно, то он доступен тут - https://github.com/innopolis-university-java-team/spring-boot-metrics-instances

Благодарю, гляну, пока подключил postgres как datasource и создавал свои графики на основании служебных таблиц.

Спасибо за статью, как раз прикручиваю графики для postgres (не через Prometheus) в Graphana. Будет очень полезно.

Сразу вопрос: если уже настроена трассировка через тот же Jaeger, то чем этот способ лучше? В Jaeger же можно видеть запросы и их время выполнения.

Согласен, но в поддержку автора добавлю, что у меня был опыт работы с «обиженной крошкой»; правда не из-за ревью, но суть в том, что открытый конфликт реально помог, когда мы друг другу высказали все в лицо) после чего перекурили и спокойно продолжили работать без всяких обид. Это было на стадии притирки команды, но иногда такие вот открытые споры и даже конфликты намного лучше постоянно пассивной агрессии от коллеги; причем когда еще и из подтяжка)

Интересная статья, был опыт реализации REST-сервиса для индексов на Sphinx для поиска адреса с опечатками.
Спасибо автору за осознание того, что я мог выбросит на помойку свой REST, поскольку Sphinx может столько же и даже лучше =)

На самом деле можно реализовать еще проще: как Optional + Jackson ObjectMapper для простого частичного обновления сущности из ДТО.
У меня есть подробный пример реализации метода PATCH при помощи такого подхода здесь https://github.com/ololx/restful-updating-instances; если Вам будет интересно.


В Вашем случае, это может выглядеть так:
Сущность (без аннотаций Hibernate):


public class UserModel {

    private long id;

    @JsonProperty("login")
    private String login;

    @JsonProperty("first_name")
    private String firstName;

    @JsonProperty("last_name")
    private String lastName;

    @JsonProperty("birth_date")
    private String birthDate;

    @JsonProperty("email")
    private String email;

    @JsonProperty("phone_number")
    private String phoneNumber;
}

ДТО для этой сущности:


@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserDetail {

    @JsonProperty("login")
    private Optional<String> login;

    @JsonProperty("first_name")
    private Optional<String> firstName;

    @JsonProperty("last_name")
    private Optional<String> lastName;

    @JsonProperty("birth_date")
    private Optional<String> birthDate;

    @JsonProperty("email")
    private Optional<String> email;

    @JsonProperty("phone_number")
    private Optional<String> phoneNumber;
}

Кусок реализации метода PATCH:


    @SneakyThrows
    @PatchMapping(
            path = "/{id}"
    )
    public UserDetail update(
            @ApiParam(
                    name="id",
                    value = "The update user entity id",
                    required = true
            ) @PathVariable Long id,
            @ApiParam(
                    name="updateUserRequest",
                    value = "The update user request",
                    required = true
            ) @RequestBody UserDetail updateUserRequest) {
        UserModel user = this.userRepository.findById(id).orElse(null);
        assertNotNull(user, String.format("User with id - {} is not existed", id));

        objectMapper.updateValue(user, updateUserRequest);
        this.userRepository.save(user);

        UserDetail updateUserResponse = new UserDetail();
        objectMapper.updateValue(updateUserResponse, user);

        return updateUserResponse;
    }

Где userRepository — обычный JpaRepository; а objectMapper — стандартный ObjectMapper.


    @Autowired 
    PersonRepository peopleRepository;

    @Autowired
    ObjectMapper objectMapper;
Поддерживаю. Сами используем такой способ.

Information

Rating
Does not participate
Location
Пафос, Government controlled area, Кипр
Date of birth
Registered
Activity