Microprofile java.lang.ClassCastException: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream incompatible - microprofile

I'm using microprofile 3.2 to call out to a rest webservice that returns a java bean in a Response entity. When I try to extract the bean from the response though I get a
java.lang.ClassCastException: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream incompatible with <class>
error.
E.g.
My bean:
public class MyBean {
private int id;
public int getId() { return id; }
public void setId(final int id) { this.id = id; }
}
REST WS api interface:
#GET
#Path("/{id}")
Response getBean(#PathParam("id") Integer id);
REST implementation class:
public Response getBean(final Integer id) {
MyBean myBean = new Service().getBean(id);
return Response.ok(myBean).build();
}
RestClient:
IBeanResource beanResource =
RestClientBuilder.newBuilder().baseUri(apiURI).build(IBeanResource.class);
Response beanResponse = beanResource.getBean(100);
if (beanResponse.getStatus() == Response.Status.OK.getStatusCode()) {
MyBean bean = (MyBean) beanResponse.getEntity();
}
Error fires on line
MyBean bean = (MyBean) beanResponse.getEntity();
Has anyone seen this before? The documentation isn't great.

Yes that would be the expected behavior. If you inspect the value of beanResponse in debug you will see the Response is of type InboundJaxrsResonse and entity is nothing but of type HttpUrlConnector. Which is why when you try to cast it to your custom bean class it throws ClassCastException. You can try any of the following approach:
you could instead do as below
String jsonString = beanResponse.readEntity(String.class);
The above will give you the JSON response as String and then you may convert it to your respective class using libraries such as gson or jackson of your choice.
In your REST WS api interface instead of returning Response return your model MyBean. As per the Microprofile rest Client spec it states the implementation of Microprofile Rest client must provide a built-in JSON-P entity provider and if it supports JSON-B then it must provide JSON-B entity provider.
microprofile-rest-client-spec-2.0

Thanks for the reply. I'll take a look at the spec again for returning models. I like the idea of capturing the response rather than the model so I have any header or status information too e.g. how would a 404 be handed if the resource could not be found?
I was able to get around this by reading the InputStream and using jsonb to bind to the bean
InputStream is = (InputStream) beanResponse.getEntity();
return jsonb.fromJson(is, MyBean.class);

Related

How to remove/handle irrelevant or bad sort parameters from http url using Pageable interface in spring boot?

How to remove/handle irrelevant or bad sort parameters from http url using Pageable interface in spring boot?
For e.g. I have a query like
http://localhost:8080/all?sort=firstName,asc&sort=nosuchfield,asc
How can I handle or remove the irrelevant field "nosuchfield"?
Also, how can I limit sort parameters in URL?
If the sorting field doesn't present in the database then below exception will be thrown by Spring JPA.
org.springframework.data.mapping.PropertyReferenceException: No property nosuchfield found for type <TYPE>!
at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:94)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:382)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:358)
However, the exception can be handled using various types. Ultimately, you can just log it or transform it into any custom exception. As per my requirement, I have transformed it into a custom exception.
Using AOP
#Aspect
#Component
public class UnKnownColumnSortingExceptionHandler {
#AfterThrowing(pointcut = "execution(* com.repositorypackage.*.*(..))", throwing = "exception")
public void executeWhenExceptionThrowninRepository(JoinPoint jp, Throwable ex) {
if (ex instanceof PropertyReferenceException) {
throw new CustomException("Invalid Database operation");
}
}
}
Using #ControllerAdvice(Exception handling in Application wise)
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
public GlobalExceptionHandler() {}
#ExceptionHandler({PropertyReferenceException.class})
public ResponseEntity<Void> handleAllExceptions(Exception ex, WebRequest req) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Exception handling in Controller wise
Add the below piece of code to your controller
#ExceptionHandler({PropertyReferenceException.class})
public ResponseEntity<Void> handleAllExceptions(Exception ex, WebRequest req)
{
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}

spring can't instantiate UriInfo in rest service

