Browse Source

ApplicationListener beans get obtained on demand, supporting non-singletons as well; ApplicationListeners will be called in the order according to the Ordered contract; generified ApplicationListener interface

conversation
Juergen Hoeller 16 years ago
parent
commit
60392d6e74
  1. 7
      org.springframework.context/src/main/java/org/springframework/context/ApplicationListener.java
  2. 80
      org.springframework.context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java
  3. 24
      org.springframework.context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java
  4. 96
      org.springframework.context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java
  5. 31
      org.springframework.context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java
  6. 37
      org.springframework.context/src/main/java/org/springframework/context/event/SmartApplicationListener.java
  7. 20
      org.springframework.context/src/main/java/org/springframework/context/event/SourceFilteringListener.java
  8. 21
      org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
  9. 160
      org.springframework.context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java
  10. 8
      org.springframework.core/src/main/java/org/springframework/core/GenericCollectionTypeResolver.java
  11. 14
      org.springframework.core/src/main/java/org/springframework/core/GenericTypeResolver.java
  12. 17
      org.springframework.core/src/main/java/org/springframework/core/Ordered.java
  13. 17
      org.springframework.core/src/test/java/org/springframework/core/GenericCollectionTypeResolverTests.java
  14. 12
      org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.java
  15. 12
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java

7
org.springframework.context/src/main/java/org/springframework/context/ApplicationListener.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2006 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,14 +24,15 @@ import java.util.EventListener; @@ -24,14 +24,15 @@ import java.util.EventListener;
* for the Observer design pattern.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see org.springframework.context.event.ApplicationEventMulticaster
*/
public interface ApplicationListener extends EventListener {
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(ApplicationEvent event);
void onApplicationEvent(E event);
}

