Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AvailabilityChangeEvent does not carry generic information #21898

Closed
alimate opened this issue Jun 12, 2020 · 6 comments
Closed

AvailabilityChangeEvent does not carry generic information #21898

alimate opened this issue Jun 12, 2020 · 6 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@alimate
Copy link
Contributor

alimate commented Jun 12, 2020

Synopsis

I was experimenting with the new Kubernetes probes support in Spring Actuator. The official documentation suggests that we can listen to changes in readiness or liveness by registering a bean like the following:

@Component
public class ReadinessStateExporter {

    @EventListener
    public void onStateChange(AvailabilityChangeEvent<ReadinessState> event) {
        switch (event.getState()) {
        case ACCEPTING_TRAFFIC:
            // create file /tmp/healthy
        break;
        case REFUSING_TRAFFIC:
            // remove file /tmp/healthy
        break;
        }
    }
}

However, running an application with a bean like this fails with:

java.lang.ClassCastException: org.springframework.boot.availability.LivenessState cannot be cast to org.springframework.boot.availability.ReadinessState

Details

This failure makes sense because during the application startup, Spring Boot fires both AvailabilityChangeEvent<ReadinessState> and AvailabilityChangeEvent<LivenessState> events.
Due to erasure, both of those events would e handled by the ReadinessStateExporter. Quite reasonably, the LivenessState change event should fail because we can't cast LivenessState to ReadinessState.
Hence the error:

java.lang.ClassCastException: org.springframework.boot.availability.LivenessState cannot be cast to org.springframework.boot.availability.ReadinessState
	at ReadinessChangedListener.onStateChange(ReadinessChangedListener.java:13) ~[classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_161]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_161]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161]
	at org.springframework.context.event.ApplicationListenerMethodAdapter.doInvoke(ApplicationListenerMethodAdapter.java:305) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.event.ApplicationListenerMethodAdapter.processEvent(ApplicationListenerMethodAdapter.java:190) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.event.ApplicationListenerMethodAdapter.onApplicationEvent(ApplicationListenerMethodAdapter.java:153) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:403) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:360) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.boot.availability.AvailabilityChangeEvent.publish(AvailabilityChangeEvent.java:81) ~[spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at org.springframework.boot.availability.AvailabilityChangeEvent.publish(AvailabilityChangeEvent.java:67) ~[spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at org.springframework.boot.context.event.EventPublishingRunListener.started(EventPublishingRunListener.java:103) ~[spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at org.springframework.boot.SpringApplicationRunListeners.started(SpringApplicationRunListeners.java:71) ~[spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:321) [spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) [spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at DemoApplication.main(DemoApplication.java:10) [classes/:na]

I guess we probably should change the documentation as the sample code makes the Spring App to fail at startup.
Also, we could listen to AvailabilityState :

@Component
public class ReadinessChangedListener {

    @EventListener
    public void onStateChange(AvailabilityChangeEvent<AvailabilityState> event) {
        // check if it's liveness or readiness or anything else
    }

}

Please kindly let me know what you think of this.
Cheers!

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jun 12, 2020
@philwebb
Copy link
Member

I was under the impression that ApplicationListenerMethodAdapter would deal with generics correctly. Do you by any chance have a sample application that reproduces the problem?

@philwebb philwebb added the status: waiting-for-feedback We need additional information before we can continue label Jun 12, 2020
@alimate
Copy link
Contributor Author

alimate commented Jun 12, 2020

Sure. This a simple sample application
https://github.com/alimate/boot-probes

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Jun 12, 2020
@philwebb philwebb changed the title Generic Type Erasure and AvailabilityChangeEvent Listener AvailabilityChangeEvent does not carry generic information Jun 12, 2020
@philwebb philwebb added type: bug A general bug and removed status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged labels Jun 12, 2020
@philwebb philwebb self-assigned this Jun 12, 2020
@philwebb philwebb added this to the 2.3.2 milestone Jun 12, 2020
@philwebb
Copy link
Member

Thanks for the detailed report and the sample app. I've tracked this down and things should work as expected with the next release. In the meantime, I'm afraid you'll have to use your workaround.

@lifejwang11
Copy link

Sure. This a simple sample application
https://github.com/alimate/boot-probes

hello,i see this problem to understand the source code,try this without changing the source code,I hope that will be helpful.

     @EventListener
    public void onStateChange(AvailabilityChangeEvent<AvailabilityState> event) {
        if (event.getState() instanceof LivenessState){
            switch ((LivenessState)event.getState()){
                case  CORRECT:
                    System.out.println("CORRECT");
                    break;
                case BROKEN:
                    System.out.println("BROKEN");
                    break;
                default:

            }
        }else if(event.getState() instanceof ReadinessState){
            switch ((ReadinessState)event.getState()){
                case  ACCEPTING_TRAFFIC:
                    System.out.println("ACCEPTING_TRAFFIC");
                    break;
                case REFUSING_TRAFFIC:
                    System.out.println("REFUSING_TRAFFIC");
                    break;
                default:

            }
        }
    }

@alimate
Copy link
Contributor Author

alimate commented Jun 13, 2020

@lifejwang11 Definitely helpful. Thanks.
Moreover, this bug apparently is going to be fixed by 2.4.0 release.

@bclozel
Copy link
Member

bclozel commented Jun 13, 2020

@alimate the fix is scheduled for 2.3.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

5 participants