I try to use UriInfo to get the list of request parameters, here is my code :
#RestController public class MyController {
#RequestMapping(value = "/documents", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public Object getDocuments( #Context UriInfo uriInfo,
#RequestParam(value = "sta", required = false) String param1, #RequestParam(value = "sta2", required = false) String param2){
MultivaluedMap<String, String> queryParamList = uriInfo.getQueryParameters();
}
this code cause this exception :
org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.ws.rs.core.UriInfo]: Specified class is an interface
thanks for help
It's because UriInfo isn't a Spring MVC object. It is a JAX-RS object and you are not using JAX-RS, you're using Spring MVC. With Spring MVC, if you just want the parameter map, you can just inject it with #RequestParam
public Object getDocuments(#RequestParam MultiValueMap<String, String> requestParams)
Note, the MultiValueMap is a Spring class, it's not the JAX-RS MultivaluedMap.
See also:
Spring MVC - How to get all request params in a map in Spring controller?

Content Type Not Set Junit REST POST test

I have a Spring Boot REST application. The unit tests for all of the GET requests are working perfectly; however, the POST requests are all returning
java.lang.AssertionError: Content type not set
Here is the controller:
#RestController
public class ClassificationController {
private IClassificationService classificationService;
#Autowired
public ClassificationController(IClassificationService classificationService) {
this.classificationService = classificationService;
}
#RequestMapping(value="/category", method = RequestMethod.POST, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
#ResponseStatus(HttpStatus.CREATED)
#ResponseBody
public CategoryDTO createCategory(#RequestBody final CategoryDTO category) throws MctException {
return classificationService.createCategory(category);
}
The unit test I have is:
#RunWith(MockitoJUnitRunner.class)
public class ClassificationControllerTest {
#Mock
private IClassificationService classificationService;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new ClassificationController(classificationService)).build();
}
#Test
public void createCategoryTest() throws Exception {
String jsonTask = String.format("{\"id\": \"2\",\"categoryName\": \"Category Name 2\"}");
MvcResult result = mockMvc.perform(post("/category")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(jsonTask))
.andDo(MockMvcResultHandlers.print())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(content().string(containsString("\"id\":2")))
.andExpect(content().string(containsString("\"categoryName\":\"Category Name 2\"")))
.andExpect(status().isCreated())
.andReturn();
}
I have also tried this with a CategoryDTO object instead of the String jsonTask with the same result.
I discovered it was just failing on that assertion because it was the first one, but it just wasn't returning anything from the endpoint. I am returning the content type because it is returning the object that is being inserted so a content type is valid. I ended up changing my test create the content JSON using an ObjectMapper and then I had to add an equals method on my domain object....once I added the equals method, the test passed. I didn't realize the mock framework used that method.
#Test
public void createClassTest() throws Exception {
String jsonInString = objectMapper.writeValueAsString(singleClass);
when(classificationService.createClass(5, singleClass)).thenReturn(singleClass);
MvcResult result = mockMvc.perform(post("/class/5")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(jsonInString))
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(content().string(containsString("\"id\":1")))
.andExpect(content().string(containsString("\"className\":\"Test Class Name 1\"")))
.andExpect(status().isCreated())
.andReturn();
verify(classificationService).createClass(5, singleClass);
}
Judging by the assertion error, it seems the endpoint is not returning a MediaType.APPLICATION_JSON_UTF8. Try removing the contentType check or debugging and seeing what the endpoint is actually returning. Once again, judging by the error you are seeing, seems it's not returning any content type at all. So you should probably be checking that no content type is set.
I know typically the POST request I usually test do not return a contentType at all.
After all, it could be that endpoint is actually doing something incorrectly if you do expect the content type to be set.

Marshalling List<String> with JAX-RS

I'm used to working with jax-ws where a wsdl file is generated, and a client can then be generated based on this wsdl file and its xsd(s) using a maven plugin. Using this client is no hassle at at, and you don't have to really think about what happens in the background, like marshalling, http transfer and such.
I'm currently working on a jax-rs project using jaxb to unmarshal objects. One of the methods there returns a list of strings, but it seems that jaxb does not know how to marshal this, which is kinda surprising as it does know how to marshal a list of entities (ex, customers).
Also, I have written a client for the jax-rs service on my own, handling both http responses and unmarshalling of the payload using jaxb. Marshalling and unmarshalling with jaxb is a real hassle since it cannot automatically marshall or unmarshall list of entities that is added to its context, even less lists of strings.
I would like to know if there is some neat way to get all of this for free using restful webservices? This would have to be quite lightweight, and the clients must be easy to distribute.
Thanks!
Runar
The service method that is not working using jaxrs and jaxb:
#GET
#Path("/{customerId}")
#Produces(MediaType.APPLICATION_XML)
public List<String> isCustomerLocked(#PathParam("customerId") Long customerId) {
}
Client code that attempts to marshall/unmarshall text payload. Classes added to the jaxbcontext not shown:
javax.xml.bind.Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(obj, stringwriter)
javax.xml.bind.Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.unmarshal(inputstream)
I'd use JAXB to wrap the data. For a simple List<String> this may look as overkill. But in most cases you want to un-/marshall Resource Representations, not simple objects.
Remember: REST ist not RPC!
If you really want tom un-/marshall List<String> write a JAX-RS Provider. But I'd prefer using JAXB.
S.java
#XmlRootElement
public class S {
private String s;
public S() {
}
public S(String s) {
this.s = s;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
}
Ss.java
#XmlRootElement(name="ss-wrapper")
public class Ss {
private List<S> ss;
public List<S> getSs() {
return ss;
}
public void setSs(List<S> ss) {
this.ss = ss;
}
public Ss(List<S> ss) {
this.ss = ss;
}
public Ss() {
}
}
JAX-RS class
#Path("/strings")
#GET
#Produces(MediaType.APPLICATION_XML)
public Response getListOfStrings() {
S s1 = new S("foo");
S s2 = new S("bar");
List<S> strings = new ArrayList<S>();
strings.add(s1);
strings.add(s2);
Ss ss = new Ss(strings);
return Response.ok(ss).build();
}
XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ss-wrapper>
<ss>
<s>foo</s>
</ss>
<ss>
<s>bar</s>
</ss>
</ss-wrapper>

JBoss AS7 + RestEasy : How to enable a custom MessageBodyReader using #Provider did nothing

I have a wierd problem. I'm using #Provider to annote my Mapper Exception and it's work fine, but when I'm using it to annote the class below it won't work at all.
#Consumes("application/x-java-serialized-object")
#Provider
public class JAXBSpecificMarshaller implements MessageBodyReader
{
#PersistenceContext(unitName = "primary", type = PersistenceContextType.EXTENDED)
private EntityManager em;
#Override
public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType)
{
return type.isAnnotationPresent(XmlRootElement.class);
}
#Override
public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException
{
try
{
// DataAdapter dataAdapter = new DataAdapter(em);
//unmarshaller.setAdapter(dataAdapter);
System.out.println(type.getName());
JAXBContext ctx = JAXBContext.newInstance(type);
Unmarshaller unmarshaller = ctx.createUnmarshaller();
return unmarshaller.unmarshal(entityStream);
}
catch ( JAXBException ex )
{
throw new RuntimeException(ex);
}
}
}
My main reason is to be able to use specific adapter to retrieve an object by passing its id in the input xml. I followed this Serialize a JAXB object via its ID? . But to initialize the adapter with my enitymanger I was told to use MessageBodyReader to do so.
Thank you for your help.
Can you provide some context on what application server you are deploying to and what JAX-RS implementation you are using?
I had a similar problem with RESTeasy on JBoss AS 7 trying to implement a #Produces #Provider for some JAXB annotated classes, but the provided JAXB marshaller provider from RESTeasy always took precedence, and my marshaller never got executed.
My solution was to write implementations for custom JAXBContextFinder, ContextResolver and JAXBContext. I used resteasy-jettison-provider source code as a recipe for implementing my own handlers. http://docs.jboss.org/resteasy/docs/2.0.0.GA/userguide/html/Built_in_JAXB_providers.html