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 extends Notification> 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);
});
}
}