I have an issue reading a GenericType on my webservice client.
Here is my webservice :
#GET
#Path("/results")
#Produces(MediaType.APPLICATION_JSON)
public Response getPlayerResults(#QueryParam("nick") String nick, #Context HttpServletRequest request,
#Context HttpServletResponse response) {
// if (!isAuth(request, response)) {
// authError(response);
// }
System.out.println(nick);
GenericEntity<List<JSONTournament>> entity = new GenericEntity<List<JSONTournament>>(
MongoTournaments.getPlayerResults(nick, null)) {
};
return Response.ok(entity).build();
}
My client :
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://localhost:8080/WS").path("players/results");
Response response = webTarget.queryParam("nick", nick).request(MediaType.APPLICATION_JSON).get();
System.out.println(nick);
tournaments = response.readEntity(new GenericType<List<JSONTournament>>() {
});
And I get this Exception and a strange media-type (text/html) instead of application/json... :
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=text/html;charset=utf-8, type=interface java.util.List, genericType=java.util.List<com.winascrap.database.model.JSONTournament>.
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:232)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:156)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1085)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:853)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:812)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:368)
at org.glassfish.jersey.client.InboundJaxrsResponse$2.call(InboundJaxrsResponse.java:122)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:419)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:119)
I don't understand what is missing or not done correctly.
My objects have #XmlRootElement annotations, I use jersey.core, jersey.media, and moxy as dependencies... (but maybe I forgot on dependencie or configuration ?) :
Thanks for your help !
Solved !
I missed to add an empty constructor to my custom type annoted with #XmlRootElement . It's working only adding a constructor without params.
Related
I am trying to make a generic REST api using Jersey and I followed the following blog for this:
https://theza.ch/2009/08/11/uri-extensions-in-jersey/
So what is happening is that the server is working fine when I use .xml in my url and when I use .json, it gives a 500 Internal Server error. I have tried different things,but to no avail. Could anyone by any change know why this is happening in json and not for xml and how to fix this?
My code looks something like this:
#GET
#Path("/order/{product-key}/getorderid")
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getOrderIdByDomain(#Context HttpServletRequest request,
#PathParam("product-key") final String productKey,
#QueryParam("domain-name") final String domainName ) throws Exception
{
try
{
Integer response = doSomething();
return Response.status(200).entity(response).build();
}
catch (Exception lbe)
{
Hashtable response = new Hashtable();
response.put("Error",lbe.getMessage());
return Response.status(400).entity(response).build();
}
}
UPDATE:
After adding the jersey-json dependency, the 500 error changed to 200 OK but I am still getting an empty response. For xml, I am getting the expected response. Did someone face a similar issue? Please suggest something because I have tried a few things from other answers but it doesn't seem to be working.
StackTrace:
Caused by: java.lang.AbstractMethodError
at org.codehaus.jackson.map.AnnotationIntrospector$Pair.findSerializer(AnnotationIntrospector.java:1148)
at org.codehaus.jackson.map.ser.BasicSerializerFactory.findSerializerFromAnnotation(BasicSerializerFactory.java:362)
at org.codehaus.jackson.map.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:252)
at org.codehaus.jackson.map.ser.StdSerializerProvider._createUntypedSerializer(StdSerializerProvider.java:782)
at org.codehaus.jackson.map.ser.StdSerializerProvider._createAndCacheUntypedSerializer(StdSerializerProvider.java:735)
at org.codehaus.jackson.map.ser.StdSerializerProvider.findValueSerializer(StdSerializerProvider.java:344)
at org.codehaus.jackson.map.ser.StdSerializerProvider.findTypedValueSerializer(StdSerializerProvider.java:420)
at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:601)
at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:256)
at org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:1606)
at org.codehaus.jackson.jaxrs.JacksonJsonProvider.writeTo(JacksonJsonProvider.java:520)
at com.sun.jersey.json.impl.provider.entity.JacksonProviderProxy.writeTo(JacksonProviderProxy.java:160)
at com.sun.jersey.spi.container.ContainerResponse.write(ContainerResponse.java:306)
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1437)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1349)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1339)
at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:416)
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:699)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
... 48 more
I am using the following dependencies: jersey-server, jersey-json version 1.8.
After trying for long to fix this, i switched to genson,
<dependency>
<groupId>com.owlike</groupId>
<artifactId>genson</artifactId>
<version>1.3</version>
</dependency>
It worked very easily. Will try to find later why Jackson didn't work.
After adding jar dependency as suggested in comment,
you need have additional parameter to decide xml or json named 'format' ,so now you can do like this by changing return statement as below
#GET
#Path("/order/{product-key}/{format}/getorderid")
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getOrderIdByDomain(#Context HttpServletRequest request,
#PathParam("product-key") final String productKey,#PathParam("format") final String format,
#QueryParam("domain-name") final String domainName ) throws Exception
{
try
{
Integer response = doSomething();
return Response
// Set the status and Put your entity here.
.ok(entity)
// Add the Content-Type header to tell Jersey which format it should marshall the entity into.
.header(HttpHeaders.CONTENT_TYPE, "json".equals(format) ? MediaType.APPLICATION_JSON : MediaType.APPLICATION_XML)
.build();
}
catch (Exception lbe)
{
Hashtable response = new Hashtable();
response.put("Error",lbe.getMessage());
return Response.status(400).entity(response).build();
}
}
I´m using swagger on my java EE7 application (Glassfish as application server). Everything works fine except for a method with FormDataParam, which gave me the tradicional error:
org.glassfish.jersey.server.ContainerException: java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.AnnotationIntrospector.findPropertyIndex(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Integer;
I tried everything, but is just a method, so I do not want so badly this method in my swagger.json
How can I exclude this method from swagger. I tried:
#ApiModelProperty(hidden = true) and #ApiOperation(value="",hidden = true)
#POST
#Path("something")
#Produces(MediaType.TEXT_PLAIN)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response newsomething(#FormParam("something") String something,#Context HttpServletRequest request, #Context HttpServletResponse response) throws IOException {
return "something";
}
What I´m doing wrong?
It's related with Glassfish, it use different version of Jackson. You need to upgrade Glassfish/Jackson. More details:
java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.AnnotationIntrospector.findPropertyIndex(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Integer solution https://github.com/swagger-api/swagger-core/issues/1001
Upgrading Jackson in GlassFish 4.1
com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector not found --- while using Swagger
java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.AnnotationIntrospector.findPropertyIndex(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Integer; solution - https://groups.google.com/forum/#!topic/swagger-swaggersocket/uQEn-anX_Og
java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.AnnotationIntrospector.findPropertyDesc solution https://groups.google.com/forum/#!topic/swagger-swaggersocket/x-Pz_3orTCE
For me adding #ApiModelProperty(hidden = true) in paths worked
Is it wrong or correct?
#ApiModelProperty(hidden = true)
#GET
#Produces({MediaType.APPLICATION_JSON})
#ApiOperation(value = "return getApi ",
tags = {"getApi"},
notes = "Returns a Array of getApi",
hidden = true
)
#ApiResponses(value = {
#ApiResponse(response = GetApi.class, message = "", code = 200)
})
#Path("getApi")
public Response getApi(#Context HttpHeaders httpHeaders, #BeanParam QueryParamBean queryParamBean) {
// codes..
}
I know the question is weird. Unfortunately I have a service that requires everything to have the header ContentType=application/x-www-form-urlencoded, eventhough the body is JSON
I am trying to use JAX-RS 2.0 ClientBuilder to call it:
String baseUrl = "http://api.example.com/";
JSONObject body = new JSONObject();
body.put("key", "value");
Client client = ClientBuilder.newClient();
client.register(new LoggingFilter());
Builder builder = client.target(baseUrl).path("something").request();
Invocation inv = builder
.header("Content-type", MediaType.APPLICATION_FORM_URLENCODED)
.buildPost(Entity.json(body));
Response response = inv.invoke();
int status = response.getStatus();
// I get 415, unsupported media type (in this case is unexpected)
I have checked my logs and I eventhough I am setting application/x-www-form-urlencoded (via the MediaType) the request appearantly has the Content-type of application/json
How can I force the request to have the Content-type I want?
BTW: This is my custom logger:
public class LoggingFilter implements ClientRequestFilter {
private static final Logger LOG = Logger.getLogger(LoggingFilter.class.getName());
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
LOG.log(Level.INFO, "body");
LOG.log(Level.INFO, requestContext.getEntity().toString());
LOG.log(Level.INFO, "headers");
LOG.log(Level.INFO, requestContext.getHeaders().toString());
}
}
And these are the logs I get:
com.acme.LoggingFilter I body
com.acme.LoggingFilter I {"key":"value"}
com.acme.LoggingFilter I headers
com.acme.LoggingFilter I {Content-type=[application/json]}
The problem with trying to use one of the static Entity helper methods is that it overrides any previous Content-Type header you may have set. In your current case, Entity.json automatically sets the header to application/json.
Instead of using the .json method, you can just use the general purpose Entity.entity(Object, MediaType) method. With your current case though, you can just do Entity.entity(body, MediaType.APPLICATION_FORM_URLENCODED_TYPE) though. The reason is that the client will look for a provider that knows how to serialize a JSONObject to application/x-www-form-urlencoded data, which there is none. So you will need to first serialize it to a String. That way the provider that handles application/x-www-form-urlencoded doesn't need to serialize anything. So just do
Entity.entity(body.toString(), MediaType.APPLICATION_FORM_URLENCODED_TYPE);
So I wanted to know my web service's client's locale or ip etc.. How do I get it?
My endpoint method:
#POST
#Produces({MediaType.APPLICATION_JSON})
#Consumes({MediaType.APPLICATION_JSON})
#Path("/{EmployeeID}/Shifts/{ShiftID}/Confirm")
public Response confirmShift(#PathParam("EmployeeID")String employeeId, String params, #PathParam("ShiftID")String tbId);
How I get it in interceptor:
Map<String, List> headers = (Map<String, List>) message.get(Message.PROTOCOL_HEADERS);
I think protocol header must contain this info, I havn't checked it by the way. But how do I get it in web service.
Note: I want to avoid getting/setting stuff in cxf request context.
You need to inject MessageContext into your method, which contains HTTP servlet request.
For e.g.:
#POST
#Produces({MediaType.APPLICATION_JSON})
#Consumes({MediaType.APPLICATION_JSON})
#Path("/{EmployeeID}/Shifts/{ShiftID}/Confirm")
public Response confirmShift(#PathParam("EmployeeID") String employeeId,
String params,
#PathParam("ShiftID") String tbId,
#Context MessageContext context){
HttpServletRequest request = context.getHttpServletRequest();
String ip = request.getRemoteAddr();
/** ..... **/
}
Also there are some other ways of getting HTTP servlet request, one would be:
Message message = PhaseInterceptorChain.getCurrentMessage();
HttpServletRequest httpRequest = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST);
Hope this helps.
Update 02/05/2018 (about 4 years later)...I tested this again as people have been upvoting my question/answer and Sotirios Delimanolis is correct that I should not have to write the code in my answer to make this work. I used basically the same RestTemplate/REST service setup as shown in my question with the REST service having a confirmed response content type of application/json and RestTemplate was able to process the response with no issues into a Map.
I'm invoking a rest service that returns JSON like this:
{
"some.key" : "some value",
"another.key" : "another value"
}
I would like to think that I can invoke this service with a java.util.Map as the response type but that's not working for me. I get this exception:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map]
Should I just specify String as the response type and convert the JSON to a Map?
Edit I
Here's my restTemplate call:
private Map<String, String> getBuildInfo(String buildUrl) {
return restTemplate.getForObject(buildUrl, Map.class);
}
Here's how I'm setting up the restTemplate:
#PostConstruct
public void initialize() {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new ClientHttpRequestInterceptor() {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpRequestWrapper requestWrapper = new HttpRequestWrapper(request);
requestWrapper.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
return execution.execute(requestWrapper, body);
}
});
restTemplate.setInterceptors(interceptors);
}
Edit II
Full error message:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map] and content type [application/octet-stream]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:108) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:549) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:502) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:239) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at idexx.ordering.services.AwsServersServiceImpl.getBuildInfo(AwsServersServiceImpl.java:96) ~[classes/:na]
RestTemplate has a method named exchange that takes an instance of ParameterizedTypeReference as parameter.
To make a GET request that returns a java.util.Map, just create an instance of an anonym class that inherits from ParameterizedTypeReference.
ParameterizedTypeReference<Map<String, String>> responseType =
new ParameterizedTypeReference<>() {};
You can then invoke the exchange method:
RequestEntity<Void> request = RequestEntity.get("http://example.com/foo")
.accept(MediaType.APPLICATION_JSON).build();
Map<String, String> jsonDictionary = restTemplate.exchange(request, responseType).getBody();
As I had previously noted, your error message is showing us that you are receiving application/octet-stream as a Content-Type.
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map] and content type [application/octet-stream]
As such, Jackson's MappingJackson2HttpMessageConverter cannot parse the content (it's expecting application/json).
Original answer:
Assuming your HTTP response's Content-Type is application/json and you have have Jackson 1 or 2 on the classpath, a RestTemplate can deserialize JSON like you have into a java.util.Map just fine.
With the error you are getting, which you haven't shown in full, either you've registered custom HttpMessageConverter objects which overwrite the defaults ones, or you don't have Jackson on your classpath and the MappingJackson2HttpMessageConverter isn't registered (which would do the deserialization) or you aren't receiving application/json.
I think you can achieve what you're aiming for simply using the RestTemplate and specifying a JsonNode as the response type.
ResponseEntity<JsonNode> response =
restTemplate.exchange(url, HttpMethod.GET, entity, JsonNode.class);
JsonNode map = response.getBody();
String someValue = map.get("someValue").asText();
Update 02/05/2018 (about 4 years later)...I tested this again as people have been upvoting my question/answer and Sotirios Delimanolis is correct that I should not have to write the code in my answer to make this work. I used basically the same RestTemplate/REST service setup as shown in my question with the REST service having a confirmed response content type of application/json and RestTemplate was able to process the response with no issues into a Map.
I ended up getting the contents as a String and then converting them to a Map like this:
String json = restTemplate.getForObject(buildUrl, String.class);
Map<String,String> map = new HashMap<String,String>();
ObjectMapper mapper = new ObjectMapper();
try {
//convert JSON string to Map
map = mapper.readValue(json, new TypeReference<HashMap<String,String>>(){});
} catch (Exception e) {
logger.info("Exception converting {} to map", json, e);
}
return map;
I know its old, but just for other people that may visit this topic:
If you want to register some additional converters with RestTemplateBuilder you also have to explicitly register default ones
#Bean
public RestTemplateBuilder builder() {
return new RestTemplateBuilder()
.defaultMessageConverters()
.additionalMessageConverters(halMessageConverter());
}
private HttpMessageConverter halMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper().registerModule(new Jackson2HalModule());
TypeConstrainedMappingJackson2HttpMessageConverter halConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class);
halConverter.setSupportedMediaTypes(Collections.singletonList(MediaTypes.HAL_JSON));
halConverter.setObjectMapper(objectMapper);
return halConverter;
}
This worked 100% for me
in client
Map<String, Object> mapRespuesta = new HashMap<>();
mapRespuesta.put("mensaje", "Process completed successfully");
mapRespuesta.put("idResponse", id);
return new ResponseEntity<Map<String, Object>>(mapRespuesta, HttpStatus.OK);
in which it makes the connection
ResponseEntity<Map> result = restTemplate.postForEntity(url, params, Map.class);
String id = result.getBody().get("idResponse").toString();
#GetMapping(value = "getSunny/{userId}")
public Map<String, SunnyVO> getSunny(#PathVariable int sunnyId) {
Map<String, SunnyVO> newObj = new HashMap<String, SunnyVO>();
final String url = "http://localhost:8085/Sunny/getSunny/{sunnyId}";
RestTemplate restTemplate = new RestTemplate();
newObj = restTemplate.getForObject(url, Map.class, sunnyId);
return newObj;
}
It is working for me ...