001    
002    /*
003     * Copyright (C) 2011 Archie L. Cobbs. All rights reserved.
004     *
005     * $Id: VaadinApplicationListener.java 286 2012-02-17 15:18:13Z archie.cobbs $
006     */
007    
008    package org.dellroad.stuff.vaadin;
009    
010    import org.springframework.context.ApplicationEvent;
011    import org.springframework.context.event.SmartApplicationListener;
012    
013    /**
014     * A Spring {@link org.springframework.context.ApplicationListener} support superclass customized for use by
015     * listeners that are part of a Vaadin application when listening to non-Vaadin application event sources.
016     *
017     * <p>
018     * Listeners that are part of a Vaadin application should use this superclass if they are going to be registered
019     * with non-Vaadin event sources. This will ensure that events are delivered {@linkplain ContextApplication#invoke
020     * in the proper Vaadin application context}.
021     * </p>
022     *
023     * <p>
024     * Note: to avoid memory leaks, listeners must be explicitly unregistered when the associated Vaadin application closes.
025     * This can be done by explicitly {@linkplain ContextApplication#addListener registering for an application close notification}
026     * in which the listener is unregistered, or by unregistering the listener in the Spring
027     * {@linkplain org.springframework.beans.factory.DisposableBean#destroy destroy-method} associated with a
028     * bean that has {@link VaadinApplicationScope scope="vaadinApplication"} or lives in a {@link SpringContextApplication}
029     * application context (so that the bean's destroy method will be invoked when the Vaadin application closes).
030     * </p>
031     *
032     * <p>
033     * Note: when listening to event sources that are scoped to specific Vaadin application instances and originate events
034     * within the proper Vaadin application context, then the use of this listener superclass is not necessary.
035     * </p>
036     *
037     * @see ContextApplication#invoke
038     * @see VaadinApplicationScope
039     * @see SpringContextApplication
040     */
041    public abstract class VaadinApplicationListener<E extends ApplicationEvent> implements SmartApplicationListener {
042    
043        private final Class<E> eventType;
044        private final ContextApplication application;
045    
046        /**
047         * Convenience constructor. Equivalent to:
048         * <blockquote>
049         *  {@link #VaadinApplicationListener(Class, ContextApplication) VaadinApplicationListener(eventType, ContextApplication.get())}
050         * </blockquote>
051         *
052         * @throws IllegalArgumentException if {@code eventType} is null
053         * @throws IllegalStateException if there is no {@link ContextApplication} associated with the current thread
054         */
055        public VaadinApplicationListener(Class<E> eventType) {
056            this(eventType, ContextApplication.get());
057        }
058    
059        /**
060         * Primary constructor.
061         *
062         * @param eventType type of events this instance should receive (others will be ignored)
063         * @param application the associated Vaadin application
064         * @throws IllegalArgumentException if either parameter is null
065         */
066        public VaadinApplicationListener(Class<E> eventType, ContextApplication application) {
067            if (eventType == null)
068                throw new IllegalArgumentException("null eventType");
069            if (application == null)
070                throw new IllegalArgumentException("null application");
071            this.eventType = eventType;
072            this.application = application;
073        }
074    
075        public final Class<E> getEventType() {
076            return this.eventType;
077        }
078    
079        public final ContextApplication getApplication() {
080            return this.application;
081        }
082    
083        @Override
084        public final void onApplicationEvent(ApplicationEvent event) {
085            E castEvent;
086            try {
087                castEvent = this.eventType.cast(event);
088            } catch (ClassCastException e) {
089                // should not happen
090                return;
091            }
092            final E castEvent2 = castEvent;
093            this.application.invoke(new Runnable() {
094                @Override
095                public void run() {
096                    VaadinApplicationListener.this.onApplicationEventInternal(castEvent2);
097                }
098            });
099        }
100    
101        /**
102         * Handle a listener event within the context of the {@link ContextApplication} with which this listener is associated.
103         * The current {@link ContextApplication} is also available via {@link ContextApplication#get}.
104         *
105         * @see ContextApplication#get
106         */
107        protected abstract void onApplicationEventInternal(E event);
108    
109        /**
110         * Determine whether this listener actually supports the given event type.
111         *
112         * <p>
113         * The implementation in {@link VaadinApplicationListener} tests whether {@code eventType}
114         * is assignable to the type given in the constructor. Subclasses may override as desired.
115         */
116        @Override
117        public boolean supportsEventType(@SuppressWarnings("hiding") Class<? extends ApplicationEvent> eventType) {
118            return this.eventType.isAssignableFrom(eventType);
119        }
120    
121        /**
122         * Determine whether this listener actually supports the given source type.
123         *
124         * <p>
125         * The implementation in {@link VaadinApplicationListener} always returns true. Subclasses may override as desired.
126         */
127        @Override
128        public boolean supportsSourceType(Class<?> sourceType) {
129            return true;
130        }
131    
132        /**
133         * Get ordering value.
134         *
135         * <p>
136         * The implementation in {@link VaadinApplicationListener} always returns zero. Subclasses may override as desired.
137         */
138        @Override
139        public int getOrder() {
140            return 0;
141        }
142    }
143