Inheritance conflicts in resteasy - java

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.

Related

Java method Overloading with REST - org.codehaus.jackson.map.JsonMappingException

I have a REST service that has a POST endpoint. This POST endpoint needs to receive an object (TravelRequisitionFormDTO) as part of its body:
#POST
#Path("/request")
#ApiOperation(value="Request a trip. Submit the trip request.")
#ApiResponses({
#ApiResponse(code=200, message="Success"),
#ApiResponse(code=404, message="Not Found")
})
#Produces({ MediaType.APPLICATION_JSON })
public Response getSubmitTrip(#HeaderParam("Authorization") String token, #ApiParam(required = true) TravelRequisitionFormDTO travelRequisitionFormDTO, #Context HttpServletRequest request) {
...
}
So when I call the endpoint, I get the following error:
<p><b>message</b> <u>org.codehaus.jackson.map.JsonMappingException: Conflicting setter definitions for property
"contactMethods": utility.dataobjects.ContactObject#setContactMethods(1 params) vs
utility.dataobjects.ContactObject#setContactMethods(1 params)</u></p>
<p><b>description</b> <u>The request sent by the client was syntactically incorrect
(org.codehaus.jackson.map.JsonMappingException: Conflicting setter definitions for property
"contactMethods": utility.dataobjects.ContactObject#setContactMethods(1 params) vs
utility.dataobjects.ContactObject#setContactMethods(1 params)).</u></p>
The reason for the error is because the TravelRequisitionFormDTO has a member variable (called ContactObject) that has two methods that are overloaded. So when it tries to convert the JSON body to JAVA, I guess it does not know which overloaded method to use. I think it sees it as ambiguous.
public void setContactMethods(ArrayList list)
and
public void setContactMethods(String[] list)
I don't want to change ContactObject if possible, because it is used in a number of other places.
Question
Is there any way I can resolve this? i.e. so that the JSON body can be converted successfuly into the Java object?
you can keep single property accepting List. and your Contractobject can consume both Array & List.
You could annotate one setter with Jackson #JsonSetter annotation:
#JsonSetter
public void setContactMethods(ArrayList list)
Make sure that you use right package. In your case it would be org.codehaus.jackson.annotate.JsonSetter like you can see in the error message. It might happen that you have also com.fasterxml.jackson.annotation.JsonSetter in the classpath so you have to be careful not to mix it.
Alternatively you can use #JsonProperty instead.

#Intercepted vs InvocationContext

When implementing an Interceptor, is there a way to get the name of the actual (concrete) class being intercepted using the InvocationContext ?
Unless I am wrong, calling ic.getMethod().getDeclaringClass().getName() will return the (extdended) interface / base abstract class?
Is the #Intercepted annotation the only way of getting the actual implementation being intercepted?
In the past, I have used the following to get the name of the intercepted class:
public Object audit(InvocationContext invocation) throws Exception
{
final String name = invocation.getTarget().getClass().getName();
...
The "target" in this context is the class that is being intercepted.

JSON deserialize only with at least 2 parameters

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)

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…

Specific MessageBodyWriter for field

Say I have a data class in a JAX-RS 1 environment (RestEasy 2 with the Jackson provider) like this:
class Foo {
int id;
String name;
Bar bar;
...
}
with Bar being:
class Bar {
int one;
String two;
}
Now I want to have Bar serialized in a special way (perhaps depending on the media type that was requested (or depending the phase of the moon), I would write a MessageBodyWriter<Bar>
#Provider
#Produces("application/json")
public class BarWriter implements MessageBodyWriter<Bar> {
...
}
which works very well if Bar is requested on its own like in
#GET #Path("bar")
public Bar getBar() { return new Bar(...); }
But when I request Foo as in
#GET #Path("foo")
public Foo getFoo() { return new Foo(...); }
the message body writer is ignored.
Now what I want is that this MessageBodyWriter is also used when I return Foo or a List<Bar>
I think the latter can be achieved by just writing a custom MessageBodyWriter for the List case, but for the former case I can't write a message body writer for all my application classes that contain a Bar field.
Any ideas on how to solve this? I was also trying to use a Jackson serializer on the Bar instance, but it looks like this is not even registered by RestEasy (and then, I think that way is too fragile anyway).
Unfortunately, this is not how message body writers work. The JAX-RS implementation will locate a writer, to be used in serialization, based on the type being returned from your resource method. So in your case, with a custom writer defined for Bar, with this resource method:
#GET #Path("bar")
public Bar getBar() { return new Bar(...); }
the JAX-RS provider will serialize Bar using your custom writer. However for this resource method:
#GET #Path("foo")
public Foo getFoo() { return new Foo(...); }
you do not have a custom writer defined, and serialization will be handled by the first matching (default) provider that can handle the combination of return class and content-type. A key thing to remember is that, unlike typical JSON and XML serialization libraries, JAX-RS entity providers are not recursive. Aka, for a given object A being returned in a resource method, the provider will attempt to locate a custom writer only for A, and not for any of the types included in A as variables.
Since you are using Jackson though, why not just define a custom serializer for your Bar class? That will handle pretty much every scenario you described:
public class BarSerializer extends JsonSerializer<Bar> {
#Override
public void serialize(final Bar value, final JsonGenerator jgen,
final SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeStartObject();
jgen.writeFieldName("myBar");
jgen.writeString(value.getTwo());
jgen.writeEndObject();
}
}
You tell Jackson to use this custom serializer thusly:
#JsonSerialize(using=BarSerializer.class)
class Bar {
int one;
String two;
}
Lastly, don't forget that if you anticipate getting JSON back in the same form as you serialized, that you will also need a custom JsonDeserializer.
To get it to work, you need the jackson-mapper and jackson-jaxrs jars in your classpath (and probably the jackson-core one as well).
The JAX-RS runtime will only look up one MessageBodyWriter for the object returned by the resource method (see sectioin "4.2.2 Message Body Writer" in the specification), then that single MessageBodyWriter has complete control over the serialization of the entire object graph to be returned to the client.
In order to implement the behavior you wanted, you would need a custom MessageBodyWriter per media type, that is willing to delegate the serialization of a part of the object graph to another writer whenever it encounters a specific type in the graph, and then resume its own logic. Obtaining the delegate writer for the specific type wouldn't be a big problem (inject a javax.ws.rs.ext.Providers and call getMessageBodyWriter()), but I don't think the existing xml/json/etc serializers are implemented with such kind of extensions in mind, so I guess you couldn't just relay on them. Reimplementing an xml marshaller just for this is not an attractive option either.
Refer the below post for writing custom message body writer for your Java object's serialization.
http://h2labz.blogspot.in/2014/12/marshalling-java-to-json-in-jax-rs.html

Categories

Resources