RESTEasy cannot take in #PathParam typed Long - jax-rs

I have seen in tutorial (Jersey) that there is possibility to pass in JAX-RS #PathParam of type Long.
But when I have tried to do this with RESTEasy I'm getting error:
21:50:32,353 WARN [org.jboss.resteasy.core.ExceptionHandler] (default task-15) failed to execute: javax.ws.rs.NotSupportedException: Could not find message body reader for type: long of content type: */*
at org.jboss.resteasy.core.interception.ServerReaderInterceptorContext.throwReaderNotFound(ServerReaderInterceptorContext.java:52)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.getReader(AbstractReaderInterceptorContext.java:73)
I tested it with Integer, but it also don't work...
22:14:45,590 WARN [org.jboss.resteasy.core.ExceptionHandler] (default task-18) failed to execute: javax.ws.rs.NotSupportedException: Could not find message body reader for type: class java.lang.Integer of content type: */*
at org.jboss.resteasy.core.interception.ServerReaderInterceptorContext.throwReaderNotFound(ServerReaderInterceptorContext.java:52)
Moreover I am trying to take this ID value as String and then convert it to Long using Long.valueOf(String s) or Long.parseLong(String s) and in both cases I am getting error
Caused by: java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:601)
at java.lang.Long.parseLong(Long.java:631)
at pl.salonea.jaxrs.UserAccountResource.getUserAccount(UserAccountResource.java:41)
It's the code of my web service resource method:
#GET
#Path("/{userId}")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public UserAccount getUserAccount(#PathParam("userId") String userId) {
return userAccountFacade.find(Long.parseLong(userId));
}
SOLVED
I have bad import not import javax.ws.rs.PathParam;, but something from web sockets PathParam... I just relay on IntelliJ automatic imports and haven't seen that it was wrong.

Related

Problems with a get request via RestClient in Groovy

I'm new to Groovy and a bit rusty on development.
I am trying to make a GET request to a woocommerce API. With Postman it works OK but not in Groovy it is not entirely clear to me what you need to cast from String to Map Entry.
I imagine it's something relatively simple but I can't find it.
Thanks a lot.
#Grab('com.github.groovy-wslite:groovy-wslite:1.1.2')
import groovy.json.JsonSlurper
import wslite.rest.*
def request
RESTClient client = new RESTClient("https://www.somewoomcersite.com")
def path = "/wp-json/wc/v3/customers"
request = client.get(path: path, headers: ["Authorization", "BLA BLA"])
Caught: java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Map$Entry
java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Map$Entry
at wslite.rest.RESTClient.createRequestParams(RESTClient.groovy:118)
at wslite.rest.RESTClient.this$2$createRequestParams(RESTClient.groovy)
at wslite.rest.RESTClient.executeMethod(RESTClient.groovy:93)
at wslite.rest.RESTClient.this$2$executeMethod(RESTClient.groovy)
at wslite.rest.RESTClient.get(RESTClient.groovy:49)
at wslite.rest.RESTClient.get(RESTClient.groovy)
at wslite.rest.RESTClient$get.call(Unknown Source)
at MAG.run(MAG.groovy:9)
An example json of the response would be this:
https://woocommerce.github.io/woocommerce-rest-api-docs/#list-all-customers

Correct media type for swagger spec for downloading zip file

I am creating an api which allows downloading a zip file, so for this I am looking for correct media type for sending this zip file as response in swagger "2.0" specification.
My current api spec looks like this
/config:
get:
produces:
- application/zip
responses:
200: # OK
description: All config files
schema:
type: string
format: binary
I have compiled this spec with "go-swagger" and implemented the backend for this, but when try to call this API I get this error
http: panic serving 127.0.0.1:20366: applicationZip producer has not yet been implemented
In the documentation of swagger I don't see this media type
Official swagger media types
So then what should be the correct media type if we want to provide an API to download a zip file.
Thanks in advance
You need to implement this.
The error occurs because of the following generated code:
func NewSampleAPI(spec *loads.Document) *SampleAPI {
return &SampleAPI{
...
ApplicationZipProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
return errors.NotImplemented("applicationZip producer has not yet been implemented")
}),
So after calling NewSampleAPI, you should set ApplicationZipProducer:
api := operations.NewSampleAPI(swaggerSpec)
api.ApplicationZipProducer = func(w io.Writer, data interface{}) error {
...
}
You should use "application/octet-stream" for implementing api that downloads file as an attachment , and since its Producer is already implemented by default so you won't face this issue.
As mentioned in other answer, using "application/octet-stream" partially solves the issue, but the browser does not know the file format. I think that his can be improved adding the correct headers to the response, that will tell the browser that the file has a zip extension. At least, I can download a file as ZIP and Firefox recognizes it.
#Operation(summary = "Downloads a ZIP file)
#GetMapping(value = "/zip", produces="application/octet-stream")
public byte[] start(HttpServletResponse response) {
final ContentDisposition contentDisposition = ContentDisposition.builder("attachment")
.filename("MyFile.zip").build();
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
return getZipDataAsByteArray();
}

