Ensure AvailabilityChangeEvent carries generics
Update `AvailabilityChangeEvent` to be a `PayloadEvent` and ensure
that the `getResolvableType` method returns a generic compatible
result.
Prior to this commit, a ClassCastExeption would be thrown if the
following event listener was declared:
@EventListener
void onEvent(AvailabilityChangeEvent<ReadinessState> event) {
...
}
Closes gh-21898
This commit is contained in:
parent
a65ca7ac2f
commit
1604545727
|
|
@ -19,6 +19,8 @@ package org.springframework.boot.availability;
|
|||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.PayloadApplicationEvent;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
|
@ -32,9 +34,7 @@ import org.springframework.util.Assert;
|
|||
* @author Phillip Webb
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public class AvailabilityChangeEvent<S extends AvailabilityState> extends ApplicationEvent {
|
||||
|
||||
private final S state;
|
||||
public class AvailabilityChangeEvent<S extends AvailabilityState> extends PayloadApplicationEvent<S> {
|
||||
|
||||
/**
|
||||
* Create a new {@link AvailabilityChangeEvent} instance.
|
||||
|
|
@ -42,9 +42,7 @@ public class AvailabilityChangeEvent<S extends AvailabilityState> extends Applic
|
|||
* @param state the availability state (never {@code null})
|
||||
*/
|
||||
public AvailabilityChangeEvent(Object source, S state) {
|
||||
super(source);
|
||||
Assert.notNull(state, "State must not be null");
|
||||
this.state = state;
|
||||
super(source, state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -52,7 +50,20 @@ public class AvailabilityChangeEvent<S extends AvailabilityState> extends Applic
|
|||
* @return the availability state
|
||||
*/
|
||||
public S getState() {
|
||||
return this.state;
|
||||
return getPayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResolvableType getResolvableType() {
|
||||
return ResolvableType.forClassWithGenerics(getClass(), getStateType());
|
||||
}
|
||||
|
||||
private Class<?> getStateType() {
|
||||
S state = getState();
|
||||
if (state instanceof Enum) {
|
||||
return ((Enum<?>) state).getDeclaringClass();
|
||||
}
|
||||
return state.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ import org.mockito.ArgumentCaptor;
|
|||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
|
@ -39,7 +43,7 @@ class AvailabilityChangeEventTests {
|
|||
@Test
|
||||
void createWhenStateIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new AvailabilityChangeEvent<>(this.source, null))
|
||||
.withMessage("State must not be null");
|
||||
.withMessage("Payload must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -49,6 +53,24 @@ class AvailabilityChangeEventTests {
|
|||
assertThat(event.getState()).isEqualTo(state);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResolvableType() {
|
||||
LivenessState state = LivenessState.CORRECT;
|
||||
AvailabilityChangeEvent<LivenessState> event = new AvailabilityChangeEvent<>(this.source, state);
|
||||
ResolvableType type = event.getResolvableType();
|
||||
assertThat(type.resolve()).isEqualTo(AvailabilityChangeEvent.class);
|
||||
assertThat(type.resolveGeneric()).isEqualTo(LivenessState.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResolvableTypeWhenSubclassedEnum() {
|
||||
SubClassedEnum state = SubClassedEnum.TWO;
|
||||
AvailabilityChangeEvent<SubClassedEnum> event = new AvailabilityChangeEvent<>(this.source, state);
|
||||
ResolvableType type = event.getResolvableType();
|
||||
assertThat(type.resolve()).isEqualTo(AvailabilityChangeEvent.class);
|
||||
assertThat(type.resolveGeneric()).isEqualTo(SubClassedEnum.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void publishPublishesEvent() {
|
||||
ApplicationContext context = mock(ApplicationContext.class);
|
||||
|
|
@ -61,4 +83,45 @@ class AvailabilityChangeEventTests {
|
|||
assertThat(event.getState()).isEqualTo(state);
|
||||
}
|
||||
|
||||
@Test
|
||||
void publishEvenToContextConsidersGenericType() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
|
||||
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
|
||||
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
|
||||
}
|
||||
|
||||
enum SubClassedEnum implements AvailabilityState {
|
||||
|
||||
ONE {
|
||||
|
||||
@Override
|
||||
String getDescription() {
|
||||
return "I have been overridden";
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
TWO {
|
||||
|
||||
@Override
|
||||
String getDescription() {
|
||||
return "I have aslo been overridden";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
abstract String getDescription();
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@EventListener
|
||||
void onLivenessAvailabilityChange(AvailabilityChangeEvent<LivenessState> event) {
|
||||
assertThat(event.getState()).isInstanceOf(LivenessState.class).isEqualTo(LivenessState.CORRECT);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue