All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ocadotechnology.notification.PointToPointValidator Maven / Gradle / Ivy

There is a newer version: 16.6.21
Show newest version
/*
 * Copyright © 2017-2023 Ocado (Ocava)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ocadotechnology.notification;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Enforces that there is at most ONE subscribing class for any PointToPointNotification.
 * We do not check broadcasts (there may be multiple broadcasters). We do not allow multiple instances of a subscriber.
 *
 * Formally, it raises an error if this weak check passes:
 *  exists sub1(A), sub2(B) in Subscriptions, A, B in Classes : sub1 != sub2 & A >= B & P2P >= A
 */
class PointToPointValidator {
    //Map from P2P notification class to subscribing class
    private final Map, Subscription> subscriptions = new ConcurrentHashMap<>();

    private static final class Subscription {
        final Class subscriberClass;
        final Class notificationClass;

        private Subscription(Class subscriberClass, Class notificationClass) {
            this.subscriberClass = subscriberClass;
            this.notificationClass = notificationClass;
        }
    }

    PointToPointValidator() {
    }

    /** This method is and needs to remain ThreadSafe. */
    void validate(Object subscriber, List> subscribedNotifications) {
        Class subscriberClass = subscriber.getClass();
        for (Class subscribedNotification : subscribedNotifications) {
            if (PointToPointNotification.class.isAssignableFrom(subscribedNotification)) {
                for (Class supertypeClass : getP2PSupertypes(subscribedNotification)) {
                    Subscription newSubscription = new Subscription(subscriberClass, subscribedNotification);
                    Subscription oldSubscription = subscriptions.putIfAbsent(supertypeClass, newSubscription);
                    if (oldSubscription != null) {
                        throw new IllegalStateException(getErrorMessage(newSubscription, oldSubscription));
                    }
                }
            }
        }
    }

    private List> getP2PSupertypes(Class clazz) {
        List> classesToCheck = new ArrayList<>();
        classesToCheck.add(clazz);

        for (int i = 0; i < classesToCheck.size(); i++) {
            Class next = classesToCheck.get(i);

            Class superclass = next.getSuperclass();
            addIfClassImplementsP2P(superclass, classesToCheck);

            for (Class inter : next.getInterfaces()) {
                addIfClassImplementsP2P(inter, classesToCheck);
            }
        }

        return classesToCheck;
    }

    private void addIfClassImplementsP2P(Class clazz, List> classesToCheck) {
        if (clazz != null && classImplementsP2P(clazz) && !classesToCheck.contains(clazz)) {
            classesToCheck.add(clazz);
        }
    }

    private boolean classImplementsP2P(Class clazz) {
        return PointToPointNotification.class.isAssignableFrom(clazz) && !clazz.equals(PointToPointNotification.class);
    }

    private String getErrorMessage(Subscription newSubscription, Subscription oldSubscription) {
        if (newSubscription.subscriberClass.equals(oldSubscription.subscriberClass)) {
            return String.format(
                    "Too many P2P subscribers. PointToPointNotification %s is subscribed to twice by %s",
                    newSubscription.notificationClass.getSimpleName(),
                    newSubscription.subscriberClass.getSimpleName());
        }
        if (newSubscription.notificationClass.equals(oldSubscription.notificationClass)) {
            return String.format(
                    "Too many P2P subscribers. A subscriber of type %s, and one of type %s (which both listen to notification %s, which is P2P) have been registered",
                    newSubscription.subscriberClass.getSimpleName(),
                    oldSubscription.subscriberClass.getSimpleName(),
                    newSubscription.notificationClass.getSimpleName());
        }

        String ancestorDescendant = newSubscription.notificationClass.isAssignableFrom(oldSubscription.notificationClass) ?
                "descendant" :
                "ancestor";

        return String.format(
                "Too many P2P subscribers. A subscriber of type %s which listens to notifications of type %s, and one of type %s which listens to its P2P %s %s have been registered",
                newSubscription.subscriberClass.getSimpleName(),
                newSubscription.notificationClass.getSimpleName(),
                oldSubscription.subscriberClass.getSimpleName(),
                ancestorDescendant,
                oldSubscription.notificationClass.getSimpleName());
    }

    void reset() {
        subscriptions.clear();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy