11/*
2- * Copyright 2002-2022 the original author or authors.
2+ * Copyright 2002-2023 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
2121
2222import org .junit .jupiter .api .Test ;
2323
24+ import org .springframework .beans .BeansException ;
2425import org .springframework .context .ApplicationListener ;
2526import org .springframework .context .ConfigurableApplicationContext ;
2627import org .springframework .context .PayloadApplicationEvent ;
@@ -68,16 +69,97 @@ void payloadApplicationEventWithType() {
6869});
6970}
7071
72+ @ Test
73+ @ SuppressWarnings ("resource" )
74+ void testEventClassWithPayloadType () {
75+ ConfigurableApplicationContext ac =new AnnotationConfigApplicationContext (NumberHolderListener .class );
76+
77+ PayloadApplicationEvent <NumberHolder <Integer >>event =new PayloadApplicationEvent <>(this ,
78+ new NumberHolder <>(42 ),ResolvableType .forClassWithGenerics (NumberHolder .class ,Integer .class ));
79+ ac .publishEvent (event );
80+ assertThat (ac .getBean (NumberHolderListener .class ).events .contains (event .getPayload ())).isTrue ();
81+ ac .close ();
82+ }
83+
84+ @ Test
85+ @ SuppressWarnings ("resource" )
86+ void testEventClassWithPayloadTypeOnParentContext () {
87+ ConfigurableApplicationContext parent =new AnnotationConfigApplicationContext (NumberHolderListener .class );
88+ ConfigurableApplicationContext ac =new GenericApplicationContext (parent );
89+ ac .refresh ();
90+
91+ PayloadApplicationEvent <NumberHolder <Integer >>event =new PayloadApplicationEvent <>(this ,
92+ new NumberHolder <>(42 ),ResolvableType .forClassWithGenerics (NumberHolder .class ,Integer .class ));
93+ ac .publishEvent (event );
94+ assertThat (parent .getBean (NumberHolderListener .class ).events .contains (event .getPayload ())).isTrue ();
95+ ac .close ();
96+ parent .close ();
97+ }
98+
99+ @ Test
100+ @ SuppressWarnings ("resource" )
101+ void testPayloadObjectWithPayloadType () {
102+ final Object payload =new NumberHolder <>(42 );
103+
104+ AnnotationConfigApplicationContext ac =new AnnotationConfigApplicationContext (NumberHolderListener .class ) {
105+ @ Override
106+ protected void finishRefresh ()throws BeansException {
107+ super .finishRefresh ();
108+ // This is not recommended: use publishEvent(new PayloadApplicationEvent(...)) instead
109+ publishEvent (payload ,ResolvableType .forClassWithGenerics (NumberHolder .class ,Integer .class ));
110+ }
111+ };
112+
113+ assertThat (ac .getBean (NumberHolderListener .class ).events .contains (payload )).isTrue ();
114+ ac .close ();
115+ }
116+
117+ @ Test
118+ @ SuppressWarnings ("resource" )
119+ void testPayloadObjectWithPayloadTypeOnParentContext () {
120+ final Object payload =new NumberHolder <>(42 );
121+
122+ ConfigurableApplicationContext parent =new AnnotationConfigApplicationContext (NumberHolderListener .class );
123+ ConfigurableApplicationContext ac =new GenericApplicationContext (parent ) {
124+ @ Override
125+ protected void finishRefresh ()throws BeansException {
126+ super .finishRefresh ();
127+ // This is not recommended: use publishEvent(new PayloadApplicationEvent(...)) instead
128+ publishEvent (payload ,ResolvableType .forClassWithGenerics (NumberHolder .class ,Integer .class ));
129+ }
130+ };
131+ ac .refresh ();
132+
133+ assertThat (parent .getBean (NumberHolderListener .class ).events .contains (payload )).isTrue ();
134+ ac .close ();
135+ parent .close ();
136+ }
137+
71138@ Test
72139@ SuppressWarnings ("resource" )
73140void testEventClassWithInterface () {
74141ConfigurableApplicationContext ac =new AnnotationConfigApplicationContext (AuditableListener .class );
142+
75143AuditablePayloadEvent <String >event =new AuditablePayloadEvent <>(this ,"xyz" );
76144ac .publishEvent (event );
77145assertThat (ac .getBean (AuditableListener .class ).events .contains (event )).isTrue ();
78146ac .close ();
79147}
80148
149+ @ Test
150+ @ SuppressWarnings ("resource" )
151+ void testEventClassWithInterfaceOnParentContext () {
152+ ConfigurableApplicationContext parent =new AnnotationConfigApplicationContext (AuditableListener .class );
153+ ConfigurableApplicationContext ac =new GenericApplicationContext (parent );
154+ ac .refresh ();
155+
156+ AuditablePayloadEvent <String >event =new AuditablePayloadEvent <>(this ,"xyz" );
157+ ac .publishEvent (event );
158+ assertThat (parent .getBean (AuditableListener .class ).events .contains (event )).isTrue ();
159+ ac .close ();
160+ parent .close ();
161+ }
162+
81163@ Test
82164@ SuppressWarnings ("resource" )
83165void testProgrammaticEventListener () {
@@ -96,6 +178,27 @@ void testProgrammaticEventListener() {
96178ac .close ();
97179}
98180
181+ @ Test
182+ @ SuppressWarnings ("resource" )
183+ void testProgrammaticEventListenerOnParentContext () {
184+ List <Auditable >events =new ArrayList <>();
185+ ApplicationListener <AuditablePayloadEvent <String >>listener =events ::add ;
186+ ApplicationListener <AuditablePayloadEvent <Integer >>mismatch = (event ->event .getPayload ());
187+
188+ ConfigurableApplicationContext parent =new GenericApplicationContext ();
189+ parent .addApplicationListener (listener );
190+ parent .addApplicationListener (mismatch );
191+ parent .refresh ();
192+ ConfigurableApplicationContext ac =new GenericApplicationContext (parent );
193+ ac .refresh ();
194+
195+ AuditablePayloadEvent <String >event =new AuditablePayloadEvent <>(this ,"xyz" );
196+ ac .publishEvent (event );
197+ assertThat (events .contains (event )).isTrue ();
198+ ac .close ();
199+ parent .close ();
200+ }
201+
99202@ Test
100203@ SuppressWarnings ("resource" )
101204void testProgrammaticPayloadListener () {
@@ -108,10 +211,75 @@ void testProgrammaticPayloadListener() {
108211ac .addApplicationListener (mismatch );
109212ac .refresh ();
110213
111- AuditablePayloadEvent <String >event =new AuditablePayloadEvent <>(this ,"xyz" );
112- ac .publishEvent (event );
113- assertThat (events .contains (event .getPayload ())).isTrue ();
214+ String payload ="xyz" ;
215+ ac .publishEvent (payload );
216+ assertThat (events .contains (payload )).isTrue ();
217+ ac .close ();
218+ }
219+
220+ @ Test
221+ @ SuppressWarnings ("resource" )
222+ void testProgrammaticPayloadListenerOnParentContext () {
223+ List <String >events =new ArrayList <>();
224+ ApplicationListener <PayloadApplicationEvent <String >>listener =ApplicationListener .forPayload (events ::add );
225+ ApplicationListener <PayloadApplicationEvent <Integer >>mismatch =ApplicationListener .forPayload (Integer ::intValue );
226+
227+ ConfigurableApplicationContext parent =new GenericApplicationContext ();
228+ parent .addApplicationListener (listener );
229+ parent .addApplicationListener (mismatch );
230+ parent .refresh ();
231+ ConfigurableApplicationContext ac =new GenericApplicationContext (parent );
232+ ac .refresh ();
233+
234+ String payload ="xyz" ;
235+ ac .publishEvent (payload );
236+ assertThat (events .contains (payload )).isTrue ();
237+ ac .close ();
238+ parent .close ();
239+ }
240+
241+ @ Test
242+ @ SuppressWarnings ("resource" )
243+ void testPlainPayloadListener () {
244+ ConfigurableApplicationContext ac =new AnnotationConfigApplicationContext (PlainPayloadListener .class );
245+
246+ String payload ="xyz" ;
247+ ac .publishEvent (payload );
248+ assertThat (ac .getBean (PlainPayloadListener .class ).events .contains (payload )).isTrue ();
249+ ac .close ();
250+ }
251+
252+ @ Test
253+ @ SuppressWarnings ("resource" )
254+ void testPlainPayloadListenerOnParentContext () {
255+ ConfigurableApplicationContext parent =new AnnotationConfigApplicationContext (PlainPayloadListener .class );
256+ ConfigurableApplicationContext ac =new GenericApplicationContext (parent );
257+ ac .refresh ();
258+
259+ String payload ="xyz" ;
260+ ac .publishEvent (payload );
261+ assertThat (parent .getBean (PlainPayloadListener .class ).events .contains (payload )).isTrue ();
114262ac .close ();
263+ parent .close ();
264+ }
265+
266+
267+ static class NumberHolder <T extends Number > {
268+
269+ public NumberHolder (T number ) {
270+ }
271+ }
272+
273+
274+ @ Component
275+ public static class NumberHolderListener {
276+
277+ public final List <NumberHolder <Integer >>events =new ArrayList <>();
278+
279+ @ EventListener
280+ public void onEvent (NumberHolder <Integer >event ) {
281+ events .add (event );
282+ }
115283}
116284
117285
@@ -139,11 +307,16 @@ public void onEvent(Auditable event) {
139307}
140308}
141309
142- static class NumberHolder <T extends Number > {
143310
144- public NumberHolder (T number ) {
145- }
311+ @ Component
312+ public static class PlainPayloadListener {
313+
314+ public final List <String >events =new ArrayList <>();
146315
316+ @ EventListener
317+ public void onEvent (String event ) {
318+ events .add (event );
319+ }
147320}
148321
149322}