Resteasy: How does json extension get extracted from /customers.json - resteasy

I don't understand how the below class is able to recognize the request /customers.json as the /customers path and able to extract and extract the json extension. There is no path parameters.
"Consider this
JAX-RS resource class:
#Path("/customers")
public class CustomerResource
{
#GET
#Produces("application/xml")
public Customer getXml() {...}
#GET
#Produces("application/json")
public Customer getJson() {...}
}
For this CustomerService JAX-RS resource class, if a request of GET /custom
ers.json came in, the JAX-RS implementation would extract the .json suffix and remove
it from the request path. It would then look in its media type mappings for a media
type that matched json. In this case, let’s say json mapped to application/json. It
would use this information instead of the Accept header and dispatch this request to
the getJson() method."

I got the official answer from author: "The specification does not define a facility for this, but most JAX-RS implementations support it."

Related

How do I set CSV header in camel DSL?

I have one CSV file without header which is containing two value like
"lenovo","30000"
I want to set header in first row using camel DSL and pass it to another route, header is like:
"laptop","price"
My DSL route:
from("file:...?fileName=file1.csv&noop=true")
//Something I want to include string like this
.addLineInBody("laptop"+"price").append("\n")
.to("file:../?fileName=output.csv");
How can I do this in camel DSL?
I do not fully understand your header-problem. If you want to just prepend the CSV file with a static header a save it again as CSV you could simply use a Java bean to prepend the message body.
If you want to convert CSV to a data structure or vice versa you should check out the Camel CSV documentation: http://camel.apache.org/csv.html
Update:
You can write a java bean like this. The important part is the #Body Camel annotation to inject the message body into the method. See http://camel.apache.org/parameter-binding-annotations.html for more information about this.
Do the body manipulation as you like.
public class CsvHeaderEnricher {
public String enrichCsvHeader(#Body String messageBody) {
String enrichedBody = "YourHeader" + messageBody;
return enrichedBody;
}
}
Then you can call the bean in your route like this:
from("file:...?fileName=file1.csv&noop=true")
.bean(new CsvHeaderEnricher())
.to("file:../?fileName=output.csv");
You can also register the bean in the Spring context, autowire it into the Route class and use an instance variable instead of new
As long as your bean contains only one method, you don't need to tell Camel what method to use

Cloud Endpoints not accepting JSON array

I want to build my endpoint, which accept JSON array of below format:
[
{
"test":"Math",
"result":"Pass"
},
{
"test":"Science",
"result":"FirstClass"
}
]
It will be a POST call with the above JSON to my endpoint.
I tried it with servlet too but did not get the required result, and also tried to with list and inserting in a new class and posting to that class. Thanks in advance.
Is that an accurate representation of the JSON object which is being sent over? Because one does not simply send a a POST request with a JSON object of their param object to a cloud endpoint. See here for a thorough guide to Endpoint API interaction from a javascript perspective - notice how the client library exposes an object "gapi" through which calls are made. If you're sending this JSON from an iOS or Android app, there are similar client libraries which can be generated for you by a cloud endpoints build tool.
After much frustration, I resorted to reading the docs more carefully. In that quest, I found an important note in the doc:
https://cloud.google.com/endpoints/docs/frameworks/java/parameter-and-return-types
"Any type except a parameter or injected type is considered an entity type. ... Entity types cannot be annotated with #Named"
With all examples showing named parameters, I was stumped as the docs don't explain further, but then found a solution. It ends up that if you do not have named parameters, everything is just passed in as a LinkedHashMap. Usually, you can do any work you need to with just that data structure, but if you HAVE to have it in JSON, you can convert it. Here are some examples:
#ApiMethod(name = "endpointIterfaceName.createItems", httpMethod = "post", path = "test/items")
public WhateverReturnType createItems(LinkedHashMap<String, Object> itemsMap) {
// Do Stuff with map values
return whateverReturnValue;
}
With this, you need to be sure that you post your data with the Content-Type of json (i.e. Content-Type:application/json; charset=UTF-8). So, for example, when testing, with a jquery ajax call you would need to set dataType to "json" or with Postman, you would select "Raw" then JSON (application/json).
If you really want to convert this to a JSON object in Java because for whatever reason you can not use a hash map, you can do the following in your method:
// Use gson library to convert the map to a string
Gson gson = new Gson();
String mapAsJsonString = gson.toJson(itemsMap);
// create a JSON object from the new string representation
JSONObject obj = new JSONObject(mapAsJsonString);
As a side note, if this is passed as Content-Type:text then the whole body will be in the map as the first key of the map. You could do some inadvisable things here and just get that key and avoid converting the map to a string and then to a json object, but, like I said, that is inadvisable. :)

What ObjectMapper config / annotations required for successful JSON / JSON array exhange using Jackson 2.2

