< Sergii Kostenko's blog

Java EE CDI события. Динамический квалификатор.

08 апреля 2019

Java ЕЕ предоставляет нам хороший механизм для обработки событий, который является частью CDI для спецификации Java EE. Динамический CDI спецификатор может быть полезен для обработки событий, например, в domain driven архитектуре или при маршрутизации сообщений веб-сокета и т.д.

Генерация простого события:

@Named
public class MyEventSource {

    @Inject
    private Event<String> myEvent;

    public void fireEvent(){
        myEvent.fire("Hello World!");
    }
}

Наблюдатель события:

@Named
public class MyEventObserver {
    public void observeEvent(@Observes String message){
        System.out.println(message);
    }
}

Используя CDI спецификатор, можно определить, какой наблюдатель должен обработать событие

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Important {
}

Например:

@Named
public class MyEventSource {

    @Inject
    @Important
    private Event<String> myEvent;
    ...
@Named
public class MyEventObserver {
  public void observeEvent(@Observes @Important String message){
    ...

По-умолчанию, событие будет обработано обсервером в текущей транзакции, но можно изменить это поведение, используя @Observes атрибут during

@Named
public class TransactionEventObserver {
    public void observeImportantMessage(@Observes(during = TransactionPhase.AFTER_SUCCESS) String message){
        System.out.println(message);
    }
}

Доступны следующие значения:

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

Итак, сначала нам нужно создать Qualifier с доступными значениями событий

@Qualifier
@Target({METHOD, FIELD, PARAMETER, TYPE})
@Retention(RUNTIME)
public @interface UserEvent {

    Routes value();
}

где Routes - это enum с доступными значениями, например:

public enum Routes {
  LOGIN,
  LOGOUT,
  REGISTRATION
}

Потом нам нужно создать дочерний от javax.enterprise.util.AnnotationLiteral класс для возможности использования квалификатора динамически.

public class UserEventBinding extends AnnotationLiteral<UserEvent> implements UserEvent {

    Routes routes;

    public UserEventBinding(Routes routes) {
        this.routes = routes;
    }

    @Override
    public Routes value() {
        return routes;
    }
}

Теперь давайте сгенерируем событие, используя динамический выбор наблюдателей

@Named
public class UserEventSource {

    @Inject
    private Event<String> userEvent;

    public void fireEvent(Routes route){
        userEvent.select(new UserEventBinding(route)).fire("Instead of string you can use your object");
    }
}

Время показать, как выглядит наш Observer.

import static Routes.*;
...

@Named
public class UserObserver {

    public void registration(@Observes @UserEvent(REGISTRATION) String eventData) {
      ....
    }

    public void login(@Observes @UserEvent(LOGIN) String eventData) {
      ....
    }

    public void logout(@Observes @UserEvent(LOGOUT) String eventData) {
      ....
    }
}

P.S. В Java EE 8 с CDI 2.0 вы можете использовать асинхронные события CDI с помощью метода fireAsync и аннотации @ObserveAsync

Comments


Wildfly обработка больших запросов

26 марта 2019

По умолчанию подсистема Undertow в Wildfly AS настроена на обработку запросов с max-post-size= 10MB. В случае, если ваш запрос, больше, чем 10 МБ, вы получите

java.io.IOException: UT000020: Connection terminated as request was larger than 10485760

Для увеличения этого параметра, можно отредактировать непосредственно standalone или domain конфигурации

<subsystem xmlns="urn:jboss:domain:undertow:3.1">
  <buffer-cache name="default"/>
  <server name="default-server">
    <http-listener name="default" socket-binding="http" max-post-size="15728640" redirect-socket="https" enable-http2="true"/>
    <https-listener name="https" socket-binding="https" max-post-size="15728640" security-realm="SSLRealm"/>
....

или используйте команды CLI как показано ниже:

/subsystem=undertow/server=default-server/http-listener=default/:write-attribute(name=max-post-size,value=15728640)
/subsystem=undertow/server=default-server/https-listener=https/:write-attribute(name=max-post-size,value=15728640)

Заметьте! Если Вы используете Wildfly в доменном режиме с балансировщиком нагрузки AJP, вам также может потребоваться изменить max-post-size для ajp-listener

/subsystem=undertow/server=default-server/ajp-listener=ajp/:write-attribute(name=max-post-size,value=15728640)

Comments


Видео знакомство с Hibernate Search

20 марта 2019

Опубликовано мое второе видео на YouTube (о Hibernate Search и интеграции с Wildfly)!

Comments


SQL хак. Условный COUNT для выражения SELECT

16 марта 2019

Для выполнения условной логики в SQL SELECT, можно использовать выражение CASE. Следующий SQL хак позволит вам выполнить условный count() для вашего запроса.

Например, чтобы выбрать количество записей с id > 5:

SQL:

SELECT SUM(CASE WHEN id > 5 THEN 1 ELSE 0 END) FROM Table

JPQL:

entityManager.createQuery("SELECT SUM(CASE WHEN b.id > 5 THEN 1 ELSE 0 END) FROM BlogEntity b").getSingleResult()

Criteria API:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Number> query = cb.createQuery(Number.class);
Root<BlogEntity> blogEntity = query.from(BlogEntity.class);

query.select(
        cb.sum(
                cb.<Number>selectCase()
                        .when(cb.gt(blogEntity.get("id"), 5), 1)
                        .otherwise(0)
        )
);

Number result = em.createQuery(query).getSingleResult();

Исходный код, доступен на GitHub

Comments


Как поднять ssh тоннель на Linux

15 марта 2019

SSH тоннель позволяет перенаправлять локальный трафик через SSH на удаленный хост. Может быть использован, например, для соедиения с удаленным приложением, которое было запущено на локальном порту удаленного хоста.

Например, чтобы соединиться с mysql, который был запущен на локальном порту 3306 хоста remotehost.com, вы можете использовать:

ssh -L 3366:localhost:3306 [USERNAME]@remotehost.com

или с использованием SSH keys :

ssh -i [KEY_FILENAME] -L 3366:localhost:3306 [USERNAME]@remotehost.com

После этого, вы можете соединиться с базой, используя любимый тулинг на localhost:3366

Comments