80
org.springframework.context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,11 +17,15 @@ @@ -17,11 +17,15 @@
package org.springframework.context.event;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.LinkedList;
import java.util.Set;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.ApplicationListener;
import org.springframework.core.OrderComparator;
/**
* Abstract implementation of the {@link ApplicationEventMulticaster} interface,
@ -43,73 +47,47 @@ import org.springframework.context.ApplicationListener; @@ -43,73 +47,47 @@ import org.springframework.context.ApplicationListener;
* @see #getApplicationListeners()
* @see SimpleApplicationEventMulticaster
*/
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster {
public abstract class AbstractApplicationEventMulticaster
implements ApplicationEventMulticaster, BeanFactoryAware {
/** Collection of ApplicationListeners */
private Collection<ApplicationListener> applicationListeners = new LinkedHashSet<ApplicationListener>();
private final Set<ApplicationListener> applicationListeners = new LinkedHashSet<ApplicationListener>();
private final Set<String> applicationListenerBeans = new LinkedHashSet<String>();
/**
* Set whether this multicaster should expect concurrent updates at runtime
* (i.e. after context startup finished). In case of concurrent updates,
* a copy-on-write strategy is applied, keeping iteration (for multicasting)
* without synchronization while still making listener updates thread-safe.
*/
public void setConcurrentUpdates(boolean concurrent) {
Collection<ApplicationListener> newColl = concurrent ?
new CopyOnWriteArraySet<ApplicationListener>() : new LinkedHashSet<ApplicationListener>();
// Add all previously registered listeners (usually none).
newColl.addAll(this.applicationListeners);
this.applicationListeners = newColl;
}
/**
* Specify the collection class to use. Can be populated with a fully
* qualified class name when defined in a Spring application context.
* <p>Default is a linked HashSet, keeping the registration order.
* Note that a Set class specified will not permit multiple instances
* of the same listener, while a List class will allow for registering
* the same listener multiple times.
*/
@SuppressWarnings("unchecked")
public void setCollectionClass(Class collectionClass) {
if (collectionClass == null) {
throw new IllegalArgumentException("'collectionClass' must not be null");
}
if (!Collection.class.isAssignableFrom(collectionClass)) {
throw new IllegalArgumentException("'collectionClass' must implement [java.util.Collection]");
}
// Create desired collection instance.
Collection<ApplicationListener> newColl =
(Collection<ApplicationListener>) BeanUtils.instantiateClass(collectionClass);
// Add all previously registered listeners (usually none).
newColl.addAll(this.applicationListeners);
this.applicationListeners = newColl;
}
private BeanFactory beanFactory;
public void addApplicationListener(ApplicationListener listener) {
this.applicationListeners.add(listener);
}
public void removeApplicationListener(ApplicationListener listener) {
this.applicationListeners.remove(listener);
public void addApplicationListenerBean(String listenerBeanName) {
this.applicationListenerBeans.add(listenerBeanName);
}
public void removeAllListeners() {
this.applicationListeners.clear();
public final void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* Return the current Collection of ApplicationListeners.
* <p>Note that this is the raw Collection of ApplicationListeners,
* potentially modified when new listeners get registered or
* existing ones get removed. This Collection is not a snapshot copy.
* @return a Collection of ApplicationListeners
* @see org.springframework.context.ApplicationListener
*/
protected Collection<ApplicationListener> getApplicationListeners() {
return this.applicationListeners;
LinkedList<ApplicationListener> allListeners =
new LinkedList<ApplicationListener>(this.applicationListeners);
if (!this.applicationListenerBeans.isEmpty()) {
if (this.beanFactory == null) {
throw new IllegalStateException("ApplicationEventMulticaster cannot retrieve listener beans " +
"because it is not associated with a BeanFactory: " + this.applicationListenerBeans);
}
for (String listenerBeanName : applicationListenerBeans) {
allListeners.add(this.beanFactory.getBean(listenerBeanName, ApplicationListener.class));
}
}
Collections.sort(allListeners, new OrderComparator());
return allListeners;
}
}

24
org.springframework.context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
/*
* Copyright 2002-2005 the original author or authors.
*
/*
* Copyright 2002-2009 the original author or authors.
*
* 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.
@ -27,6 +27,7 @@ import org.springframework.context.ApplicationListener; @@ -27,6 +27,7 @@ import org.springframework.context.ApplicationListener;
* as a helper to publish events to listeners.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public interface ApplicationEventMulticaster {
@ -37,17 +38,10 @@ public interface ApplicationEventMulticaster { @@ -37,17 +38,10 @@ public interface ApplicationEventMulticaster {
void addApplicationListener(ApplicationListener listener);
/**
* Remove a listener from the notification list.
* @param listener the listener to remove
*/
void removeApplicationListener(ApplicationListener listener);
/**
* Remove all listeners registered with this multicaster.
* It will perform no action on event notification until more
* listeners are registered.
* Add a listener to be notified of all events.
* @param listenerBeanName the name of the listener bean to add
*/
void removeAllListeners();
void addApplicationListenerBean(String listenerBeanName);
/**
* Multicast the given application event to appropriate listeners.

96
org.springframework.context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java

@ -0,0 +1,96 @@ @@ -0,0 +1,96 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.context.event;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
/**
* {@link SmartApplicationListener} adapter that determines supported event types
* through introspecting the generically declared type of the target listener.
*
* @author Juergen Hoeller
* @since 3.0
* @see org.springframework.context.ApplicationListener#onApplicationEvent
*/
public class GenericApplicationListenerAdapter implements SmartApplicationListener {
private final ApplicationListener delegate;
/**
* Create a new GenericApplicationListener for the given delegate.
* @param delegate the delegate listener to be invoked
*/
public GenericApplicationListenerAdapter(ApplicationListener delegate) {
Assert.notNull(delegate, "Delegate listener must not be null");
this.delegate = delegate;
}
@SuppressWarnings("unchecked")
public void onApplicationEvent(ApplicationEvent event) {
this.delegate.onApplicationEvent(event);
}
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return getGenericEventType(this.delegate.getClass()).isAssignableFrom(eventType);
}
public int getOrder() {
return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE);
}
@SuppressWarnings("unchecked")
private Class<? extends ApplicationEvent> getGenericEventType(Class<? extends ApplicationListener> currentClass) {
Class classToIntrospect = currentClass;
while (classToIntrospect != null) {
Type[] ifcs = classToIntrospect.getGenericInterfaces();
for (Type ifc : ifcs) {
if (ifc instanceof ParameterizedType) {
ParameterizedType paramIfc = (ParameterizedType) ifc;
Type rawType = paramIfc.getRawType();
if (ApplicationListener.class.equals(rawType)) {
Type arg = paramIfc.getActualTypeArguments()[0];
if (arg instanceof TypeVariable) {
arg = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg, this.delegate.getClass());
}
if (arg instanceof Class) {
return (Class) arg;
}
}
else if (ApplicationListener.class.isAssignableFrom((Class) rawType)) {
return getGenericEventType((Class) rawType);
}
}
else if (ApplicationListener.class.isAssignableFrom((Class) ifc)) {
return getGenericEventType((Class) ifc);
}
}
classToIntrospect = classToIntrospect.getSuperclass();
}
return ApplicationEvent.class;
}
}

