package de.rwth.idsg.bikeman.ixsi.store;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author Sevket Goekay <goekay@dbis.rwth-aachen.de>
* @since 04.11.2014
*/
public abstract class AbstractSubscriptionStore<T> implements SubscriptionStore<T> {
@Autowired private ScheduledExecutorService executorService;
// Want to get the logger of the extending class and not of this abstract one
protected final Logger log = LoggerFactory.getLogger(getClass());
/**
* Key (T) = ID of the item
* Value (Set<String>) = IDs of the subscribed systems
*/
@Getter
private final ConcurrentHashMap<T, Set<String>> lookupTable = new ConcurrentHashMap<>();
@Override
public void subscribe(String systemID, List<T> itemIDs, Integer expireIntervalinMinutes) {
subscribeInternal(systemID, itemIDs);
scheduleRemove(systemID, itemIDs, expireIntervalinMinutes);
log.debug("System '{}' subscribed to '{}'. This subscription is scheduled to expire in {} minutes",
systemID, itemIDs, expireIntervalinMinutes);
}
@Override
public void subscribe(String systemID, List<T> itemIDs) {
subscribeInternal(systemID, itemIDs);
log.debug("System '{}' subscribed to '{}'", systemID, itemIDs);
}
private void subscribeInternal(String systemID, List<T> itemIDs) {
Set<String> systemIDSet;
for (T itemID : itemIDs) {
systemIDSet = lookupTable.get(itemID);
if (systemIDSet == null) {
systemIDSet = new HashSet<>();
systemIDSet.add(systemID);
lookupTable.put(itemID, systemIDSet);
} else {
systemIDSet.add(systemID);
}
}
}
@Override
public void unsubscribe(String systemID, List<T> itemIDs) {
Set<String> systemIDSet;
for (T itemID : itemIDs) {
systemIDSet = lookupTable.get(itemID);
if (systemIDSet != null) {
systemIDSet.remove(systemID);
}
}
log.debug("System '{}' unsubscribed from '{}'", systemID, itemIDs);
}
@Override
public void unsubscribeAll(String systemID) {
for (Set<String> systemList : lookupTable.values()) {
systemList.remove(systemID);
}
}
@Override
public Set<String> getSubscribedSystems(T itemID) {
Set<String> set = lookupTable.get(itemID);
if (set == null) {
return Collections.emptySet();
} else {
return set;
}
}
@Override
public List<T> getSubscriptions(String systemID) {
List<T> subscriptions = new ArrayList<>();
for (Map.Entry<T, Set<String>> entry : lookupTable.entrySet()) {
if (entry.getValue().contains(systemID)) {
subscriptions.add(entry.getKey());
}
}
return subscriptions;
}
@Override
public void clear() {
lookupTable.clear();
log.debug("Cleared the subscription store");
}
@Override
public String toString() {
return lookupTable.toString();
}
// -------------------------------------------------------------------------
// Schedule to remove subscriptions
// -------------------------------------------------------------------------
private void scheduleRemove(String systemID, List<T> itemIDs, long expireIntervalinMinutes) {
executorService.schedule(new RemoveJob(systemID, itemIDs), expireIntervalinMinutes, TimeUnit.MINUTES);
}
@RequiredArgsConstructor
private class RemoveJob implements Runnable {
private final String systemID;
private final List<T> itemIDs;
@Override
public void run() {
unsubscribe(systemID, itemIDs);
}
}
}