All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.esbtools.eventhandler.PollingNotificationProcessorRoute Maven / Gradle / Ivy

/*
 *  Copyright 2016 esbtools Contributors and/or its affiliates.
 *
 *  This file is part of esbtools.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see .
 */

package org.esbtools.eventhandler;

import com.google.common.util.concurrent.Futures;
import org.apache.camel.builder.RouteBuilder;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class PollingNotificationProcessorRoute extends RouteBuilder {
    private final NotificationRepository notificationRepository;
    private final DocumentEventRepository documentEventRepository;
    private final Duration pollingInterval;
    private final int batchSize;

    private static final AtomicInteger idCounter = new AtomicInteger(1);
    private final int id = idCounter.getAndIncrement();

    public PollingNotificationProcessorRoute(NotificationRepository notificationRepository,
            DocumentEventRepository documentEventRepository, Duration pollingInterval,
            int batchSize) {
        this.notificationRepository = notificationRepository;
        this.documentEventRepository = documentEventRepository;
        this.pollingInterval = pollingInterval;
        this.batchSize = batchSize;
    }

    @Override
    public void configure() throws Exception {
        from("timer:pollForNotifications" + id + "?period=" + pollingInterval.toMillis())
        .routeId("notificationProcessor-" + id)
        .process(exchange -> {
            List notifications =
                    notificationRepository.retrieveOldestNotificationsUpTo(batchSize);
            Map>> notificationsToFutureEvents =
                    new HashMap<>(notifications.size());

            // Intentionally cache all futures before waiting for any.
            for (Notification notification : notifications) {
                try {
                    Future> futureEvents = notification.toDocumentEvents();
                    notificationsToFutureEvents.put(notification, futureEvents);
                } catch (Exception e) {
                    log.error("Failed to get future document events for notification: " +
                            notification, e);
                    notificationsToFutureEvents.put(notification, Futures.immediateFailedFuture(e));
                }
            }

            Map> notificationsToDocumentEvents =
                    new HashMap<>();
            List failedNotifications = new ArrayList<>();

            for (Entry>> notificationToFutureEvents
                    : notificationsToFutureEvents.entrySet()) {
                Notification notification = notificationToFutureEvents.getKey();
                Future> futureEvents =
                        notificationToFutureEvents.getValue();
                try {
                    Collection events = futureEvents.get();
                    notificationsToDocumentEvents.put(notification, events);
                } catch (ExecutionException | InterruptedException e) {
                    log.error("Failed to get document events for notification: " + notification, e);
                    failedNotifications.add(new FailedNotification(notification, e));
                }
            }

            Iterator>> notificationsToEventsIterator =
                    notificationsToDocumentEvents.entrySet().iterator();
            while (notificationsToEventsIterator.hasNext()) {
                Entry> notificationToEvents =
                        notificationsToEventsIterator.next();
                try {
                    notificationRepository.ensureTransactionActive(notificationToEvents.getKey());
                } catch (Exception e) {
                    notificationsToEventsIterator.remove();
                    if (log.isWarnEnabled()) {
                        log.warn("Notification transaction no longer active, not processing: " +
                                notificationToEvents.getKey(), e);
                    }
                }
            }

            List documentEvents = notificationsToDocumentEvents.values()
                    .stream()
                    .flatMap(Collection::stream)
                    .collect(Collectors.toList());

            log.debug("Persisting {} document events via route {}: {}",
                    documentEvents.size(), exchange.getFromRouteId(), documentEvents);

            try {
                documentEventRepository.addNewDocumentEvents(documentEvents);
            } catch (Exception e) {
                log.error("Failed to persist new document events from notifications. Rolling " +
                        "back processing. Document events were: " + documentEvents, e);
                notificationsToDocumentEvents.clear();
            }

            notificationRepository.markNotificationsProcessedOrFailed(
                    notificationsToDocumentEvents.keySet(), failedNotifications);
        });
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy