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

com.github.datalking.context.event.AbstractApplicationEventMulticaster Maven / Gradle / Ivy

package com.github.datalking.context.event;

import com.github.datalking.beans.factory.BeanFactory;
import com.github.datalking.beans.factory.BeanFactoryAware;
import com.github.datalking.beans.factory.config.ConfigurableBeanFactory;
import com.github.datalking.beans.factory.support.AbstractBeanFactory;
import com.github.datalking.common.OrderComparator;
import com.github.datalking.context.ApplicationEvent;
import com.github.datalking.context.ApplicationListener;
import com.github.datalking.util.ObjectUtils;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author yaoo on 6/2/18
 */
public abstract class AbstractApplicationEventMulticaster
//        implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
        implements ApplicationEventMulticaster, BeanFactoryAware {

    private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);

    private final Map retrieverCache = new ConcurrentHashMap<>(64);

    private ClassLoader beanClassLoader;

    private BeanFactory beanFactory;

    private Object retrievalMutex = this.defaultRetriever;

    public void setBeanClassLoader(ClassLoader classLoader) {
        this.beanClassLoader = classLoader;
    }

    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        if (this.beanClassLoader == null && beanFactory instanceof ConfigurableBeanFactory) {
//            this.beanClassLoader = ((ConfigurableBeanFactory) beanFactory).getBeanClassLoader();
            this.beanClassLoader = this.getClass().getClassLoader();
        }
        if (beanFactory instanceof AbstractBeanFactory) {
//            this.retrievalMutex = ((AbstractBeanFactory) beanFactory).getSingletonMutex();
        }
    }

    private BeanFactory getBeanFactory() {
        if (this.beanFactory == null) {
            throw new IllegalStateException("ApplicationEventMulticaster cannot retrieve listener beans because it is not associated with a BeanFactory");
        }
        return this.beanFactory;
    }


    public void addApplicationListener(ApplicationListener listener) {
        synchronized (this.retrievalMutex) {
            this.defaultRetriever.applicationListeners.add(listener);
            this.retrieverCache.clear();
        }
    }

    public void addApplicationListenerBean(String listenerBeanName) {
        synchronized (this.retrievalMutex) {
            this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
            this.retrieverCache.clear();
        }
    }

    public void removeApplicationListener(ApplicationListener listener) {
        synchronized (this.retrievalMutex) {
            this.defaultRetriever.applicationListeners.remove(listener);
            this.retrieverCache.clear();
        }
    }

    public void removeApplicationListenerBean(String listenerBeanName) {
        synchronized (this.retrievalMutex) {
            this.defaultRetriever.applicationListenerBeans.remove(listenerBeanName);
            this.retrieverCache.clear();
        }
    }

    public void removeAllListeners() {
        synchronized (this.retrievalMutex) {
            this.defaultRetriever.applicationListeners.clear();
            this.defaultRetriever.applicationListenerBeans.clear();
            this.retrieverCache.clear();
        }
    }


    /**
     * Return a Collection containing all ApplicationListeners.
     *
     * @return a Collection of ApplicationListeners
     */
    protected Collection getApplicationListeners() {
        synchronized (this.retrievalMutex) {
            return this.defaultRetriever.getApplicationListeners();
        }
    }

    /**
     * Return a Collection of ApplicationListeners matching the given event type.
     * Non-matching listeners get excluded early.
     *
     * @param event the event to be propagated.
     *              Allows for excluding non-matching listeners early, based on cached matching information.
     * @return a Collection of ApplicationListeners
     */
    protected Collection getApplicationListeners(ApplicationEvent event) {
        Class eventType = event.getClass();
        Object source = event.getSource();
        Class sourceType = (source != null ? source.getClass() : null);
        ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

        // Quick check for existing entry on ConcurrentHashMap...
        ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
        if (retriever != null) {
            return retriever.getApplicationListeners();
        }

//        if (this.beanClassLoader == null ||
//                (ClassUtils.isCacheSafe(eventType, this.beanClassLoader) &&
//                        (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
//            // Fully synchronized building and caching of a ListenerRetriever
//            synchronized (this.retrievalMutex) {
//                retriever = this.retrieverCache.get(cacheKey);
//                if (retriever != null) {
//                    return retriever.getApplicationListeners();
//                }
//                retriever = new ListenerRetriever(true);
//                Collection listeners =
//                        retrieveApplicationListeners(eventType, sourceType, retriever);
//                this.retrieverCache.put(cacheKey, retriever);
//                return listeners;
//            }
//        } else {
        // No ListenerRetriever caching -> no synchronization necessary
        return retrieveApplicationListeners(eventType, sourceType, null);
//        }
    }

    /**
     * Actually retrieve the application listeners for the given event and source type.
     *
     * @param eventType  the application event type
     * @param sourceType the event source type
     * @param retriever  the ListenerRetriever, if supposed to populate one (for caching purposes)
     * @return the pre-filtered list of application listeners for the given event and source type
     */
    private Collection retrieveApplicationListeners(
            Class eventType, Class sourceType, ListenerRetriever retriever) {

        LinkedList allListeners = new LinkedList<>();
        Set listeners;
        Set listenerBeans;

        synchronized (this.retrievalMutex) {
            listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
            listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
        }

        for (ApplicationListener listener : listeners) {
            if (supportsEvent(listener, eventType, sourceType)) {
                if (retriever != null) {
                    retriever.applicationListeners.add(listener);
                }
                allListeners.add(listener);
            }
        }
        if (!listenerBeans.isEmpty()) {
            BeanFactory beanFactory = getBeanFactory();

            for (String listenerBeanName : listenerBeans) {
//                ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                ApplicationListener listener = (ApplicationListener) beanFactory.getBean(listenerBeanName);
                if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                    if (retriever != null) {
                        retriever.applicationListenerBeans.add(listenerBeanName);
                    }
                    allListeners.add(listener);
                }
            }
        }
        OrderComparator.sort(allListeners);
        return allListeners;
    }

    /**
     * Determine whether the given listener supports the given event.
     * 

The default implementation detects the SmartApplicationListener * interface. In case of a standard {@link ApplicationListener}, a * GenericApplicationListenerAdapter will be used to introspect * the generically declared type of the target listener. * * @param listener the target listener to check * @param eventType the event type to check against * @param sourceType the source type to check against * @return whether the given listener should be included in the candidates for the given event type */ protected boolean supportsEvent(ApplicationListener listener, Class eventType, Class sourceType) { SmartApplicationListener smartListener = (listener instanceof SmartApplicationListener ? (SmartApplicationListener) listener : new GenericApplicationListenerAdapter(listener)); return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType)); } /** * Cache key for ListenerRetrievers, based on event type and source type. */ private static class ListenerCacheKey { private final Class eventType; private final Class sourceType; public ListenerCacheKey(Class eventType, Class sourceType) { this.eventType = eventType; this.sourceType = sourceType; } @Override public boolean equals(Object other) { if (this == other) { return true; } ListenerCacheKey otherKey = (ListenerCacheKey) other; return ObjectUtils.nullSafeEquals(this.eventType, otherKey.eventType) && ObjectUtils.nullSafeEquals(this.sourceType, otherKey.sourceType); } @Override public int hashCode() { return ObjectUtils.nullSafeHashCode(this.eventType) * 29 + ObjectUtils.nullSafeHashCode(this.sourceType); } } /** * Helper class that encapsulates a specific set of target listeners, * allowing for efficient retrieval of pre-filtered listeners. *

An instance of this helper gets cached per event type and source type. */ private class ListenerRetriever { public final Set applicationListeners; public final Set applicationListenerBeans; private final boolean preFiltered; public ListenerRetriever(boolean preFiltered) { this.applicationListeners = new LinkedHashSet<>(); this.applicationListenerBeans = new LinkedHashSet<>(); this.preFiltered = preFiltered; } public Collection getApplicationListeners() { LinkedList allListeners = new LinkedList<>(); for (ApplicationListener listener : this.applicationListeners) { allListeners.add(listener); } if (!this.applicationListenerBeans.isEmpty()) { BeanFactory beanFactory = getBeanFactory(); for (String listenerBeanName : this.applicationListenerBeans) { // ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class); ApplicationListener listener = (ApplicationListener) beanFactory.getBean(listenerBeanName); if (this.preFiltered || !allListeners.contains(listener)) { allListeners.add(listener); } } } OrderComparator.sort(allListeners); return allListeners; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy