Условный обработчик в потоке интеграции

Я разрабатываю службу уведомлений веб-крючков, которая позволяет клиентам подписываться/отписываться от сообщений, проходящих через промежуточное ПО, и получать уведомления о сообщениях (в соответствии с предоставленными критериями), отправляя полезную нагрузку сообщения на предоставленный URL-адрес обратного вызова. Доставка сообщения выглядит так:

flowBuilder
    .enrichHeaders(e->e.header(MessageHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE,true))
    .handle(Http.outboundChannelAdapter(message-> {
              String subscriptionId = message.getHeaders().get(SUBSCRIPTION_ID_HEADER_NAME, String.class);
                return subscriptionsStore.get(UUID.fromString(subscriptionId)).getCallbackUrl(); //potential NPE if subscription was removed 

            },restTemplateBuilder.build())

.get()

Как видите, реализация uriFunction извлекает URL-адрес обратного вызова из subscriptionsStore по идентификатору подписки (часть заголовка сообщения).

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

Я знаю, что могу фильтровать сообщения с идентификатором подписки, которые все еще присутствуют в хранилище подписок, но это не правильное решение, так как клиент может отказаться от подписки между операциями filter и handle, все еще вызывая NRE в uriFunction.

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

Я могу подумать о другом подходе: вычислить URI несуществующих подписок как некоторое статическое значение и добавить перехватчик к RestTempalte для имитации воспроизведения HTTP OK для этого конкретного значения URI...

Итак, мой вопрос о правильном способе обработки этого случая с использованием стандартного EIP или другой функции интеграции Spring, о которой я не знаю...

Спасибо

ОБНОВЛЕНИЕ

Я добавил класс DedicatedMessage, который содержит контекст:

public static class DedicatedMessage  extends GenericMessage<Object> implements MessageDecorator{
        @Getter
        @Transient
        private Subscription subscription;


        public DedicatedMessage(Subscription subscription,Object payload,Map<String,Object> headers) {
            super(payload,headers);
            this.subscription = subscription;
        }

        @Override
        public Message<?> decorateMessage(Message<?> message) {
            return new DedicatedMessage (subscription,message.getPayload(),message.getHeaders());
        }
    }

и изменил поток как:

flowBuilder
   .enrichHeaders(e->e.header(MessageHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE,true))
  .handle((payload, headers) -> {
                        String subscriptionId = (String) headers.get(SUBSCRIPTION_ID_HEADER_NAME);
                        Subscription subscription = subscriptionsCache.get(UUID.fromString(subscriptionId));
                        return Optional.ofNullable(subscription)
                                .map(s->  new DedicatedMessage(s, payload,headers))
                                .orElse(null);
                    })
  .handle(Http.outboundChannelAdapter(message->((DedicatedMessage)message).getSubscription().getCallbackUrl()
                            ,restTemplateBuilder.build())
.get()

Есть проблемы с этим подходом?


person Alexander.Furer    schedule 05.11.2017    source источник


Ответы (1)


Я не уверен, что означает ваша аббревиатура NRE, но вы можете выбросить NoSuchSubscriptionException из своего метода subscriptionsStore.get(), а затем проигнорировать/сообщить об этом исключении в ExpressionEvaluatingRequestHandlerAdvice, примененном к адаптеру исходящего канала в его цепочке endpoint.advice().

person Gary Russell    schedule 05.11.2017
comment
Исключение Null Reference Exception, извините, должно было быть NPE, выбрасываемым, когда subscribeStore.get(id) возвращает null. Я постараюсь применить совет и обработать исключение там - person Alexander.Furer; 05.11.2017
comment
Вам нужно обернуть get() и выдать исключение, если оно возвращает null. - person Gary Russell; 06.11.2017
comment
Да, конечно, завтра попробую ваш совет. Но не думаете ли вы, что условный обработчик может быть кандидатом на включение в интеграцию Spring? - person Alexander.Furer; 06.11.2017
comment
Непонятно, что вы имеете в виду - как вы говорите, если мы добавим условие, все равно будет состояние гонки, в котором подписка может быть удалена между проверкой условия и вызовом обработчика. Это просто сокращает временное окно по сравнению с использованием фильтра, но не устраняет его. - person Gary Russell; 06.11.2017
comment
Что ж, условный обработчик мог бы быть немного умнее, предоставив «контекст» обернутому обработчику: context=getContext(message);if(test the context) {targetHandler.handle(message, context)}... - person Alexander.Furer; 06.11.2017
comment
Я понимаю, что для этого потребуются изменения API-интерфейса обработчика, но контекст может быть передан, например, через локальный поток. - person Alexander.Furer; 06.11.2017
comment
Я обновил свой вопрос другим возможным решением, есть ли там подводные камни? - person Alexander.Furer; 06.11.2017