Apache Camel 3.4 - Various Unmarshalling Errors Using Jackson

I have succesfully written a simple use-case to unmarshal a JSON response from the Quickbooks oAuth API to refresh tokens.
With that working, I need to go and fetch the actual data:
https://quickbooks.api.intuit.com/v3/company/XXXXXXXXXXXXXX/query?query=SELECT * FROM Invoice WHERE Metadata.LastUpdatedTime%3E='2020-07-01T01:00:00' ORDERBY Metadata.LastUpdatedTime, Id STARTPOSITION 1 MAXRESULTS 1000 &minorversion=47
The HTTP call works OK:
// make the HTTP REST call, without C10y* & Camel* headers:
.toD("https://${header." + Headers.IEP_API_HOST + "}?headerFilterStrategy=C10yHeaderFilterStrategy")
I can check that the JSON returned is OK:
.log(LoggingLevel.DEBUG, API_LOG, "JSON returned: ${body}")
But from here it goes pear-shaped:
.unmarshal().json(JsonLibrary.Jackson, Payload.class)
This is what happens:
With the above log statement a MismatchedInputException is raised with the message "No content to map due to end-of-input".
Without the above log statement a ClassNotFoundException is raised with the message "Payload.class".
Re. 1.
My understanding is that responses are cached by default so it should be possible to read the inputstream multiple times. By default the HTTP endpoint option disableStreamCache is set to false, so it's enabled.
The stack trace is:
com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
at [Source: (org.apache.camel.converter.stream.CachedOutputStream$WrappedInputStream); line: 1, column: 0]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4624) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4469) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3471) ~[jackson-databind-2.11.0.jar:2.11.0]
at org.apache.camel.component.jackson.JacksonDataFormat.unmarshal(JacksonDataFormat.java:188) ~[camel-jackson-3.4.0.jar:3.4.0]
at org.apache.camel.support.processor.UnmarshalProcessor.process(UnmarshalProcessor.java:64) ~[camel-support-3.4.0.jar:3.4.0]
at org.apache.camel.processor.errorhandler.RedeliveryErrorHandler$RedeliveryTask.doRun(RedeliveryErrorHandler.java:702) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.errorhandler.RedeliveryErrorHandler$RedeliveryTask.run(RedeliveryErrorHandler.java:616) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.impl.engine.DefaultReactiveExecutor$Worker.schedule(DefaultReactiveExecutor.java:148) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.impl.engine.DefaultReactiveExecutor.scheduleMain(DefaultReactiveExecutor.java:60) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.Pipeline.process(Pipeline.java:147) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:286) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.component.timer.TimerConsumer.sendTimerExchange(TimerConsumer.java:203) ~[camel-timer-3.4.0.jar:3.4.0]
at org.apache.camel.component.timer.TimerConsumer$1.run(TimerConsumer.java:76) ~[camel-timer-3.4.0.jar:3.4.0]
at java.base/java.util.TimerThread.mainLoop(Timer.java:556) ~[na:na]
at java.base/java.util.TimerThread.run(Timer.java:506) ~[na:na]
Re. 2.
The stack trace for this is:
org.apache.camel.CamelExecutionException: Exception occurred during execution on the exchange: Exchange[ID-WIN-10-DM-1594724136485-0-1]
at org.apache.camel.CamelExecutionException.wrapCamelExecutionException(CamelExecutionException.java:47) ~[camel-api-3.4.0.jar:3.4.0]
at org.apache.camel.language.simple.SimpleExpressionBuilder$26.evaluate(SimpleExpressionBuilder.java:590) ~[camel-core-languages-3.4.0.jar:3.4.0]
at org.apache.camel.support.ExpressionAdapter.evaluate(ExpressionAdapter.java:36) ~[camel-support-3.4.0.jar:3.4.0]
at org.apache.camel.reifier.language.SimpleExpressionReifier$1.evaluate(SimpleExpressionReifier.java:42) ~[camel-core-engine-3.4.0.jar:3.4.0]
at org.apache.camel.processor.SetHeaderProcessor.process(SetHeaderProcessor.java:48) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.errorhandler.RedeliveryErrorHandler$RedeliveryTask.doRun(RedeliveryErrorHandler.java:702) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.errorhandler.RedeliveryErrorHandler$RedeliveryTask.run(RedeliveryErrorHandler.java:616) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.impl.engine.DefaultReactiveExecutor$Worker.schedule(DefaultReactiveExecutor.java:148) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.impl.engine.DefaultReactiveExecutor.scheduleMain(DefaultReactiveExecutor.java:60) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.Pipeline.process(Pipeline.java:147) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:286) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.component.timer.TimerConsumer.sendTimerExchange(TimerConsumer.java:203) ~[camel-timer-3.4.0.jar:3.4.0]
at org.apache.camel.component.timer.TimerConsumer$1.run(TimerConsumer.java:76) ~[camel-timer-3.4.0.jar:3.4.0]
at java.base/java.util.TimerThread.mainLoop(Timer.java:556) ~[na:na]
at java.base/java.util.TimerThread.run(Timer.java:506) ~[na:na]
Caused by: java.lang.ClassNotFoundException: Payload.class
at org.apache.camel.impl.engine.DefaultClassResolver.resolveMandatoryClass(DefaultClassResolver.java:87) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.language.simple.SimpleExpressionBuilder$26.evaluate(SimpleExpressionBuilder.java:588) ~[camel-core-languages-3.4.0.jar:3.4.0]
... 13 common frames omitted
All of this is running within an Eclipse/Maven project, so I have cleaned, refreshed, compiled, rebuilt, etc, etc, to no avail.
I have written a simple jUnit and it works absolutely fine using the JSON from the above log, saved to a file:
/**
* POJO Jackson unmarshalling
*/
#Test
public void pojoUnmarshallTest() {
ObjectMapper om = new ObjectMapper();
try {
Payload payload = om.readValue(getFile("qb.Payload.Invoice.json"), Payload.class);
assertTrue(payload.toString().startsWith("c10y.model.qb.Payload"));
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
Lastly, I'm using the latest versions of Camel & Spring Boot:
<properties>
<!-- latest versions # Jul 2020 -->
<java.version>11</java.version>
<camel.version>3.4.0</camel.version> <!-- latest long term support version -->
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
<run.profiles>dev</run.profiles>
</properties>
As ever, thanks in advance for your help!
PS:
The POJO classes that receive the unmarshalled JSON were generated at http://www.jsonschema2pojo.org/. The POJO for the working route was hand-cranked. I mention it just in case it could make a difference (it shouldn't IMHO).
The entire route can be obtained here: https://drive.google.com/file/d/1Qu0vwJaSlggH6BrIgUFMU_BZNpmMQYuh/view?usp=sharing.
I have tried the Gson library and get the same ClassNotFoundException: Payload.class error. This is looking like less of an unmarshalling problem and more of a classpath issue.
Having two .log() statements gives the following output:
2020-07-15 10:06:27.108 DEBUG 6720 --- [mer://startHere] c.c.r.qb.fetch.delta : JSON returned: {"QueryResponse":{"Invoice":[{"AllowIPNPayment":false, ...removed... ,"Balance":0}],"startPosition":1,"maxResults":1,"totalCount":1},"time":"2020-07-15T02:06:21.869-07:00"}
2020-07-15 10:06:27.108 DEBUG 6720 --- [mer://startHere] c.c.r.qb.fetch.delta : JSON returned:
Contrary to the documentation, it looks like the input stream is not, in fact, cached.
PS2:
Adding .streamCaching() to the route and &disableStreamCache=false to the endpoint URI didn't make any difference to the second .log(); it remained empty.
I also tried the following Java Config approach:
#Configuration
public class Config {
#Bean
CamelContextConfiguration contextConfiguration() {
return new CamelContextConfiguration() {
#Override
public void beforeApplicationStart(CamelContext context) {
System.out.println("****** beforeApplicationStart ******");
}
#Override
public void afterApplicationStart(CamelContext context) {
System.out.println("****** afterApplicationStart ******");
context.setStreamCaching(true);
}
};
}
}
I can see the sysout in the console but this didn't work either.
I downloaded jsonschema2pojo-1.0.2 and ran it against a much bigger JSON sample with the following arguments:
--annotation-style JACKSON2
--big-decimals
--date-class java.util.Date
--format-dates
--format-date-times
--package c10y.model.qb.jxon
--remove-old-output
--source C:\Users\...\src\test\resources\qb.Payload.Invoice.json
--source-type JSON
--target c:\temp
--target-language JAVA
--target-version 11
This created the root/base POJO called QbPayloadInvoice, which looks like it's taken from the input file name. I updated my route:
.unmarshal().json(JsonLibrary.Jackson, QbPayloadInvoice.class)
It still raises the java.lang.ClassNotFoundException: Payload.class.
There's nothing in the JSON response, or in any of the other generated POJOs, called Payload.
At the same time, my updated jUnit works fine:
QbPayloadInvoice payload = om.readValue(getFile("qb.Payload.Invoice.json"), QbPayloadInvoice.class);
expected = "c10y.model.qb.jxon.QbPayloadInvoice";
assertEquals(expected, payload.toString().substring(0, expected.length()));
Go figure!
The clue exists above where I wrote "There's nothing in the JSON response, or in any of the other generated POJOs, called Payload." This statement turned out to be absolutely correct.
The unmarshalling had been working all along:
.unmarshal().json(JsonLibrary.Jackson, QbPayloadInvoice.class)
It was the following line that was failing:
.setHeader(QB_START_POS, simple("${bodyAs(Payload.class).QueryResponse.startPosition}"))
This was made more obvious given that I'd changed the unmarshalling class from Payload to QbPayloadInvoice.

Swagger definition errors

Trying to use Swagger on my JAX-RS application with enunciate Maven plugin.
The generated JSON definition works well.
But when I try to validate it online with Swagger Editor, the converted YAML file has some weird errors.
For example:
Definition:
parameters:
- name: body
in: body
type: file
description: 'Bla bla'
responses:
'201':
ERROR:
Parameters with "type: file" must have "in: formData"
JAX-RS Code:
#POST
#Path(value = "/validatedata")
public Response validate(ValidateDataFromInput validateDataFromInput) throws CustomException {
return Response.status(Status.ACCEPTED).entity(validationActionService.getDataFromInput(validateDataFromInput.getConsumerInput(),
validateDataFromInput.getValidateInput(), null)).build();
}
I am using: swagger-core-1.5.21 and enunciate-maven-plugin-2.11.1.
Not sure which is wrong here.

RESTEASY003145: Unable to find a MessageBodyReader of content-type text/xml;charset=UTF-8

I'm a newbie in a REST services, but all solutions I found was due to lack of necessary providers.
Dependencies:
compile group: 'org.jboss.resteasy', name: 'resteasy-client', version: '3.6.1.Final'
compile group: 'org.jboss.resteasy', name: 'resteasy-jackson2-provider', version: '3.6.1.Final'
compile "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.+"
compile "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.+"
Data class:
#JacksonXmlRootElement(localName = "Response")
data class ResponseSessionInit(#JacksonXmlProperty(localName = "Error") val error: Error)
data class Error(#JacksonXmlProperty(localName = "Text", isAttribute = true) val text: String)
Proxy:
interface L3Client {
#POST
#Consumes("text/xml;charset=UTF-8")
#Produces("text/xml; charset=UTF-8")
fun test(string: String): ResponseSessionInit
}
Proxy method invocation:
val proxy = client.target(API).proxyBuilder(L3Client::class.java).build()
println(proxy.test("<Request><ProtocolInfoGet/></Request>"))
I receive exception:
javax.ws.rs.client.ResponseProcessingException: javax.ws.rs.ProcessingException: RESTEASY003145: Unable to find a MessageBodyReader of content-type text/xml;charset=UTF-8 and type class ru.abinet.commons.cft.L3.messages.ResponseSessionInit
at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.extractResult(ClientInvocation.java:156)
at org.jboss.resteasy.client.jaxrs.internal.proxy.extractors.BodyEntityExtractor.extractEntity(BodyEntityExtractor.java:60)
at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:150)
at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:112)
at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:76)
at com.sun.proxy.$Proxy19.test(Unknown Source)
It really looks like if Resteasy client can't find a decoder for my XML, however I have it in path.
I can change test() method to return String and successfully parse XML with something like
val mapper = XmlMapper()
mapper.registerModule(KotlinModule())
val value = mapper.readValue(xml, ResponseSessionInit::class.java)
However, I'd like to do it automagically. What I'm missing ?
Reasteasy can't handle application/xml content-type out of the box with jackson-xml. So the idea is to write it by yourself.
#Provider
#Consumes(MediaType.TEXT_XML, MediaType.APPLICATION_XML)
class BodyReaderProvider : MessageBodyReader<Any> {
//jackson instantiation and some check code here
}
and register it
val client = ResteasyClientBuilder.newBuilder()
.register(BodyReaderProvider())