package eu.lod2.rsine.queryhandling; import eu.lod2.rsine.dissemination.notifier.INotifier; import eu.lod2.rsine.queryhandling.policies.IEvaluationPolicy; import eu.lod2.rsine.queryhandling.policies.ImmediateEvaluationPolicy; import eu.lod2.rsine.registrationservice.NotificationQuery; import eu.lod2.rsine.registrationservice.RegistrationService; import eu.lod2.rsine.registrationservice.Subscription; import org.openrdf.OpenRDFException; import org.openrdf.repository.RepositoryException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Component public class QueryDispatcher implements IQueryDispatcher { private final Logger logger = LoggerFactory.getLogger(QueryDispatcher.class); private final int NUM_NOTIFY_THREADS = 15; @Autowired private RegistrationService registrationService; @Autowired private QueryEvaluator queryEvaluator; @Autowired private PostponedQueryHandler postponedQueryHandler; @Autowired private IEvaluationPolicy evaluationPolicy; private ExecutorService notificationExecutor = Executors.newFixedThreadPool(NUM_NOTIFY_THREADS); private boolean asyncNotification; public QueryDispatcher() { } public QueryDispatcher(boolean asyncNotification) { this.asyncNotification = asyncNotification; } @Override public void trigger() { Iterator<Subscription> subscriptionIt = registrationService.getSubscriptionIterator(); if (!subscriptionIt.hasNext()) { logger.info("No subscribers registered"); } try { while (subscriptionIt.hasNext()) { dispatchForSubscription(subscriptionIt.next()); } } catch (RepositoryException e) { logger.error("Error issuing query", e); } } private void dispatchForSubscription(Subscription subscription) throws RepositoryException { logger.debug("Dispatching queries for subscription with id '" + subscription.getId() + "'"); Iterator<NotificationQuery> queryIt = subscription.getQueries(); while (queryIt.hasNext()) { issueQueryAndNotify(queryIt.next()); } } public synchronized void issueQueryAndNotify(NotificationQuery query) throws RepositoryException { issueQueryAndNotify(query, false); } public synchronized void issueQueryAndNotify(NotificationQuery query, boolean evaluateImmediately) throws RepositoryException { List<String> messages = new ArrayList<String>(); try { Collection<String> queryMessages = queryEvaluator.evaluate( query, evaluateImmediately ? new ImmediateEvaluationPolicy() : evaluationPolicy); messages.addAll(queryMessages); if (!messages.isEmpty()) { appendSubscriptionDetails(messages, query.getSubscription()); sendNotifications(messages, query.getSubscription()); } postponedQueryHandler.remove(query); } catch (OpenRDFException e) { logger.error("Could not evaluate query", e); } catch (EvaluationPostponedException e) { postponedQueryHandler.add(query); } } private void sendNotifications(Collection<String> messages, Subscription subscription) { Iterator<INotifier> notifierIt = subscription.getNotifierIterator(); while (notifierIt.hasNext()) { INotifier notifier = notifierIt.next(); if (asyncNotification) { notificationExecutor.execute(new Notification(notifier, messages)); } else { notifier.notify(messages); } } } private void appendSubscriptionDetails(Collection<String> messages, Subscription subscription) { String rationale = "You receive this notification because of subscription '" +subscription.getId()+ "'"; if (!subscription.getDescription().isEmpty()) { rationale += " (" +subscription.getDescription()+ ")"; } messages.add(rationale); } private class Notification implements Runnable { private INotifier notifier; private Collection<String> messages; private Notification(INotifier notifier, Collection<String> messages) { this.notifier = notifier; this.messages = messages; } @Override public void run() { notifier.notify(messages); } } }