JAXRS: No message body reader in a simple example? - jax-rs

I do not understand why this happens: when I declare explicitly the filename of the input in my annotated java class named "process", everything works perfect:
#GET
public static void process() throws IOException {
File file = new File("a.pdf");
FileUtils.writeStringToFile(new File("a.pdf" + ".exported"), menu.parseToString(file));
}
However, when I try to pass the file name as an argument, and configure also eclipse through run configurations to give the appropriate argument (path to the "a.pdf"):
#GET
public static void process(String[] args) throws IOException {
File file = new File(args[0]);
FileUtils.writeStringToFile(new File(args[0] + ".exported"), menu.parseToString(file));
}
when I call the service it fails with an error:
Oct 09, 2014 9:44:55 AM org.apache.cxf.jaxrs.utils.JAXRSUtils readFromMessageBody
WARNING: No message body reader has been found for request class String[], ContentType :
application/octet-stream.
I am new to jax rs. Do I miss any annotation? Thank you very much...

Invalid Java syntax
This
public static void process("a.pdf") throws IOException {
// ...
}
is not valid Java syntax. Every IDE and javacwill complain about it. Eclipse says:
Syntax error on token ""a.pdf"", delete this token
application/octet-stream as body
It looks like you try to make a GET request with Content-Type: application/octet-stream (the file you somehow 'clicked'). This has two problems:
A GET request normally as no body, it just has headers.
A body of bytes encoded as application/octet-stream can not be mapped to a String[] because JAX-RS has no way to know how to intepret the bytes.
What you probably want
It looks like you want to make a GET request with a file name as a request parameter (that is not the sames as a file in the body of the request). You could do this:
GET http://example.com/service?filename=foo.pdf
Then a JAX-RS like the following could be used to service this request:
#GET
public Response service(#QueryParam("filename") String filename) {
// use filename to open a File and do something with it
}
Note the use of #QueryParam which allows service to extract filename=foo.pdf from the request URL.

Related

Could Http server return error code before POST body is sent?

If a Http server is not reachable, and Http client is trying to send a POST request to the server, will the Post content ever have the opportunity to be sent or no (say error code come back and the code will not be executed)?
More specifically, I have special logic in writeTo function, I would like to know if it is executed or not if Http server is not reachable.
public interface HttpEntity {
boolean isRepeatable();
boolean isChunked();
long getContentLength();
Header getContentType();
Header getContentEncoding();
InputStream getContent() throws IOException, IllegalStateException;
void writeTo(OutputStream var1) throws IOException;
boolean isStreaming();
void consumeContent() throws IOException;
}

How to solve file upload error in Postman?

I use file upload with webapi in my project. I am testing with Postman. However, Request.Content.IsMimeMultipartContent() always returns false.
Postman screenshot:
FileUploadController Code:
public async Task<HttpResponseMessage> UserImageUpload()
{
try
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var userImageUploadPath = HttpContext.Current.Server.MapPath(CommonParameters.UserProfileImageServerPath);
var streamProvider = new CustomMultipartFormDataStreamProvider(userImageUploadPath);
await Request.Content.ReadAsMultipartAsync(streamProvider);
var files = new List<string>();
foreach (MultipartFileData file in streamProvider.FileData)
{
files.Add(Path.GetFileName(file.LocalFileName));
}
return Request.CreateResponse(HttpStatusCode.OK, files);
}
catch (Exception exception)
{
logger.ErrorFormat("An error occured in UserImageUpload() Method - Class:FileUploadController - Message:{0}", exception);
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
This is Postman bug. Try removing the Content-Type header. When sending the actual Post, the browser will automatically add the proper header and create the boundary.
There is no need to mention Content-Type in headers in Postman, I tried sending attachments without Content-Type it works fine for me.
When i used Content-Type: multipart/formdata it throws an error saying "Could not get any response". Postman sends your file attachments also with Content-Type →text/plain; charset=utf-8.
There are few things to consider:
1) Don't send any Content Type Header if you are using Postman
2) Specify the Key for your choosen file in Body (PFB Screenshot for your reference)
You need to uncheck Content-Type if you have it checked, and let Postman add it for you, like in this picture:
Might by a bit late. I encountered the same error in ARC and resolved by providing a name for the file field (after the blue check mark on your second screenshot)