Using Jackson 2.2.2 and Apache CXF web services client and server API.
I'm finding it impossible to serialize / deserialize JSON without failure.
Java class:
MyPojo
{
..... various properties
}
JSON produced from Jackson:
{
"MyPojo":
{
..... various properties
}
}
When I send the exact same JSON back to Jackson for it to consume, it fails with:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "MyPojo" (class app.model.MyPojo), not marked as ignorable (17 known properties: ,.....
Ideally, Jackson would not wrap the MyPojo object with {"MyPojo":} because I only ever exchange MyPojo objects, so it is implied.
To that end, how can I get Jackson to produce:
{
..... various properties
}
Then, how do I get jackson to consume the same JSON without failing? i.e. what ObjectMapper configuration or annotations or combination of both do I have to use?
If this is impossible, then how do I configure / annotate to get Jackson to consume the "wrapped" JSON without failing?
ALSO,
I have the same issues when producing / consuming an array of MyPojo objects:
JSON produced from Jackson:
{
"MyPojo":
[
{
..... various properties
},
{
..... various properties
}
]
}
..when consumed fails with:
com.fasterxml.jackson.databind.JsonMappingException:
Can not deserialize instance of app.model.MyPojo[] out of START_OBJECT token
Again, ideally (but not essential) Jackson would produce / consume:
[
{
..... various properties
},
{
..... various properties
}
]
Note, the Apache CXF WS appears to perform some magic via its #GET, #POST, etc, annotations as when used in conjuntion with a RESTful WS resource method which returns a MyPojo object, i.e. it appears that after my method returns the object, it is transformed into JSON.
To that end, I am unsure if a local or even global ObjectMapper will influence the output, so this should also be considered when answering.
Another note, I also need the same POJO to be produced and consumed in XML via JAXB.
EDIT:
I am now quite certain that TomEE/CXF is not using Jackson and that this is the cause of my issues. I'll update when I get it to work.
RESOLVED:
Further investigation revealed that whilst the JSON was being deserailized by Jackson, the default Jettison provider was not being overriden with Jackson when serializing due to misconfiguration of default JSON provider in CXF/TomEE. This resulted in a Jackson - Jettison formatting mismatch.
On stackOverflow are more than many answered questions like yours.
This is a solution:
objectMapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
You can see similar questions:
Use class name as root key for JSON Jackson serialization
Enable Jackson to not output the class name when serializing (using Spring MVC)

Jax-Rs service is not getting invoked

I am developing JAX-RS Rest service using Apache CXF. After deploying it to Tomcat 7 server, if I type the URL http://localhost:8080/Rest/rest?_wadl it shows me the WADL. but if I enter the URL http://localhost:8080/Rest/rest/retrieve it gives me 404 error.
In above URLs: Rest is the name of my project
/rest is the url-pattern for my CXFServlet which is specified in web.xml
/ is the address of jaxrs:server which is specified in beans.xml
retrieve is the path of service which is specified in my interface with #Path annotation.
(My apologies: I can't provide the XML documents referred to above.)
I think this is a CXF bug which get the incorrect base URL for restful web services.
The class "org.apache.cxf.transport.servlet.ServletController" invokes the method "getBaseURL" of the class "org.apache.cxf.transport.servlet.BaseUrlHelper".
It gets the base URL from request URL, and it ignores the parameters part.
This is correct for SOAP web servcies, because SOAP web services URL is just like: http://host:port/basepath?para=a. Unfortunately, for restful web services, the URL is just like http://host:port/basepath/method/parameter. The correct base URL should be http://host:port/basepath, but actually, the BaseUrlHelper gives you http://host:port/basepath/method/parameter. It just gives the URL before "?". It's why the result is correct when you access http://localhost:8080/Rest/rest?_wadl, in this case, it gives the correct base URL http://localhost:8080/Rest.
If you access http://localhost:8080/Rest/rest?_wadl at first then you access http://localhost:8080/Rest/rest/retrieve, it would be correct. Because, CXF set the base URL as the address of EndpointInfo only at the first time. It means, you MUST access the correct base URL at the first time! :(
The solution is: override the method "getBaseURL(HttpServletRequest request)" of "org.apache.cxf.transport.servlet.ServletController", let it return correct base URL.
For example, step1: extends the ServletController.
public class RestfulServletController extends ServletController {
private final String basePath;
public RestfulServletController(DestinationRegistry destinationRegistry, ServletConfig config,
HttpServlet serviceListGenerator, String basePath) {
super(destinationRegistry, config, serviceListGenerator);
this.basePath = basePath;
}
#Override
protected String getBaseURL(HttpServletRequest request) {
// Fixed the bug of BaseUrlHelper.getBaseURL(request) for restful service.
String reqPrefix = request.getRequestURL().toString();
int idx = reqPrefix.indexOf(basePath);
return reqPrefix.substring(0, idx + basePath.length());
}
}
step2: extends CXFNonSpringServlet and use the RestfulServletController in the subclass
public class RestfulCXFServlet extends CXFNonSpringServlet {
... ...
private ServletController createServletController(ServletConfig servletConfig) {
HttpServlet serviceListGeneratorServlet = new ServiceListGeneratorServlet(destinationRegistry, bus);
ServletController newController = new RestfulServletController(destinationRegistry, servletConfig,
serviceListGeneratorServlet, basePath);
return newController;
}
}
step3: instead of CXFNonSpringServlet , you use the derived class RestfulServletController.
Don't forget, you should config the "basePath" as /Rest/rest.
Hope this can help you.

Modifying Headers Depending on Annotation

I wanted to modify the outgoing headers on a client interface depending on the annotation on the request method. For example:
#Path("/myservices")
public interface MyService {
#GET
#Path("/get/something")
#Produces("application/json")
#MyAnnotation("value")
String getsomething(
#QueryParam("first_param") String firstParam,
#QueryParam("second_param") int secondParam);
The getsomething method has a annotation MyAnnotation, which an interceptor [out interceptor perhaps] can check if it exists and modify the headers accordingly. I am able to modify the headers in the out interceptor but how do I check for the annotation? The underlying class is no more and I suppose I am way beyond interface and dealing with raw request/response.
Is there any other way of doing this? I could place the annotation at the class level and before JAXRSClientFactoryBean.create. I could check the class annotation and stuff the headers into the client. However, I would prefer it at method level. Any suggestions? Note this is for client interface.

Resources