Skip to content

Inconsistent discovery of parameter names for selectors in custom actuator endpoints #31240

@ahubold

Description

@ahubold

Version is Spring Boot 2.6.7

I've seen the documentation recommending that "Java code that implements an endpoint should be compiled with -parameters". It says "should", not "must", and projects may have reasons not to do that, see for example https://stackoverflow.com/a/44075684/2086307.

If you don't compile with -parameters, a read operation like the following does not work when invoked by HTTP:

@ReadOperation
public Result operation(@Selector String name) {
org.springframework.boot.actuate.endpoint.invoke.MissingParametersException: 
Failed to invoke operation because the following required parameters were missing: name of type java.lang.String
	at org.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker.validateRequiredParameters(ReflectiveOperationInvoker.java:81)
	at org.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker.invoke(ReflectiveOperationInvoker.java:70)
	at org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation.invoke(AbstractDiscoveredOperation.java:60)
	at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$ServletWebOperationAdapter.handle(AbstractWebMvcEndpointHandlerMapping.java:353)

Interestingly, Spring Boot is still able to get the parameter name as the above exception message shows: "name of type java.lang.String"

This is because org.springframework.boot.actuate.endpoint.invoke.reflect.OperationMethod uses org.springframework.core.DefaultParameterNameDiscoverer, which can still retrieve parameter names even if the code wasn't compiled with -parameters. (It uses ASM in LocalVariableTableParameterNameDiscoverer). That's why the operation still expects an argument "name" in this example.

The problem here is that the code that supplies these arguments only uses java.lang.reflect.Parameter#getName, for example in org.springframework.boot.actuate.endpoint.web.annotation.RequestPredicateFactory#getPath and org.springframework.boot.actuate.endpoint.web.annotation.DiscoveredWebOperation#dashName. This leads to the synthesized argument name "arg0", which of course doesn't match the expected one "name".

This is inconsistent: The code to collect arguments from the request and the code that consumes these arguments use different strategies for naming if not compiled with -parameters. Either both should only uses synthesized "arg0"-like names, or both should also consider the more sophisticated approach using ASM.

Apart from compiling with -parameters, a workaround would be to rename the parameter to "arg0"

@ReadOperation
public Result operation(@Selector String arg0) { // DO NOT RENAME THE PARAMETER!

This is of course not very nice, and can easily break. It would be better if one could at least specify the argument name with an annotation, but I don't think that's possible?

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions