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