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}}
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!
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.