31
org.springframework.context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java

@ -18,6 +18,7 @@ package org.springframework.context.event; @@ -18,6 +18,7 @@ package org.springframework.context.event;
import java.util.concurrent.Executor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.task.SyncTaskExecutor;
@ -38,13 +39,26 @@ import org.springframework.core.task.SyncTaskExecutor; @@ -38,13 +39,26 @@ import org.springframework.core.task.SyncTaskExecutor;
* @author Rod Johnson
* @author Juergen Hoeller
* @see #setTaskExecutor
* @see #setConcurrentUpdates
*/
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
private Executor taskExecutor = new SyncTaskExecutor();
/**
* Create a new SimpleApplicationEventMulticaster.
*/
public SimpleApplicationEventMulticaster() {
}
/**
* Create a new SimpleApplicationEventMulticaster for the given BeanFactory.
*/
public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
setBeanFactory(beanFactory);
}
/**
* Set the TaskExecutor to execute application listeners with.
* <p>Default is a SyncTaskExecutor, executing the listeners synchronously
@ -71,11 +85,16 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM @@ -71,11 +85,16 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
public void multicastEvent(final ApplicationEvent event) {
for (final ApplicationListener listener : getApplicationListeners()) {
getTaskExecutor().execute(new Runnable() {
public void run() {
listener.onApplicationEvent(event);
}
});
SmartApplicationListener smartListener = (listener instanceof SmartApplicationListener ?
(SmartApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
if (smartListener.supportsEventType(event.getClass())) {
getTaskExecutor().execute(new Runnable() {
@SuppressWarnings("unchecked")
public void run() {
listener.onApplicationEvent(event);
}
});
}
}
}

37
org.springframework.context/src/main/java/org/springframework/context/event/SmartApplicationListener.java

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.context.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
/**
* Extended variant of the standard {@link ApplicationListener} interface,
* exposing further metadata such as the supported event type.
*
* @author Juergen Hoeller
* @since 3.0
*/
public interface SmartApplicationListener extends ApplicationListener, Ordered {
/**
* Determine whether this listener actually supports the given event type.
*/
boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
}

20
org.springframework.context/src/main/java/org/springframework/context/event/SourceFilteringListener.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.context.event; @@ -18,6 +18,7 @@ package org.springframework.context.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
/**
* {@link org.springframework.context.ApplicationListener} decorator that filters
@ -30,11 +31,11 @@ import org.springframework.context.ApplicationListener; @@ -30,11 +31,11 @@ import org.springframework.context.ApplicationListener;
* @author Juergen Hoeller
* @since 2.0.5
*/
public class SourceFilteringListener implements ApplicationListener {
public class SourceFilteringListener implements SmartApplicationListener {
private final Object source;
private ApplicationListener delegate;
private SmartApplicationListener delegate;
/**
@ -46,7 +47,8 @@ public class SourceFilteringListener implements ApplicationListener { @@ -46,7 +47,8 @@ public class SourceFilteringListener implements ApplicationListener {
*/
public SourceFilteringListener(Object source, ApplicationListener delegate) {
this.source = source;
this.delegate = delegate;
this.delegate = (delegate instanceof SmartApplicationListener ?
(SmartApplicationListener) delegate : new GenericApplicationListenerAdapter(delegate));
}
/**
@ -67,12 +69,22 @@ public class SourceFilteringListener implements ApplicationListener { @@ -67,12 +69,22 @@ public class SourceFilteringListener implements ApplicationListener {
}
}
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return (this.delegate == null || this.delegate.supportsEventType(eventType));
}
public int getOrder() {
return (this.delegate != null ? this.delegate.getOrder() : Ordered.LOWEST_PRECEDENCE);
}
/**
* Actually process the event, after having filtered according to the
* desired event source already.
* <p>The default implementation invokes the specified delegate, if any.
* @param event the event to process (matching the specified source)
*/
@SuppressWarnings("unchecked")
protected void onApplicationEventInternal(ApplicationEvent event) {
if (this.delegate == null) {
throw new IllegalStateException(

21
org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,7 +19,6 @@ package org.springframework.context.support; @@ -19,7 +19,6 @@ package org.springframework.context.support;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
@ -657,7 +656,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader @@ -657,7 +656,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster();
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
@ -685,13 +684,13 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader @@ -685,13 +684,13 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener listener : getApplicationListeners()) {
addListener(listener);
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
Collection<ApplicationListener> lisBeans = getBeansOfType(ApplicationListener.class, true, false).values();
for (ApplicationListener lisBean : lisBeans) {
addListener(lisBean);
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (final String lisName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
}
}
@ -924,18 +923,18 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader @@ -924,18 +923,18 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
return getBeanFactory().getBeanNamesForType(type);
}
public String[] getBeanNamesForType(Class type, boolean includePrototypes, boolean allowEagerInit) {
return getBeanFactory().getBeanNamesForType(type, includePrototypes, allowEagerInit);
public String[] getBeanNamesForType(Class type, boolean includeNonSingletons, boolean allowEagerInit) {
return getBeanFactory().getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
}
public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
return getBeanFactory().getBeansOfType(type);
}
public <T> Map<String, T> getBeansOfType(Class<T> type, boolean includePrototypes, boolean allowEagerInit)
public <T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {
return getBeanFactory().getBeansOfType(type, includePrototypes, allowEagerInit);
return getBeanFactory().getBeansOfType(type, includeNonSingletons, allowEagerInit);
}
public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)

160
org.springframework.context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,19 +16,16 @@ @@ -16,19 +16,16 @@
package org.springframework.context.event;
import java.util.ArrayList;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import org.aopalliance.intercept.MethodInvocation;
import org.easymock.EasyMock;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.Assert;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
@ -37,6 +34,7 @@ import org.springframework.context.ApplicationListener; @@ -37,6 +34,7 @@ import org.springframework.context.ApplicationListener;
import org.springframework.context.BeanThatBroadcasts;
import org.springframework.context.BeanThatListens;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.Ordered;
/**
* Unit and integration tests for the ApplicationContext event support.
@ -46,70 +44,9 @@ import org.springframework.context.support.StaticApplicationContext; @@ -46,70 +44,9 @@ import org.springframework.context.support.StaticApplicationContext;
*/
public class ApplicationContextEventTests {
private AbstractApplicationEventMulticaster getMulticaster() {
return new AbstractApplicationEventMulticaster() {
public void multicastEvent(ApplicationEvent event) {
}
};
}
@Test
public void multicasterNewCollectionClass() {
AbstractApplicationEventMulticaster mc = getMulticaster();
mc.addApplicationListener(new NoOpApplicationListener());
mc.setCollectionClass(ArrayList.class);
assertEquals(1, mc.getApplicationListeners().size());
assertEquals(ArrayList.class, mc.getApplicationListeners().getClass());
}
@Test(expected = IllegalArgumentException.class)
public void multicasterInvalidCollectionClass_NotEvenACollectionType() {
AbstractApplicationEventMulticaster mc = getMulticaster();
mc.setCollectionClass(ApplicationContextEventTests.class);
}
@Test(expected = FatalBeanException.class)
public void multicasterInvalidCollectionClass_PassingAnInterfaceNotAConcreteClass() {
AbstractApplicationEventMulticaster mc = getMulticaster();
mc.setCollectionClass(List.class);
}
@Test(expected = IllegalArgumentException.class)
public void multicasterNullCollectionClass() {
AbstractApplicationEventMulticaster mc = getMulticaster();
mc.setCollectionClass(null);
}
@Test
public void multicasterRemoveAll() {
AbstractApplicationEventMulticaster mc = getMulticaster();
mc.addApplicationListener(new NoOpApplicationListener());
mc.removeAllListeners();
assertEquals(0, mc.getApplicationListeners().size());
}
@Test
public void multicasterRemoveOne() {
AbstractApplicationEventMulticaster mc = getMulticaster();
ApplicationListener one = new NoOpApplicationListener();
ApplicationListener two = new NoOpApplicationListener();
mc.addApplicationListener(one);
mc.addApplicationListener(two);
mc.removeApplicationListener(one);
assertEquals(1, mc.getApplicationListeners().size());
assertTrue("Remaining listener present", mc.getApplicationListeners().contains(two));
}
@Test
public void simpleApplicationEventMulticaster() {
ApplicationListener listener = EasyMock.createMock(ApplicationListener.class);
ApplicationEvent evt = new ContextClosedEvent(new StaticApplicationContext());
listener.onApplicationEvent(evt);
@ -117,14 +54,25 @@ public class ApplicationContextEventTests { @@ -117,14 +54,25 @@ public class ApplicationContextEventTests {
smc.addApplicationListener(listener);
replay(listener);
smc.multicastEvent(evt);
verify(listener);
}
@Test
public void testEvenPublicationInterceptor() throws Throwable {
public void orderedListeners() {
MyOrderedListener1 listener1 = new MyOrderedListener1();
MyOrderedListener2 listener2 = new MyOrderedListener2(listener1);
SimpleApplicationEventMulticaster smc = new SimpleApplicationEventMulticaster();
smc.addApplicationListener(listener2);
smc.addApplicationListener(listener1);
smc.multicastEvent(new MyEvent(this));
smc.multicastEvent(new MyOtherEvent(this));
}
@Test
public void testEventPublicationInterceptor() throws Throwable {
MethodInvocation invocation = EasyMock.createMock(MethodInvocation.class);
ApplicationContext ctx = EasyMock.createMock(ApplicationContext.class);
@ -134,37 +82,15 @@ public class ApplicationContextEventTests { @@ -134,37 +82,15 @@ public class ApplicationContextEventTests {
interceptor.afterPropertiesSet();
expect(invocation.proceed()).andReturn(new Object());
expect(invocation.getThis()).andReturn(new Object());
ctx.publishEvent(isA(MyEvent.class));
replay(invocation, ctx);
interceptor.invoke(invocation);
verify(invocation, ctx);
}
@Test
public void listenerAndBroadcasterWithUnresolvableCircularReference() {
StaticApplicationContext context = new StaticApplicationContext();
context.setDisplayName("listener context");
context.registerBeanDefinition("broadcaster", new RootBeanDefinition(BeanThatBroadcasts.class));
RootBeanDefinition listenerDef = new RootBeanDefinition(BeanThatListens.class);
listenerDef.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("broadcaster"));
context.registerBeanDefinition("listener", listenerDef);
try {
context.refresh();
fail("Should have thrown BeanCreationException with nested BeanCurrentlyInCreationException");
}
catch (BeanCreationException ex) {
assertTrue(ex.contains(BeanCurrentlyInCreationException.class));
}
}
@Test
public void listenerAndBroadcasterWithResolvableCircularReference() {
public void listenerAndBroadcasterWithCircularReference() {
StaticApplicationContext context = new StaticApplicationContext();
context.registerBeanDefinition("broadcaster", new RootBeanDefinition(BeanThatBroadcasts.class));
RootBeanDefinition listenerDef = new RootBeanDefinition(BeanThatListens.class);
@ -177,6 +103,7 @@ public class ApplicationContextEventTests { @@ -177,6 +103,7 @@ public class ApplicationContextEventTests {
Assert.assertEquals("The event was not received by the listener", 2, broadcaster.receivedCount);
}
public static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
@ -184,11 +111,52 @@ public class ApplicationContextEventTests { @@ -184,11 +111,52 @@ public class ApplicationContextEventTests {
}
}
private static final class NoOpApplicationListener implements ApplicationListener {
public static class MyOtherEvent extends ApplicationEvent {
public MyOtherEvent(Object source) {
super(source);
}
}
public static class MyOrderedListener1 implements ApplicationListener, Ordered {
public final Set<ApplicationEvent> seenEvents = new HashSet<ApplicationEvent>();
public void onApplicationEvent(ApplicationEvent event) {
this.seenEvents.add(event);
}
public int getOrder() {
return 0;
}
}
public interface MyOrderedListenerIfc<E extends ApplicationEvent> extends ApplicationListener<E>, Ordered {
}
public static abstract class MyOrderedListenerBase implements MyOrderedListenerIfc<MyEvent> {
public int getOrder() {
return 1;
}
}
public static class MyOrderedListener2 extends MyOrderedListenerBase {
private final MyOrderedListener1 otherListener;
public MyOrderedListener2(MyOrderedListener1 otherListener) {
this.otherListener = otherListener;
}
public void onApplicationEvent(MyEvent event) {
assertTrue(otherListener.seenEvents.contains(event));
}
}
}

8
org.springframework.core/src/main/java/org/springframework/core/GenericCollectionTypeResolver.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -45,7 +45,7 @@ public abstract class GenericCollectionTypeResolver { @@ -45,7 +45,7 @@ public abstract class GenericCollectionTypeResolver {
* @param collectionClass the collection class to introspect
* @return the generic type, or <code>null</code> if none
*/
public static Class getCollectionType(Class collectionClass) {
public static Class getCollectionType(Class<? extends Collection> collectionClass) {
return extractTypeFromClass(collectionClass, Collection.class, 0);
}
@ -55,7 +55,7 @@ public abstract class GenericCollectionTypeResolver { @@ -55,7 +55,7 @@ public abstract class GenericCollectionTypeResolver {
* @param mapClass the map class to introspect
* @return the generic type, or <code>null</code> if none
*/
public static Class getMapKeyType(Class mapClass) {
public static Class getMapKeyType(Class<? extends Map> mapClass) {
return extractTypeFromClass(mapClass, Map.class, 0);
}
@ -65,7 +65,7 @@ public abstract class GenericCollectionTypeResolver { @@ -65,7 +65,7 @@ public abstract class GenericCollectionTypeResolver {
* @param mapClass the map class to introspect
* @return the generic type, or <code>null</code> if none
*/
public static Class getMapValueType(Class mapClass) {
public static Class getMapValueType(Class<? extends Map> mapClass) {
return extractTypeFromClass(mapClass, Map.class, 1);
}

14
org.springframework.core/src/main/java/org/springframework/core/GenericTypeResolver.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -99,6 +99,18 @@ public abstract class GenericTypeResolver { @@ -99,6 +99,18 @@ public abstract class GenericTypeResolver {
return (rawType instanceof Class ? (Class) rawType : method.getReturnType());
}
/**
* Resolve the given type variable against the given class.
* @param tv the type variable to resolve
* @param clazz the class that defines the type variable
* somewhere in its inheritance hierarchy
* @return the resolved type that the variable can get replaced with,
* or <code>null</code> if none found
*/
public static Type resolveTypeVariable(TypeVariable tv, Class clazz) {
return getTypeVariableMap(clazz).get(tv);
}
/**
* Resolve the specified generic type against the given TypeVariable map.

17
org.springframework.core/src/main/java/org/springframework/core/Ordered.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -51,18 +51,13 @@ public interface Ordered { @@ -51,18 +51,13 @@ public interface Ordered {
/**
* Return the order value of this object, with a
* higher value meaning greater in terms of sorting.
* <p>Normally starting with 0 or 1, with {@link #LOWEST_PRECEDENCE}
* indicating greatest. Same order values will result in arbitrary
* positions for the affected objects.
* <p>Higher value can be interpreted as lower priority,
* consequently the first object has highest priority
* <p>Normally starting with 0, with <code>Integer.MAX_VALUE</code>
* indicating the greatest value. Same order values will result
* in arbitrary positions for the affected objects.
* <p>Higher values can be interpreted as lower priority. As a
* consequence, the object with the lowest value has highest priority
* (somewhat analogous to Servlet "load-on-startup" values).
* <p>Note that order values below 0 are reserved for framework
* purposes. Application-specified values should always be 0 or
* greater, with only framework components (internal or third-party)
* supposed to use lower values.
* @return the order value
* @see #LOWEST_PRECEDENCE
*/
int getOrder();

17
org.springframework.core/src/test/java/org/springframework/core/GenericCollectionTypeResolverTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2006 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,6 +19,7 @@ package org.springframework.core; @@ -19,6 +19,7 @@ package org.springframework.core;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Date;
import java.util.List;
import java.util.Map;
@ -101,12 +102,22 @@ public class GenericCollectionTypeResolverTests extends AbstractGenericsTests { @@ -101,12 +102,22 @@ public class GenericCollectionTypeResolverTests extends AbstractGenericsTests {
GenericCollectionTypeResolver.getCollectionReturnType(getter));
}
public void testClassResolution() {
assertEquals(String.class, GenericCollectionTypeResolver.getCollectionType(CustomSet.class));
assertEquals(String.class, GenericCollectionTypeResolver.getMapKeyType(CustomMap.class));
assertEquals(Integer.class, GenericCollectionTypeResolver.getMapValueType(CustomMap.class));
}
private abstract class CustomSet<T> extends AbstractSet<String> {
}
private abstract class CustomMap <T> extends AbstractMap<String, Integer> {
private abstract class CustomMap<T> extends AbstractMap<String, Integer> {
}
private abstract class OtherCustomMap <T> implements Map<String, Integer> {
private abstract class OtherCustomMap<T> implements Map<String, Integer> {
}

12
org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.java

@ -35,7 +35,6 @@ import org.springframework.beans.BeanUtils; @@ -35,7 +35,6 @@ import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
@ -102,7 +101,8 @@ import org.springframework.web.portlet.context.XmlPortletApplicationContext; @@ -102,7 +101,8 @@ import org.springframework.web.portlet.context.XmlPortletApplicationContext;
* @see #setContextConfigLocation
* @see #setNamespace
*/
public abstract class FrameworkPortlet extends GenericPortletBean implements ApplicationListener {
public abstract class FrameworkPortlet extends GenericPortletBean
implements ApplicationListener<ContextRefreshedEvent> {
/**
* Default context class for FrameworkPortlet.
@ -417,11 +417,9 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App @@ -417,11 +417,9 @@ public abstract class FrameworkPortlet extends GenericPortletBean implements App
* triggering a refresh of this servlet's context-dependent state.
* @param event the incoming ApplicationContext event
*/
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
this.refreshEventReceived = true;
onRefresh(((ContextRefreshedEvent) event).getApplicationContext());
}
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
onRefresh(event.getApplicationContext());
}
/**

12
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java

@ -26,7 +26,6 @@ import org.springframework.beans.BeanUtils; @@ -26,7 +26,6 @@ import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
@ -94,7 +93,8 @@ import org.springframework.web.util.WebUtils; @@ -94,7 +93,8 @@ import org.springframework.web.util.WebUtils;
* @see #setContextConfigLocation
* @see #setNamespace
*/
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationListener {
public abstract class FrameworkServlet extends HttpServletBean
implements ApplicationListener<ContextRefreshedEvent> {
/**
* Suffix for WebApplicationContext namespaces. If a servlet of this class is
@ -493,11 +493,9 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic @@ -493,11 +493,9 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
* triggering a refresh of this servlet's context-dependent state.
* @param event the incoming ApplicationContext event
*/
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
this.refreshEventReceived = true;
onRefresh(((ContextRefreshedEvent) event).getApplicationContext());
}
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
onRefresh(event.getApplicationContext());
}
/**

Loading…
Cancel
Save