/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.component.workflow.execution.api; import java.io.Serializable; import java.util.Deque; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import de.rcenvironment.core.component.execution.api.ConsoleRow; import de.rcenvironment.core.notification.DefaultNotificationSubscriber; import de.rcenvironment.core.notification.Notification; import de.rcenvironment.core.notification.NotificationService; import de.rcenvironment.core.notification.NotificationSubscriber; import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils; import de.rcenvironment.toolkit.modules.concurrency.api.BatchAggregator; import de.rcenvironment.toolkit.modules.concurrency.api.BatchProcessor; import de.rcenvironment.toolkit.modules.concurrency.api.ConcurrencyUtilsFactory; /** * Subscriber implementation that attempts to "catch up" with notifications that were sent before the subscription, but are still buffered * on the sender's side. * * @author Doreen Seider * @author Robert Mischke */ public abstract class GenericSubscriptionEventProcessor extends DefaultNotificationSubscriber { private static final long serialVersionUID = 3619909997095130853L; // configure BatchAggregator for high capacity but low latency to limit the rate of generated update events - misc_ro private static final int LOCAL_NOTIFICATION_BATCH_SIZE_LIMIT = 2000; private static final int LOCAL_NOTIFICATION_BATCH_TIME_LIMIT_MSEC = 200; // protected final transient List<Notification> notificationsToProcess = new LinkedList<Notification>(); private final transient Map<String, Long> lastMissedNotifications = new HashMap<String, Long>(); private final transient Map<String, Boolean> catchingUpWithMissedNotifications = new HashMap<String, Boolean>(); private final transient Map<String, Deque<Notification>> queuedNotifications = new HashMap<String, Deque<Notification>>(); private final transient BatchAggregator<Notification> batchAggregator; // private final transient Log log = LogFactory.getLog(getClass()); public GenericSubscriptionEventProcessor() { final ConcurrencyUtilsFactory factory = ConcurrencyUtils.getFactory(); batchAggregator = factory.createBatchAggregator(LOCAL_NOTIFICATION_BATCH_SIZE_LIMIT, LOCAL_NOTIFICATION_BATCH_TIME_LIMIT_MSEC, new BatchProcessor<Notification>() { @Override public void processBatch(List<Notification> batch) { processCollectedNotifications(batch); } }); } @Override protected void processNotification(Notification notification) { // FIXME catching up missing notifications certainly doesn't work -> rework! // currently, models are initiated very early and thus, it "probably" has no effects - seid_do, July, 2013 String notifId = createIdentifier(notification); if (catchingUpWithMissedNotifications.containsKey(notifId) && catchingUpWithMissedNotifications.get(notifId) && lastMissedNotifications.get(notifId) == NotificationService.NO_MISSED) { queuedNotifications.get(notifId).add(notification); } else if (catchingUpWithMissedNotifications.containsKey(notifId) && catchingUpWithMissedNotifications.get(notifId) && notification.getHeader().getNumber() > lastMissedNotifications.get(notifId)) { queuedNotifications.get(notifId).add(notification); } else { handleIncomingNotification(notification); if (catchingUpWithMissedNotifications.containsKey(notifId) && catchingUpWithMissedNotifications.get(notifId) && notification.getHeader().getNumber() == lastMissedNotifications.get(notifId)) { catchingUpWithMissedNotifications.put(notifId, false); while (!queuedNotifications.get(notifId).isEmpty()) { processNotification(queuedNotifications.get(notifId).pollFirst()); } } } } private String createIdentifier(Notification notification) { return createIdentifier(notification.getHeader().getNotificationIdentifier(), notification.getHeader().getPublishPlatform().getInstanceNodeSessionIdString()); } private String createIdentifier(String notifId, String nodeId) { return notifId + nodeId; } private void handleIncomingNotification(Notification notification) { // enqueue received row for batch processing batchAggregator.enqueue(notification); } /** * Process all collected {@link ConsoleRow} updates and perform a single GUI update to improve performance. */ protected abstract void processCollectedNotifications(List<Notification> notifications); @Override public Class<? extends Serializable> getInterface() { return NotificationSubscriber.class; } /** * Registers a notification type for handling past notifications of that type. * * @param notifId identifier of the notification * @param nodeId identifier of source node * @param lastMissedNumber number of last missed notification. */ public void setNumberOfLastMissingNotification(String notifId, String nodeId, Long lastMissedNumber) { String notificationId = createIdentifier(notifId, nodeId); queuedNotifications.put(notificationId, new LinkedList<Notification>()); lastMissedNotifications.put(notificationId, lastMissedNumber); catchingUpWithMissedNotifications.put(notificationId, true); } /** * Flushes all pending notifications. */ public void flush() { // TODO review @5.0: does this need a replacement after subscriber class changes? add method to flush the batchProcessor? // processCollectedNotifications(); } }