Java tutorial
/* * Copyright (C) 2015 Orange * * This software is distributed under the terms and conditions of the 'GNU GENERAL PUBLIC LICENSE * Version 2' license which can be found in the file 'LICENSE.txt' in this package distribution or * at 'http://www.gnu.org/licenses/gpl-2.0-standalone.html'. */ package com.orange.cepheus.broker; import com.fasterxml.jackson.core.JsonProcessingException; import com.orange.cepheus.broker.exception.SubscriptionException; import com.orange.cepheus.broker.exception.SubscriptionPersistenceException; import com.orange.cepheus.broker.model.Subscription; import com.orange.cepheus.broker.persistence.SubscriptionsRepository; import com.orange.ngsi.model.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.xml.datatype.DatatypeFactory; import java.time.Duration; import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import java.util.regex.PatternSyntaxException; /** * Handles subscriptions. */ @Component public class Subscriptions { private static Logger logger = LoggerFactory.getLogger(Subscriptions.class); private Map<String, Subscription> subscriptions; @Autowired private Patterns patterns; @Autowired SubscriptionsRepository subscriptionsRepository; @PostConstruct protected void loadSubscriptionsOnStartup() { try { subscriptions = subscriptionsRepository.getAllSubscriptions(); } catch (SubscriptionPersistenceException e) { logger.error("Failed to load subscriptions from database", e); } } /** * Add a subscription. * @param subscribeContext * @return the subscriptionId * @throws SubscriptionException, SubscriptionPersistenceException */ public String addSubscription(SubscribeContext subscribeContext) throws SubscriptionException, SubscriptionPersistenceException { //if duration is not present, then lb set duration to P1M Duration duration = convertDuration(subscribeContext.getDuration()); if (duration.isNegative()) { throw new SubscriptionException("negative duration is not allowed", new Throwable()); } if (duration.isZero()) { duration = convertDuration("P1M"); } // Compile all entity patterns now to check for conformance (result is cached for later use) try { subscribeContext.getEntityIdList().forEach(patterns::getPattern); } catch (PatternSyntaxException e) { throw new SubscriptionException("bad pattern", e); } // Generate a subscription id String subscriptionId = UUID.randomUUID().toString(); //create subscription and set the expiration date and subscriptionId Subscription subscription = new Subscription(subscriptionId, Instant.now().plus(duration), subscribeContext); //save subscription subscriptionsRepository.saveSubscription(subscription); subscriptions.put(subscriptionId, subscription); return subscriptionId; } /** * Removes a subscription. * @param unsubscribeContext * @return false if there is not subscription to delete * @throws SubscriptionPersistenceException */ public boolean deleteSubscription(UnsubscribeContext unsubscribeContext) throws SubscriptionPersistenceException { String subscriptionId = unsubscribeContext.getSubscriptionId(); subscriptionsRepository.removeSubscription(subscriptionId); Subscription subscription = subscriptions.remove(subscriptionId); return (subscription != null); } /** * find subscriptionID matching the updateContext. * @param searchEntityId the entity id to search * @param searchAttributes the attributes to search * @return list of matching subscription */ public Iterator<Subscription> findSubscriptions(EntityId searchEntityId, Set<String> searchAttributes) { // Filter out expired subscriptions Predicate<Subscription> filterExpired = subscription -> subscription.getExpirationDate() .isAfter(Instant.now()); // Filter only matching entity ids Predicate<EntityId> filterEntityId = patterns.getFilterEntityId(searchEntityId); // Only filter by attributes if search is looking for them final boolean noAttributes = searchAttributes == null || searchAttributes.size() == 0; // Filter each subscription (remove expired) and return its providing application // if at least one of its listed entities matches the searched context element // and if all searched attributes are defined in the subscription (if any) return subscriptions.values().stream().filter(filterExpired) .filter(subscription -> subscription.getSubscribeContext().getEntityIdList().stream() .filter(filterEntityId).findFirst().isPresent() && (noAttributes || !Collections .disjoint(subscription.getSubscribeContext().getAttributeList(), searchAttributes))) .iterator(); } /** * Removed all expired subscriptions every minute. */ @Scheduled(fixedDelay = 60000) public void purgeExpiredSubscriptions() { final Instant now = Instant.now(); subscriptions.forEach((subscriptionId, subscribeContext) -> { if (subscribeContext.getExpirationDate().isBefore(now)) { subscriptions.remove(subscriptionId); try { subscriptionsRepository.removeSubscription(subscriptionId); } catch (SubscriptionPersistenceException e) { logger.error("Failed to remove subscription from database", e); } } }); } /** * * @param subscriptionId the id of the subscription * @return the corresponding subscription or null if not found */ public Subscription getSubscription(String subscriptionId) { Subscription subscription = subscriptions.get(subscriptionId); return subscription; } /** * @return the duration in String format * @throws SubscriptionException */ private Duration convertDuration(String duration) throws SubscriptionException { // Use java.xml.datatype functions as java.time do not handle durations with months and years... try { long longDuration = DatatypeFactory.newInstance().newDuration(duration).getTimeInMillis(new Date()); return Duration.ofMillis(longDuration); } catch (Exception e) { throw new SubscriptionException("bad duration: " + duration, e); } } }