/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.notification.internal;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;
import de.rcenvironment.core.communication.api.CommunicationService;
import de.rcenvironment.core.communication.api.PlatformService;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.common.ResolvableNodeId;
import de.rcenvironment.core.notification.DistributedNotificationService;
import de.rcenvironment.core.notification.Notification;
import de.rcenvironment.core.notification.NotificationService;
import de.rcenvironment.core.notification.NotificationSubscriber;
import de.rcenvironment.core.notification.api.RemotableNotificationService;
import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils;
import de.rcenvironment.core.utils.common.ServiceUtils;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.common.rpc.RemoteOperationException;
import de.rcenvironment.toolkit.modules.concurrency.api.AsyncExceptionListener;
import de.rcenvironment.toolkit.modules.concurrency.api.CallablesGroup;
import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription;
/**
* Implementation of {@link DistributedNotificationService}.
*
* @author Doreen Seider
* @author Robert Mischke (8.0.0 id adaptations)
*/
// FIXME clarify behavior on failure: return null, empty collections or throw exceptions? -- misc_ro
// (see related Mantis issue #6542)
public class DistributedNotificationServiceImpl implements DistributedNotificationService {
private static final Log LOGGER = LogFactory.getLog(DistributedNotificationServiceImpl.class);
private static NotificationService notificationService;
private static CommunicationService nullCommunicationService = ServiceUtils.createFailingServiceProxy(CommunicationService.class);
private static CommunicationService communicationService = nullCommunicationService;
private static PlatformService platformService = ServiceUtils.createFailingServiceProxy(PlatformService.class);
protected void activate(BundleContext bundleContext) {}
protected void bindNotificationService(NotificationService newNotificationService) {
notificationService = newNotificationService;
}
protected void bindCommunicationService(CommunicationService newCommunicationService) {
communicationService = newCommunicationService;
}
protected void bindPlatformService(PlatformService newPlatformService) {
platformService = newPlatformService;
}
@Override
public void setBufferSize(String notificationId, int bufferSize) {
notificationService.setBufferSize(notificationId, bufferSize);
}
@Override
public void removePublisher(String notificationId) {
notificationService.removePublisher(notificationId);
}
@Override
public <T extends Serializable> void send(String notificationId, T notificationBody) {
notificationService.send(notificationId, notificationBody);
}
@Override
public Map<String, Long> subscribe(String notificationId, NotificationSubscriber subscriber,
ResolvableNodeId publishPlatform) throws RemoteOperationException {
try {
Pattern.compile(notificationId);
} catch (RuntimeException e) {
LOGGER.error("Notification Id is not a valid RegExp: " + notificationId, e);
throw e;
}
// If publishPlatform is null, insert local ID
if (publishPlatform == null) {
publishPlatform = platformService.getLocalInstanceNodeSessionId();
}
try {
final RemotableNotificationService remoteService = (RemotableNotificationService) communicationService.getRemotableService(
RemotableNotificationService.class, publishPlatform);
return remoteService.subscribe(notificationId, subscriber);
} catch (RemoteOperationException e) {
String message = StringUtils.format("Failed to subscribe to publisher @%s: ", publishPlatform);
throw new RemoteOperationException(message + e.toString());
}
}
@Override
public Map<InstanceNodeSessionId, Map<String, Long>> subscribeToAllReachableNodes(final String notificationId,
final NotificationSubscriber subscriber) {
final Map<InstanceNodeSessionId, Map<String, Long>> missedNumbersMap =
Collections.synchronizedMap(new HashMap<InstanceNodeSessionId, Map<String, Long>>());
// do not filter by "workflow host" flag for now, as components may send out
// notifications from nodes that are not workflow hosts - misc_ro, July 2013
Set<InstanceNodeSessionId> nodesToSubscribeTo = communicationService.getReachableInstanceNodes();
// create the parallel subscription tasks; no return value as results are added to the map
CallablesGroup<Void> callables = ConcurrencyUtils.getFactory().createCallablesGroup(Void.class);
for (final InstanceNodeSessionId nodeId : nodesToSubscribeTo) {
callables.add(new Callable<Void>() {
@Override
@TaskDescription("Distributed notification subscription")
public Void call() throws Exception {
Map<String, Long> missedNumbers = subscribe(notificationId, subscriber, nodeId);
missedNumbersMap.put(nodeId, missedNumbers);
return (Void) null;
}
});
}
callables.executeParallel(new AsyncExceptionListener() {
@Override
public void onAsyncException(Exception e) {
LOGGER.error("Asynchronous exception while subscribing for notification " + notificationId);
}
});
return missedNumbersMap;
}
@Override
public void unsubscribe(String notificationId, NotificationSubscriber subscriber, ResolvableNodeId publishPlatform)
throws RemoteOperationException {
try {
getRemoteNotificationService(publishPlatform).unsubscribe(notificationId, subscriber);
} catch (RuntimeException | RemoteOperationException e) {
throw new RemoteOperationException(
StringUtils.format("Failed to unsubscribe from publisher %s: ", publishPlatform) + e.getMessage());
}
}
@Override
public Map<String, List<Notification>> getNotifications(String notificationId, ResolvableNodeId publishPlatform)
throws RemoteOperationException {
return getRemoteNotificationService(publishPlatform).getNotifications(notificationId);
}
private RemotableNotificationService getRemoteNotificationService(ResolvableNodeId publishPlatform) throws RemoteOperationException {
return (RemotableNotificationService) communicationService.getRemotableService(RemotableNotificationService.class, publishPlatform);
}
}