I have created a JAX-RS Whiteboard resource for a REST API, a Jaxrs Extension which uses Jackson to serialize/deserialize data, and I want to read some data which will be sent using POST method.
In the following code, the @GET method works, but I just get an exception with the @POST method. If I comment the try..catch block then there is no error and I can see the response text.
How can I get it to work?
The error:
WARNING: Exception in handleFault on interceptor org.apache.cxf.jaxrs.interceptor.JAXRSDefaultFaultOutInterceptor@7103319f
org.apache.cxf.interceptor.Fault: STREAMED
at org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:162)
...
Caused by: java.lang.IllegalStateException: STREAMED
at org.eclipse.jetty.server.Request.getReader(Request.java:1153)
The JaxrsResource
//The jax-rs path annotation for this service.
@Path("/api/v1")
//The JAX-RS annotation to specify that JSON is produced.
@Produces(MediaType.APPLICATION_JSON)
//Mark this class as a OSGi DS component.
@Component(service=MyApiImpl.class)
//Mark this class as a JAX-RS resource type that should be processed by the JAX-RS whiteboard.
@JaxrsResource
//Mark this class to requiring a serializer capable of supporting JSON.
@JSONRequired
public class MyApiImpl {
@Reference(service=LoggerFactory.class)
private Logger logger;
@GET
@Path("/test")
public String getTest() {
return "Hello World, GET-test ok! --> ";
}
@POST
@Path("/resources")
@Consumes(MediaType.APPLICATION_JSON)
public String postFoo(@Context HttpServletRequest request) {
try {
BufferedReader reader = request.getReader();
} catch (IOException e) {
logger.error("error on input data", e);
return e.getLocalizedMessage();
}
return "Hello World, POST ok! --> ";
}
}
The JaxrsExtension
:
/**
* Very simple converter implementation to convert List of Strings to a JSON String.
*/
// OSGi DS component annotation with prototype scope to ensure that multiple instances can be requested
@Component(scope = ServiceScope.PROTOTYPE)
// Marks the service as a JAX-RS extension type that should be processed by the JAX-RS whiteboard.
@JaxrsExtension
// Marks the component as providing a serializer capable of supporting the named media type, in this case the standard media type for JSON.
@JaxrsMediaType(MediaType.APPLICATION_JSON)
public class JacksonJsonConverter<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {
@Reference(service=LoggerFactory.class)
private Logger logger;
private final Converter converter = Converters.newConverterBuilder()
.rule(String.class, this::toJson)
.rule(this::toObject)
.build();
private ObjectMapper mapper = new ObjectMapper();
private String toJson(Object value, Type targetType) {
try {
String retval = mapper.writeValueAsString(value);
System.out.println("trying 'toJson': " + retval);
return retval;
//return mapper.writeValueAsString(value);
} catch (JsonProcessingException e) {
logger.error("error on JSON creation", e);
return e.getLocalizedMessage();
}
}
private Object toObject(Object o, Type t) {
if(List.class.getName().equals(t.getTypeName())) {
try {
System.out.println("trying 'toObject'");
return this.mapper.readValue((String) o, List.class);
} catch (IOException e) {
logger.error("error on JSON parsing", e);
}
}
return ConverterFunction.CANNOT_HANDLE;
}
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
//return MediaType.APPLICATION_JSON_TYPE.isCompatible(mediaType) || mediaType.getSubtype().endsWith("+json");
boolean retval = MediaType.APPLICATION_JSON_TYPE.isCompatible(mediaType) || mediaType.getSubtype().endsWith("+json");
System.out.println("'isWriteable' returns: " + retval);
return retval;
}
@Override
public void writeTo(T t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
throws IOException, WebApplicationException {
String json = converter.convert(t).to(String.class);
entityStream.write(json.getBytes());
System.out.println("'writeTo' was called");
}
@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
// return MediaType.APPLICATION_JSON_TYPE.isCompatible(mediaType) || mediaType.getSubtype().endsWith("+json");
boolean retval = MediaType.APPLICATION_JSON_TYPE.isCompatible(mediaType) || mediaType.getSubtype().endsWith("+json");
System.out.println("'isReadable' returns: " + retval);
return retval;
}
@Override
public T readFrom(Class<T> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
throws IOException, WebApplicationException {
BufferedReader reader = new BufferedReader(new InputStreamReader(entityStream));
System.out.println("'readFrom' was called");
return (T) converter.convert(reader.readLine()).to(genericType);
}
}
I would expect to get the body contents to apply the business logic.