Apache CXF doesn't select by http method when #DefaultMethod annotation is used - jax-rs

I have two POST methods and one DELETE in my resource. They have same path.
I annotated one of POSTs with #DefaultMethod, so when someone doesn't send correct Accept header, correct method will be selected. But this causes that when DELETE is called, cxf selects POST instead of correct delete method. Is there any workaround for this?
CXF version: 3.1.17
#DefaultMethod
#POST
#Consumes(MeasurementMediaType.MEASUREMENT_TYPE)
#Produces(MeasurementMediaType.MEASUREMENT_TYPE)
public Response post(MeasurementRepresentation measurementRepresentation, #HeaderParam(value = HttpHeaders.ACCEPT) String acceptHeader) URISyntaxException {
...
}
#POST
#Consumes(MEASUREMENT_COLLECTION_TYPE)
#Produces(MEASUREMENT_COLLECTION_TYPE)
public Response post(MeasurementCollectionRepresentation measurementCollectionRepresentation, #HeaderParam(value = HttpHeaders.ACCEPT) String acceptHeader) {
...
}
#DELETE
public Response delete(
#QueryParam("fragmentType") String fragmentType,
#QueryParam("source") String source,
#QueryParam("dateFrom") DateTime dateFrom,
#QueryParam("dateTo") DateTime dateTo,
#QueryParam("type") String type) {
...
}
java.lang.NullPointerException
at com.cumulocity.measurement.rest.resources.MeasurementCollectionResource.post(MeasurementCollectionResource.java:280)

Two things:
1) DefaultMethod appears not to refer to the default method to select, but the default HTTPMethod. So it's essentially overriding the httpmethod of your call. This is a CXF extension to JAX-RS, so you may be able to ask the CXF team to update the functionality or create a new annotation for your use case.
2) If I understand you correctly, you would like the first method to be called if someone sends the body of {"Hello" : "World"}? Wouldn't you then get errors when trying to construct your MeasurementRepresentation? If they send a bad request, why not let CXF respond with an appropriate HTTP error code?

Related

Locale aware bean validation message interpolation at ExceptionMapper in openliberty

I have a JAX-RS #POST endpoint whose input data has to be #Valid:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response myEndpoint(#javax.validation.Valid MyInputData input) { /*...*/ }
With MyInputData class annotated with many constraints:
#lombok.Data
public class InputData {
#Size(min = 1, max = 3)
private String someString;
/* ... */
}
Beyond that I have an ExceptionMapper<ConstraintViolationException> that transform the Exception into a Collection<String> (basically every single ConstraintViolation transformed to String using its getMesssage() method), then returns a Response.status(Status.BAD_REQUEST).entity(list).build().
Everything is working nicely. Fed an invalid input and I get back a HTTP 400 with a nice array of constraint violations in json format.
So far, so good...
BUT... the messages are in server's locale. Even if HTTP post sends a Accept-language header (and it is correctly detected when getting HttpServletRequest::getLocale).
By the time the ExceptionMapper gets hold of ConstraintViolation every message has already been interpolated, so no chance set the client locale.
Since the validation runs even before the JAX-RS resource (indeed, the JAX-RS resource isn't even called in case of invalid input), this locale aware message interpolator must be configured somewhere else.
Where? Is there already a MessageInterpolator implementation whose operation takes the HttpServletRequest locale into account?

How to make a MultiMock Http Callout Test for Salesforce?

If I have an Apex function that is named authorize() that just gets a username, password, and session token, and another function called getURL('id#', 'key'), that takes an id# for the record as a string and a key for the image to return as a string as parameters. getURL calls the authorize function inside it in order to get the credentials for its callout. The authorize is a post request, and the getURL is a get request.
I am trying to figure out how to test both of these callouts just so I can make sure that getURL is returning the proper JSON as a response. It doesn't even have to be the URL yet which is its intention eventually. But I just need to test it to make sure these callouts are working and that I am getting a response back for the 75% code coverage that it needs.
I made a multiRequestMock class that looks like this:
public class MultiRequestMock implements HttpCalloutMock {
Map<String, HttpCalloutMock> requests;
public MultiRequestMock(Map<String, HttpCalloutMock> requests) {
this.requests = requests;
}
public HTTPResponse respond(HTTPRequest req) {
HttpCalloutMock mock = requests.get(req.getEndpoint());
if (mock != null) {
return mock.respond(req);
} else {
throw new MyCustomException('HTTP callout not supported for test methods');
}
}
public void addRequestMock(String url, HttpCalloutMock mock) {
requests.put(url, mock);
}
}
I then began to write a calloutTest.cls file but wasn't sure how to use this mock class in order to test my original functions. Any clarity or assistance on this would be helpful Thank you.
I believe in your calloutTest class you use Test.setMock(HttpCalloutMock.class, new MultiRequestMock(mapOfRequests)); then call the getUrl and/or authorize methods and instead of the request really executing the response returned will be that which is specified in the response(HttpRequest) method you have implemented in the MultiRequestMock class. That is basically how I see it working, for more info and an example you can see this resource on testing callout classes. This will get you the code coverage you need but unfortunately cannot check you are getting the correct JSON response. For this, you may be able to use the dev console and Execute Anonymous?
You may want to look at simplifying your HttpCalloutMock Implementation and think about removing the map from the constructor as this class really only needs to return a simple response then your calloutTest class can be where you make sure the returned response is correct.
Hope this helps

Retrieve WS request in CXF Web service

I got a CXF OSGi Web service (based on the example demo in servicemix: https://github.com/apache/servicemix/tree/master/examples/cxf/cxf-jaxws-blueprint)
The Web service works fine and i call all the available implemented methods of the service.
My question is how can i retrieve the request inside a WS method and parse in a string XML format.
I have found that this is possible inside interceptors for logging, but i want also to the WS-Request inside my methods.
For storing the request in the database I suggest to extend the new CXF message logging.
You can implement a custom LogEventSender that writes into the database.
I had similar requirement where I need to save data into DB once method is invoked. I had used ThreadLocal with LoggingInInterceptor and LoggingOutInterceptor. For example in LoggingInInterceptor I used to set the message into ThreadContext and in webservice method get the message using LoggingContext.getMessage() and in LoggingOutInterceptor I used to removed the message(NOTE: Need to be careful here you need to explictly remove the message from thread context else you will end up with memory leak, and also incase of client side code interceptors get reversed.
public class LoggingContext {
private static ThreadLocal<String> message;
public static Optional<String> getMessage() {
return Optional.ofNullable(message.get());
}
public static void setMessage(final String message) {
LoggingContext.message = new ThreadLocal<>();
LoggingContext.message.set(message);
}
}
Not an answer to this question but i achieved to do my task by using JAXB in the end and do some manipulations there.

Method parameter annotated with #FormParam is always null in JAX-RS

I am trying to send multiple form parameters to my REST servive using POST. But the parameters sent by the client are always received as null.
#POST
#Path("/login")
#Produces({ "application/json" })
public LoginData userLogin(#FormParam("picture") String picture,
#FormParam("name") String name,
#FormParam("email") String email) {
...
}
When I remove all the parameters like the code below, it works properly:
#POST
#Path("/login")
#Produces({ "application/json" })
public LoginData userLogin() {
...
}
I've checked and the values sent by the client are not null.
Is there a different way to receive the parameters?
Annotate your method with #Consumes(MediaType.APPLICATION_FORM_URLENCODED):
#POST
#Path("/login")
#Produces(MediaType.JSON)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public LoginData userLogin(#FormParam("picture") String picture,
#FormParam("name") String name,
#FormParam("email") String email) {
...
}
And ensure the Content-Type of the request is application/x-www-form-urlencoded.
This might not be useful for the actual question. Might be useful for some people who has similar issue along with consumes annotation.
I had the similar issue even if I have the annotation it was null.
The reason was like my project was using both org.codehaus.jackson library and com.fasterxml.jackson libraries. So, mapping had issues.
Once I updated the parent and related child projects to fasterxml, the formparam annotation was working fine.
So search your project and update all the references in pom.xml and java imports from org.codehaus to com.fasterxml. FormParam and HeaderParam null issue will be resolved.

Uploading a file in Jersey without using multipart

I run a web service where I convert a file from one file format into another. The conversion logic is already functioning but now, I want to query this logic via Jersey. Whenever file upload via Jersey is addressed in tutorials / questions, people describe how to do this using multipart form data. I do however simply want to send and return a single file and skip the overhead of sending multiple parts. (The webservice is triggered by another machine which I control so there is no HTML form involved.)
My question is how would I achieve something like the following:
#POST
#Path("{sessionId"}
#Consumes("image/png")
#Produces("application/pdf")
public Response put(#PathParam("sessionId") String sessionId,
#WhatToPutHere InputStream uploadedFileStream) {
return BusinessLogic.convert(uploadedFile); // returns StreamingOutput - works!
}
How do I get hold of the uploadedFileStream (It should be some annotation, I guess which is of course not #WhatToPutHere). I figured out how to directly return a file via StreamingOutput.
Thanks for any help!
You do not have to put anything in the second param of the function; just leave it un-annoted.
The only thing you have to be carefull is to "name" the resource:
The resource should have an URI like: someSite/someRESTEndPoint/myResourceId so the function should be:
#POST
#Path("{myResourceId}")
#Consumes("image/png")
#Produces("application/pdf")
public Response put(#PathParam("myResourceId") String myResourceId,
InputStream uploadedFileStream) {
return BusinessLogic.convert(uploadedFileStream);
}
If you want to use some kind of SessionID, I'd prefer to use a Header Param... something like:
#POST
#Path("{myResourceId}")
#Consumes("image/png")
#Produces("application/pdf")
public Response put(#HeaderParam("sessionId") String sessionId,
#PathParam("myResourceId") String myResourceId,
InputStream uploadedFileStream) {
return BusinessLogic.convert(uploadedFileStream);
}