Spring Boot Actuator 2.x @WriteOperation with an object as parameter

3k Views Asked by At

What I try to achieve: I want to create an endpoint that is accessible from web and jmx with an action that takes an object as a parameter

here is a simple example:


pom.xml

....

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.jolokia</groupId>
        <artifactId>jolokia-core</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

The Object class:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dog {

    protected String name;
    protected int age;
}

The endpoint class

@Component
@Endpoint(id = "dogs")
@Slf4j
public class EndpointDogsExperiment {

    protected List<Dog> dogs = new ArrayList<>();

    public EndpointDogsExperiment() {
        dogs.add(new Dog("dog0", 5));
        dogs.add(new Dog("dog1", 7));
        log.debug("dogs created {}", dogs.toString());
    }

    @WriteOperation
    public List<Dog> addDog(Dog dog) {
        log.debug("adding a dog \n{}", dog );
        dogs.add(dog);
        return dogs;
    }
}

the problem:

how do I "call" this operation

when I try HTTP POST with /actuator/dogs and body {"dog":{"name":"aaaa", "age":33}}

client gets

Response: status: 400 date: Sat, 31 Mar 2018 14:59:41 GMT connection: close transfer-encoding: chunked content-type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8

{"timestamp":"2018-03-31T14:59:41.778+0000","status":400,"error":"Bad Request","message":"No message available","path":"/actuator/dogs"}


and server log say


2018-03-31 17:59:41.766 WARN 12828 --- [nio-8081-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of java.lang.String out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of java.lang.String out of START_OBJECT token at [Source: (PushbackInputStream); line: 1, column: 8] (through reference chain: java.util.LinkedHashMap["dog"])


I have tried to create a @WriteOperation that has a String parameter and then HTTP POST body

{"paramName":"StringParamValue"}

works ok

why is it?

Thanks!

2

There are 2 best solutions below

5
Andy Wilkinson On BEST ANSWER

Endpoint operations don’t support complex input such as your Dog type. You could, however, consume separate name and age parameters and create the Dog in the operation’s implementation:

@WriteOperation
public List<Dog> addDog(String name, int age) {
    Dog dog = new Dog(name, age);
    log.debug("adding a dog \n{}", dog );
    dogs.add(dog);
    return dogs;
}

You can learn more in Spring Boot's reference documentation.

1
maximal maximus On

Just use the @RestControllerEndpoint(id = "features") class level annotation.

On the methods you then have to use your regular @PostMapping annotation and it should work