spring-bootjmxspring-boot-actuatorendpoints

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


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!


Solution

  • 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.