mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	125606 rmunteanu: Merged 5.1.1 (5.1.1) to 5.1.N (5.1.2)
      125515 slanglois: MNT-16155 Update source headers - add new Copyrights for Java and JSP source files + automatic check in the build
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@125788 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
		
	
		
			
				
	
	
		
			407 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			407 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
/*
 | 
						|
 * #%L
 | 
						|
 * Alfresco Repository
 | 
						|
 * %%
 | 
						|
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 | 
						|
 * %%
 | 
						|
 * This file is part of the Alfresco software. 
 | 
						|
 * If the software was purchased under a paid Alfresco license, the terms of 
 | 
						|
 * the paid license agreement will prevail.  Otherwise, the software is 
 | 
						|
 * provided under the following open source license terms:
 | 
						|
 * 
 | 
						|
 * Alfresco is free software: you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU Lesser General Public License as published by
 | 
						|
 * the Free Software Foundation, either version 3 of the License, or
 | 
						|
 * (at your option) any later version.
 | 
						|
 * 
 | 
						|
 * Alfresco is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU Lesser General Public License for more details.
 | 
						|
 * 
 | 
						|
 * You should have received a copy of the GNU Lesser General Public License
 | 
						|
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 * #L%
 | 
						|
 */
 | 
						|
package org.alfresco.repo.management;
 | 
						|
 | 
						|
import java.util.Collection;
 | 
						|
import java.util.LinkedHashSet;
 | 
						|
import java.util.LinkedList;
 | 
						|
import java.util.List;
 | 
						|
import java.util.Map;
 | 
						|
import java.util.Set;
 | 
						|
import java.util.concurrent.ConcurrentHashMap;
 | 
						|
import java.util.concurrent.Executor;
 | 
						|
 | 
						|
import org.apache.commons.logging.Log;
 | 
						|
import org.apache.commons.logging.LogFactory;
 | 
						|
import org.springframework.beans.BeansException;
 | 
						|
import org.springframework.beans.factory.BeanFactory;
 | 
						|
import org.springframework.beans.factory.BeanFactoryAware;
 | 
						|
import org.springframework.context.ApplicationContext;
 | 
						|
import org.springframework.context.ApplicationContextAware;
 | 
						|
import org.springframework.context.ApplicationEvent;
 | 
						|
import org.springframework.context.ApplicationListener;
 | 
						|
import org.springframework.context.event.ApplicationEventMulticaster;
 | 
						|
import org.springframework.context.event.ContextClosedEvent;
 | 
						|
import org.springframework.context.event.ContextRefreshedEvent;
 | 
						|
import org.springframework.context.event.GenericApplicationListenerAdapter;
 | 
						|
import org.springframework.context.event.SmartApplicationListener;
 | 
						|
import org.springframework.core.OrderComparator;
 | 
						|
 | 
						|
/**
 | 
						|
 * Abstract implementation of the {@link ApplicationEventMulticaster} interface,
 | 
						|
 * providing the basic listener registration facility.
 | 
						|
 * 
 | 
						|
 * <p>
 | 
						|
 * Doesn't permit multiple instances of the same listener by default, as it
 | 
						|
 * keeps listeners in a linked Set. The collection class used to hold
 | 
						|
 * ApplicationListener objects can be overridden through the "collectionClass"
 | 
						|
 * bean property.
 | 
						|
 * 
 | 
						|
 * <p>
 | 
						|
 * Implementing ApplicationEventMulticaster's actual {@link #multicastEvent}
 | 
						|
 * method is left to subclasses. {@link org.springframework.context.event.SimpleApplicationEventMulticaster}
 | 
						|
 * simply multicasts all events to all registered listeners, invoking them in
 | 
						|
 * the calling thread. Alternative implementations could be more sophisticated
 | 
						|
 * in those respects.
 | 
						|
 * 
 | 
						|
 * @author Juergen Hoeller
 | 
						|
 * @since 1.2.3
 | 
						|
 * @see #getApplicationListeners(ApplicationEvent)
 | 
						|
 * @see org.springframework.context.event.SimpleApplicationEventMulticaster
 | 
						|
 */
 | 
						|
public class SafeApplicationEventMulticaster implements ApplicationEventMulticaster, ApplicationContextAware
 | 
						|
{
 | 
						|
    private final Log log = LogFactory.getLog(SafeApplicationEventMulticaster.class);
 | 
						|
    private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
 | 
						|
 | 
						|
    private final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<ListenerCacheKey, ListenerRetriever>();
 | 
						|
 | 
						|
    private ApplicationContext appContext;
 | 
						|
    private Executor taskExecutor;
 | 
						|
 | 
						|
    /** Has the application started? */
 | 
						|
    private boolean isApplicationStarted;
 | 
						|
 | 
						|
    /** The queued events that can't be broadcast until the application is started. */
 | 
						|
    private List<ApplicationEvent> queuedEvents = new LinkedList<ApplicationEvent>();
 | 
						|
 | 
						|
 | 
						|
    /**
 | 
						|
     * Set the TaskExecutor to execute application listeners with.
 | 
						|
     * <p>
 | 
						|
     * Default is a SyncTaskExecutor, executing the listeners synchronously in
 | 
						|
     * the calling thread.
 | 
						|
     * <p>
 | 
						|
     * Consider specifying an asynchronous TaskExecutor here to not block the
 | 
						|
     * caller until all listeners have been executed. However, note that
 | 
						|
     * asynchronous execution will not participate in the caller's thread
 | 
						|
     * context (class loader, transaction association) unless the TaskExecutor
 | 
						|
     * explicitly supports this.
 | 
						|
     * 
 | 
						|
     */
 | 
						|
    public void setTaskExecutor(Executor taskExecutor)
 | 
						|
    {
 | 
						|
        this.taskExecutor = taskExecutor;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return the current TaskExecutor for this multicaster.
 | 
						|
     */
 | 
						|
    protected Executor getTaskExecutor()
 | 
						|
    {
 | 
						|
        return this.taskExecutor;
 | 
						|
    }
 | 
						|
 | 
						|
    public void addApplicationListener(ApplicationListener listener)
 | 
						|
    {
 | 
						|
        synchronized (this.defaultRetriever)
 | 
						|
        {
 | 
						|
            this.defaultRetriever.applicationListeners.add(listener);
 | 
						|
            this.retrieverCache.clear();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public void addApplicationListenerBean(String listenerBeanName)
 | 
						|
    {
 | 
						|
        synchronized (this.defaultRetriever)
 | 
						|
        {
 | 
						|
            this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
 | 
						|
            this.retrieverCache.clear();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public void removeApplicationListener(ApplicationListener listener)
 | 
						|
    {
 | 
						|
        synchronized (this.defaultRetriever)
 | 
						|
        {
 | 
						|
            this.defaultRetriever.applicationListeners.remove(listener);
 | 
						|
            this.retrieverCache.clear();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public void removeApplicationListenerBean(String listenerBeanName)
 | 
						|
    {
 | 
						|
        synchronized (this.defaultRetriever)
 | 
						|
        {
 | 
						|
            this.defaultRetriever.applicationListenerBeans.remove(listenerBeanName);
 | 
						|
            this.retrieverCache.clear();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public void removeAllListeners()
 | 
						|
    {
 | 
						|
        synchronized (this.defaultRetriever)
 | 
						|
        {
 | 
						|
            this.defaultRetriever.applicationListeners.clear();
 | 
						|
            this.defaultRetriever.applicationListenerBeans.clear();
 | 
						|
            this.retrieverCache.clear();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    private BeanFactory getBeanFactory()
 | 
						|
    {
 | 
						|
        if (this.appContext == null)
 | 
						|
        {
 | 
						|
            throw new IllegalStateException("ApplicationEventMulticaster cannot retrieve listener beans "
 | 
						|
                    + "because it is not associated with a BeanFactory");
 | 
						|
        }
 | 
						|
        return this.appContext;
 | 
						|
    }
 | 
						|
 | 
						|
    @Override
 | 
						|
    public void multicastEvent(ApplicationEvent event)
 | 
						|
    {
 | 
						|
        if (event instanceof ContextRefreshedEvent && event.getSource() == this.appContext)
 | 
						|
        {
 | 
						|
            this.isApplicationStarted = true;
 | 
						|
            for (ApplicationEvent queuedEvent : this.queuedEvents)
 | 
						|
            {
 | 
						|
                multicastEventInternal(queuedEvent);
 | 
						|
            }
 | 
						|
            this.queuedEvents.clear();
 | 
						|
            multicastEventInternal(event);
 | 
						|
        }
 | 
						|
        else if (event instanceof ContextClosedEvent && event.getSource() == this.appContext)
 | 
						|
        {
 | 
						|
            this.isApplicationStarted = false;
 | 
						|
            multicastEventInternal(event);
 | 
						|
        }
 | 
						|
        else if (this.isApplicationStarted)
 | 
						|
        {
 | 
						|
            multicastEventInternal(event);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            this.queuedEvents.add(event);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    @SuppressWarnings("unchecked")
 | 
						|
    protected void multicastEventInternal(final ApplicationEvent event) {
 | 
						|
        for (final ApplicationListener listener : getApplicationListeners(event)) {
 | 
						|
            Executor executor = getTaskExecutor();
 | 
						|
            if (executor != null) {
 | 
						|
                executor.execute(new Runnable() {
 | 
						|
                    public void run() {
 | 
						|
                        listener.onApplicationEvent(event);
 | 
						|
                    }
 | 
						|
                });
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                listener.onApplicationEvent(event);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return a Collection containing all ApplicationListeners.
 | 
						|
     * 
 | 
						|
     * @return a Collection of ApplicationListeners
 | 
						|
     * @see org.springframework.context.ApplicationListener
 | 
						|
     */
 | 
						|
    protected Collection<ApplicationListener> getApplicationListeners()
 | 
						|
    {
 | 
						|
        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
 | 
						|
     * @see org.springframework.context.ApplicationListener
 | 
						|
     */
 | 
						|
    protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event)
 | 
						|
    {
 | 
						|
        Class<? extends ApplicationEvent> eventType = event.getClass();
 | 
						|
        Class sourceType = event.getSource().getClass();
 | 
						|
        ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
 | 
						|
        ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
 | 
						|
        if (retriever != null)
 | 
						|
        {
 | 
						|
            return retriever.getApplicationListeners();
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            retriever = new ListenerRetriever(true);
 | 
						|
            LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
 | 
						|
            synchronized (this.defaultRetriever)
 | 
						|
            {
 | 
						|
                if (!this.defaultRetriever.applicationListenerBeans.isEmpty())
 | 
						|
                {
 | 
						|
                    BeanFactory beanFactory = getBeanFactory();
 | 
						|
                    for (String listenerBeanName : this.defaultRetriever.applicationListenerBeans)
 | 
						|
                    {
 | 
						|
                        ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
 | 
						|
                        if (supportsEvent(listener, eventType, sourceType))
 | 
						|
                        {
 | 
						|
                            retriever.applicationListenerBeans.add(listenerBeanName);
 | 
						|
                            allListeners.add(listener);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                for (ApplicationListener listener : this.defaultRetriever.applicationListeners)
 | 
						|
                {
 | 
						|
                    if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType))
 | 
						|
                    {
 | 
						|
                        retriever.applicationListeners.add(listener);
 | 
						|
                        allListeners.add(listener);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                OrderComparator.sort(allListeners);
 | 
						|
                this.retrieverCache.put(cacheKey, retriever);
 | 
						|
            }
 | 
						|
            if (log.isDebugEnabled())
 | 
						|
            {
 | 
						|
                log.debug(allListeners.toString());
 | 
						|
            }
 | 
						|
            return allListeners;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Determine whether the given listener supports the given event.
 | 
						|
     * <p>
 | 
						|
     * The default implementation detects the {@link SmartApplicationListener}
 | 
						|
     * interface. In case of a standard {@link ApplicationListener}, a
 | 
						|
     * {@link 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<? extends ApplicationEvent> 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 (other == null)
 | 
						|
                return false;
 | 
						|
            if (this == other)
 | 
						|
            {
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
            ListenerCacheKey otherKey = (ListenerCacheKey) other;
 | 
						|
            return (this.eventType.equals(otherKey.eventType) && this.sourceType.equals(otherKey.sourceType));
 | 
						|
        }
 | 
						|
 | 
						|
        @Override
 | 
						|
        public int hashCode()
 | 
						|
        {
 | 
						|
            return this.eventType.hashCode() * 29 + this.sourceType.hashCode();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Helper class that encapsulates a specific set of target listeners,
 | 
						|
     * allowing for efficient retrieval of pre-filtered listeners.
 | 
						|
     * <p>
 | 
						|
     * An instance of this helper gets cached per event type and source type.
 | 
						|
     */
 | 
						|
    private class ListenerRetriever
 | 
						|
    {
 | 
						|
 | 
						|
        public final Set<ApplicationListener> applicationListeners;
 | 
						|
 | 
						|
        public final Set<String> applicationListenerBeans;
 | 
						|
 | 
						|
        private final boolean preFiltered;
 | 
						|
 | 
						|
        public ListenerRetriever(boolean preFiltered)
 | 
						|
        {
 | 
						|
            this.applicationListeners = new LinkedHashSet<ApplicationListener>();
 | 
						|
            this.applicationListenerBeans = new LinkedHashSet<String>();
 | 
						|
            this.preFiltered = preFiltered;
 | 
						|
        }
 | 
						|
 | 
						|
        public Collection<ApplicationListener> getApplicationListeners()
 | 
						|
        {
 | 
						|
            LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
 | 
						|
            if (!this.applicationListenerBeans.isEmpty())
 | 
						|
            {
 | 
						|
                BeanFactory beanFactory = getBeanFactory();
 | 
						|
                for (String listenerBeanName : this.applicationListenerBeans)
 | 
						|
                {
 | 
						|
                    ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
 | 
						|
                    allListeners.add(listener);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            for (ApplicationListener listener : this.applicationListeners)
 | 
						|
            {
 | 
						|
                if (this.preFiltered || !allListeners.contains(listener))
 | 
						|
                {
 | 
						|
                    allListeners.add(listener);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            OrderComparator.sort(allListeners);
 | 
						|
            if (log.isDebugEnabled())
 | 
						|
            {
 | 
						|
                log.debug(allListeners.toString());
 | 
						|
            }
 | 
						|
            return allListeners;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    @Override
 | 
						|
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
 | 
						|
    {
 | 
						|
        this.appContext = applicationContext;
 | 
						|
    }
 | 
						|
 | 
						|
}
 |