JSON deserialize only with at least 2 parameters - java

I'm implementing a RESTful service application for TomEE Plus 1.7.1 with Jettison as default json provider. I have several facade classes for my entitiy classes to provide CRUD functionalities for each of them. Service facades have been generated by netbeans.
This is the POST method:
#POST
public void create(Course entity) {
super.create(entity);
}
While using this method (to create a new instance in the database) I got following error:
No message body reader has been found for request class Object, ContentType : application/json.
After several hours of trying, I got it to work: I only had to add another parameter to the method, like that:
#POST
public void create(#Context Context uriInfo, Course entity) {
super.create(entity);
}
I don't understand why I had to add this Context parameter. I don't need the context variable, so actually I would like to remove it...
Does anybody know the reason?

Okay, I think I found the solution:
All my rest services have been implemented as facade classes. The abstract facade (super class of all services) has several methods like:
public void create(T entity) { getEntityManager().persist(entity); }
public void edit(T entity) {getEntityManager().merge(entity);}
These methods are used by the facade classes:
public void create(Course entity) {
super.create(entity);
}
public void edit(#PathParam("id") Integer id, Course entity) {
super.edit(entity);
}
(for better viewing I've removed the annotations here)
The difference between these two methods is, that the edit method has a second parameter "id" and so does not override the edit() method of the super class. But the create() method does only have a single parameter which causes override of the super class method "create()". I don't know why, but cxf is now creating two endpoints:
POST http://localhost:8080/webprog/api/course/ -> void create(Course)
POST http://localhost:8080/webprog/api/course/ -> void create(Object)
This is also the reason why I got it working with a secon parameter: The create() method is not getting overriden anymore.
So what i did now, is simply renaming the method in de super class, to not override them in the facade classes.
by the way: all services classes have been created by netbeans generator... maybe there is a bug in it

Here are some of the pointers
Make sure you have jettison jar in your classpath, CXF automatically registers jettison as json provider.
#Context Context is not mandatory, so if you want to access some context parameters you can add.
For Method create add Media Type #Consumes(MediaType.APPLICATION_JSON)
Finally Check why you are getting No message body reader has been found for request class Object Ideally you should have got No message body reader has been found for request class Course(There might be some issues with your class definations)

Related

Inherited controller method without type parameter does not get mapped with RequestMapping

I created a base Spring controller for my latest web app project that I inherit from for all my basic CRUD controllers, called CrudController:
class CrudController<T, K extends Serializable>
T is the Entity type, and K is the key used for that type (pretty much the parameters needed for Spring's CrudRepository). I create a rest controller by extending it, like so:
#RestController
#RequestMapping(path = "/hats")
public class HatController extends CrudController<Hat, Integer> {
}
Everything works great, except for the method that gets the list of items, /hats. With this method, I get a 405 Method Not Allowed. When I look at the logging that was done during startup, I can see that RequestMappingHandlerMapping did not map {[/hats],methods=[GET]} but it did map the other four methods in the controller. Here is the code for the method that isn't being mapped:
#RequestMapping(method = RequestMethod.GET)
public HttpEntity<?> getAll() {
Iterable<T> result = controllerRepository.findAll();
return new ResponseEntity<Object>(result, HttpStatus.OK);
}
After much experimentation, I have discovered that if I add a parameter to the getAll method that is one of the class parameter types, the method will get mapped. Check this very similar code out:
#RequestMapping(method = RequestMethod.GET)
public HttpEntity<?> getAll(K dontuse) {
Iterable<T> result = controllerRepository.findAll();
return new ResponseEntity<Object>(result, HttpStatus.OK);
}
With that small code change, the /find call works fine. I can keep my dummy parameter in there and everything will work, but it smells.
Any ideas? A simplified project that replicates the issue can be found here:
Simple Project
There's currently a bug in Java, see bug reports here and here, where Class#getDeclaredMethods() returns a bridge Method for each inherited method from a superclass declared as package-private. Based on the javadoc, it shouldn't do that.
This confuses Spring MVC's inspection of #Controller annotated classes and handler methods. Going into detail in this answer won't be very useful, but you can look at the code that handles it here.
The simplest solution is to declare your CrudRepository class as public.
public class CrudController<T, K extends Serializable>

Override class level #Path annotation on the method

I have two java files that contain endpoints that deal with file management. One is called FileResource.java and the other is DirectoryResource.java. DirectoryResource.java has only one method, which is createDirectory. I need to move that method over to FileResource.java and remove DirectoryResource.java completely.
The problem is that the endpoint for the createDirectory method is currently /api/dir/create. When I move it over to FileResource.java it won't work anymore because the class-level #Path annotation is at "/file/" instead of "/dir/".
Here's my question: Is it possible to override the #Path annotation on the method so that I can to maintain the endpoint /api/dir/create after moving it to the FileResource class?
I want to make sure that those who are using the api don't have to refactor their code to point to a new endpoint.
//FileResource.java
...
#Path("/file/")
public class FileResource() {
#POST
#Path("create")
public Response createFile(String fileContent) {
...
return Response.ok().build();
}
...
}
//DirectoryResource.java
...
#Path("/dir/")
public class DirectoryResource() {
#POST
#Path("create")
public Response createDirectory(String path) {
...
return Response.ok().build();
}
...
}
There's no 'overriding' of #Path annotation. They add.
Method annotated with #Path("create") in the class annotated with #Path("dir") will resolve to /dir/create.
You define the path by defining correct methods in correct channels. You move methods and delete channels only if you need to change pathes.
I see no reason you need to change the channel without changing the API, but if you still need to, you should play, for example, with mod_rewrite on Apache. But I'd advise against it. Just keep your channels structure clean.

JAXB cannot handle an interface - what am I missing?

I'm getting familiar with web services in Java using Jax-ws (or JAXB, not sure, anyway...).
I've created small project with a single webservice. The WS has the only endpoint called transfer and returns objects inheriting ITransferResult interface.
Web service contract
//Service Endpoint Interface
#WebService
#SOAPBinding(style = Style.RPC)
public interface IBankWebSrv {
#WebMethod
ITransferResult transfer(String accountNumber, double amount);
}
Web service implementation
//Service Implementation
#WebService(endpointInterface = "Contracts.IBankWebSrv")
public class BankWebSrv implements IBankWebSrv {
#Override
public ITransferResult transfer(String accountNumber, double amount) {
ITransferResult result = new TransferResult();
// TODO logic here
result.setSuccessful(true);
return result;
}
}
TransferResult contract
#XmlJavaTypeAdapter(TransferResult.class)
public interface ITransferResult {
boolean isSuccessful();
void setSuccessful(boolean successful);
}
TransferResult implementation
public class TransferResult extends XmlAdapter<TransferResult, ITransferResult>
implements ITransferResult {
#XmlElement
boolean successful;
public boolean isSuccessful() {
return this.successful;
}
public void setSuccessful(boolean successful) {
this.successful = successful;
}
#Override
public TransferResult marshal(ITransferResult v) throws Exception {
return (TransferResult) v;
}
#Override
public ITransferResult unmarshal(TransferResult v) throws Exception {
return (ITransferResult) v;
}
}
When I publish my web service, I get the following error:
Exception in thread "main" javax.xml.ws.WebServiceException: Unable to
create JAXBContext...
Caused by: java.security.PrivilegedActionException:
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1
counts of IllegalAnnotationExceptions ITransferResult is an interface,
and JAXB can't handle interfaces. this problem is related to the
following location: at ITransferResult
I've looked over SO for the answer and applied to the most repetitive tips, but none of them have worked for me yet.
What am I missing?
You may need to change the style to be DOCUMENT instead of RPC in your declaration at #SOAPBinding(style = Style.RPC)
Although this is an old question, I thought I'd answer it as it's common exception people encounter.
The difference between the two styles in high level is as follows
Document: The return type and method arguments are clearly explained in a separate XSD with each type in detail - helpful in case of custom data types (Example in your case ITransferResult or java.util.List).
RPC: the types are defined in the WSDL itself in simple manner.
It looks like it's not processing the annotations on the TransferResult class as a bindable element. That means you probably need to add #XmlSeeAlso(TransferResult.class) to the interface (ITransferResult). You also need to put #XmlRootElement on the serialization-implementation (TransferResult) so that an actual XML document can be produced, and not just a type that you use in some other document. This is because when the JAX-WS implementation is creating the JAXB context that it uses internally, it only uses the argument and result types that you define on the service interface as arguments to JAXB.newInstance(…); anything not literally listed there (or findable via simple following the types) will be omitted, and it's entirely possible that the type adapters used are not processed for annotations (after all, they don't need to be instances of the interface they're adapting, nor does the type being adapted need to be an interface).
(Yes, a SOAP response is an enclosing document, but the recommended way of using it is to put a single element inside the SOAP Body, and that means you need to know the name of the element. Which means an #XmlRootElement annotation.)
Warning: I'm not 100% sure that this will work. If it doesn't, you'll have to switch to using concrete types (probably straight POJOs) as results. It might not be a particularly palatable thing, but it's at least easy to do…

Inheritance conflicts in resteasy

My issue is quite simple, I hava a generic parent class with the following JAXRS definition
#POST
#Restricted(permissions = {"*_create"})
public Response save(T entity) throws Exception {
And I created a specific child class with generic parameter T becoming Access type, which have the following declaration:
#POST
#Restricted(permissions = {"*_create"})
#Consumes({"application/x-www-form-urlencoded", "application/json", "application/xml"})
public Response save(final Access newAccess, #HeaderParam("Authorization") String token, #Context HttpServletRequest request) throws Exception {
My issue is that Resteasy has apparently a random behavior that is set on war launch that it will keep for application lifetime. Some time it associates the incomming POST request to the parent save method, sometime to the child one. I aim to get the child one being systematically used, but I want to avoid to change my parent class as lot's of resource defining classes in my project rely on it with no issue (and do not override the save method for instance). Is there an easy (like in resteasy) way to fiw this issue ?
As for now the solution I found was to override the parent method signature public Response save(T entity) throws Exception in the child class, without any annotation but the #Override. The method definition contains a throw new UnsupportedOperationException();.
#Override
public Response save(Access entity) throws Exception {
throw new UnsupportedOperationException();
}
This make the other save declaration with jaxrs annotation in this child class to be chosen without ambiguity.

Generic wrapper method for Play! models

I'm trying to implement a RESTful interface for my Play! framework models using a generic wrapper.
I want to use a generic method to call and return each model's respective "find" methods.
private static <T extends GenericModel> void getModel(T model, Params params){
if (params._contains("id")){
renderJSON(model.findById(params.get("id", Long.class)));
}
else{
renderJSON(model.findAll());
}
}
The above method is called as follows, in my controller's GET method according to which model is requested through a particular route:
getModel(new User(), params);
Since the find() methods are actually static methods of the GenericModels class, it should entirely be possible. However, since Play generates the code for each defined model I get this error:
UnsupportedOperationException occured : Please annotate your JPA model with #javax.persistence.Entity annotation.
At least, I think that's why. Is there no way around this? Am I forced to tediously implement the same GET, PUT, UPDATE, DELETE methods for each class?
I think "model.findById" calls the GenericModel.findById static function which is not implemented and generates the exception. It doesn't call the static function enhanced by JPAPlugin at runtime.
I'm not sure it will work but you should try to call directly JPQL function, something like:
private static <T extends GenericModel> void getModel(Class<T> clazz, Params params){
if (params._contains("id")){
renderJSON(JPQL.instance.findById(clazz.getSimpleName(), params.get("id", Long.class)));
}
else{
renderJSON(model.findAll());
}
and call it like the following:
getModel(new User(), User.class, params);

Categories

Resources