Content-Type case sensitivity in RESTEasy 3

I am developing a RestEasy client to connect to a 3rd party REST service which has defined its own custom media types. A made up example is
application/vnd.abc.thirdPartyThing-v1+json
Note the uppercase P in thirdParty.
I am using RESTEasy 3.0.11 for my client implementation. At the point where I make a POST call to the service my code looks like
Response response = target.request()
.post(Entity.<ThirdPartyThing>entity(
thing,
"application/vnd.abc.thirdPartyThing-v1+json"));
but RESTEasy sends to the server
Content-Type: application/vnd.abc.thirdpartything-v1+json
This is due to RESTEasy's MediaTypeHeaderDelegate class's toString() method, which lowercases the type and subtype MediaTypeHeaderDelegate. This should be correct, or at least unimportant, as RFC-1341 states that Content-Type values are case-insensitive - RFC-1341
Unfortunately the 3rd party service is checking the Content-Type in a case sensitive manner and so returning a 415 UNSUPPORTED MEDIA TYPE error. I've tested using curl which doesn't alter the content-type value and confirmed that it's a case issue. application/vnd.abc.thirdPartyThing-v1+json works, application/vnd.abc.thirdpartything-v1+json does not.
I'm in the process of raising a ticket, but in the meantime is there any way to override RESTEasy's default behaviour and send Content-Type headers without lowercasing the value?
Thanks for reading.
I could reproduce this behavior with RESTeasy 3.0.6.Final and would not expect it. Maybe you could check their JIRA if this has already been discussed or open an issue. I once had problems on the server side because a 2.x version of RESTeasy was checking the charset attribute of the Content-Type header case-sensitive. This was also changed.
You could solve this problem by a really ugly workaround: Overwrite the header again in a ClientRequestFilter.
public class ContentTypeFilter implements ClientRequestFilter {
private Map<String, String> contentTypes;
public ContentTypeFilter() {
contentTypes = new HashMap<>();
contentTypes.put("text/foo", "text/Foo");
}
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
String contentType = requestContext.getHeaderString("Content-Type");
if (contentTypes.containsKey(contentType)) {
requestContext.getHeaders().putSingle("Content-Type", contentTypes.get(contentType));
}
}
}
Don't forget to register this Filter:
Client client = ClientBuilder.newClient().register(ContentTypeFilter.class);

Camel aws-s3 not working

I am trying to create a camel route to transfer a file from an FTP server to an AWS S3 storage.
I have written the following route
private static class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception
{
from("sftp://<<ftp_server_name>>&noop=true&include=<<file_name>>...")
.process(new Processor(){
#Override
public void process(Exchange ex)
{
System.out.println("Hello");
}
})
.to("aws-s3://my-dev-bucket ?
accessKey=ABC***********&secretKey=12abc********+**********");
}
The issue is, this gives me the following exception:
Exception in thread "main" org.apache.camel.FailedToCreateRouteException: Failed to create route route1 at: >>> To[aws-s3://my-dev-bucket?accessKey=ABC*******************&secretKey=123abc******************** <<< in route: Route(route1)[[From[sftp://<<ftp-server>>... because of Failed to resolve endpoint: aws-s3://my-dev-bucket?accessKey=ABC***************&secretKey=123abc************** due to: The request signature we calculated does not match the signature you provided. Check your key and signing method.
I then tried to do this the other way. i.e.writing a method like this:
public void boot() throws Exception {
// create a Main instance
main = new Main();
// enable hangup support so you can press ctrl + c to terminate the JVM
main.enableHangupSupport();
// bind MyBean into the registery
main.bind("foo", new MyBean());
// add routes
AWSCredentials awsCredentials = new BasicAWSCredentials("ABC*****************", "123abc*************************");
AmazonS3 client = new AmazonS3Client(awsCredentials);
//main.bind("client", client);
main.addRouteBuilder(new MyRouteBuilder());
main.run();
}
and invoking using the bound variable #client. This approach does not give any exceptions, but the file transfer does not work.
To make sure that there's nothing wrong with my approach, I tried aws-sqs instead of aws-s3 and that works fine (file succesfully transfers to the SQS queue)
Any idea why this is happening? Is there some basic issue with "aws-s3" connector for camel?
Have you tried of using RAW() function to wrap as like RAW(secretkey or accesskey).
It will help you to pass your keys as it is without encoding.
Any plus signs in you secret key need to be url encoded as %2B, in your case **********+*********** becomes **********%2B***********
When you configure Camel endpoints using URIs then the parameter values gets url encoded by default.
This can be a problem when you want to configure passwords as is.
To do that you can tell Camel to use the raw value, by enclosing the value with RAW(value). See more details at How do I configure endpoints which has an example also.
See Camel Documentation
Your url should looks like:
aws-s3:bucketName?accessKey=RAW(XXXX)&secretKey=RAW(XXXX)

Apache Http Client Put Request Error

I'm trying to upload a file using the Apache Http Client's PUT method. The code is as below;
def putFile(resource: String, file: File): (Int, String) = {
val httpClient = new DefaultHttpClient(connManager)
httpClient.getCredentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(un, pw))
val url = address + "/" + resource
val put = new HttpPut(url)
put.setEntity(new FileEntity(file, "application/xml"))
executeHttp(httpClient, put) match {
case Success(answer) => (answer.getStatusLine.getStatusCode, "Successfully uploaded file")
case Failure(e) => {
e.printStackTrace()
(-1, e.getMessage)
}
}
}
When I tried running the method, I get to see the following error:
org.apache.http.NoHttpResponseException: The target server failed to respond
at org.apache.http.impl.conn.DefaultResponseParser.parseHead(DefaultResponseParser.java:101)
at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:252)
at org.apache.http.impl.AbstractHttpClientConnection.receiveResponseHeader(AbstractHttpClientConnection.java:281)
at org.apache.http.impl.conn.DefaultClientConnection.receiveResponseHeader(DefaultClientConnection.java:247)
at org.apache.http.impl.conn.AbstractClientConnAdapter.receiveResponseHeader(AbstractClientConnAdapter.java:219)
at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:298)
at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
at org.apache.http.impl.client.DefaultRequestDirector.tryExecute(DefaultRequestDirector.java:633)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:454)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820)
I do not know what has gone wrong? I'm able to do GET requests, but PUT seems not to work! Any clues as to where I should look for?
Look on the server. If GET Works, but PUT does not, then you have to figure out the receiving end.
Also, you may want to write a simple HTML File that has a form with PUT Method in it to rule out your Java Part.
As a sidenode: Its technically possible that something in between stops the request from going through or the response reaching you. Best setup a dummy HTTP Server to do the testing against.
Maybe its also a timeout issue, so the server takes to long to process your PUT.
The connection you are trying to use is a stale connection and therefore the request is failing.
But why are you only seeing an error for the PUT request and you are not seeing it for the GET request?
If you check the DefaultHttpRequestRetryHandler class you will see that by default HttpClient attempts to automatically recover from I/O exceptions. The default auto-recovery mechanism is limited to just a few exceptions that are known to be safe.
HttpClient will make no attempt to recover from any logical or HTTP protocol errors (those derived from HttpException class).
HttpClient will automatically retry those methods that are assumed to be idempotent. Your GET request, but not your PUT request!!
HttpClient will automatically retry those methods that fail with a transport exception while the HTTP request is still being transmitted to the target server (i.e. the request has not been fully transmitted to the server).
This is why you don't notice any error with your GET request, because the retry mechanism handles it.
You should define a CustomHttpRequestRetryHandler extending the DefaultHttpRequestRetryHandler. Something like this:
public class CustomHttpRequestRetryHandler extends DefaultHttpRequestRetryHandler {
#Override
public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
if(exception instanceof NoHttpResponseException) {
return true;
}
return super.retryRequest(exception, executionCount, context);
}
}
Then just assign your CustomHttpRequestRetryHandler
final HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.setRetryHandler(new CustomHttpRequestRetryHandler());
And that's it, now your PUT request is handled by your new RetryHandler (like the GET was by the default one)