Why does Jersey 3.1.0 fail to send some fields of the response that earlier Jersey version do? - jersey

We recently had to upgrade from Jersey 3.0.2 to Jersey 3.1.0 (due to an upgrade of Tomcat), and discovered that a REST method would sometimes fail to send all the appropriate fields of the response structure to the client, unlike the previous version of Jersey. That is, some fields would get sent, and others won't. How to handle this?
class Display {
int foo; public int getFoo() { ... }
}
class Display2 extends Display {
int bar; public int getBar() {...}
}
#POST
#Path("/move")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.APPLICATION_JSON)
public Display move(....) {
Display x= new Display2(...);
return x;
}
One would of course expect the return JSON structure sent by /move to the client to include both x.foo and x.bar. That's what it did in Jersey 2.* and 3.0.2. But now in Jersey 3.1.0, only x.foo gets sent!

A bit of experimentation shows that Jersey has apparently switched to somewhat more static typing! With Jersey 3.1.0, if a method is declared as
Display move() { ... }
and it returns an object x of the type Display2 that's a subclass of Display, then only the fields of x that are declared in Display are returned, and not those that are declared in Display2.
Our solution was to change the signatures of our REST methods. That is, if the method is likely to return an object of the type Display2 rather than Display, then we need to explicitly declare the method as returning Display2... and then all is well!
Of course, if a method sometimes returns an object of the type Display and sometimes Display2, then we need to to add a "converting" method,
class Display2 {
static Display2 convertFrom(Display x) { ... }
}
which would basically create a Display2 object with the same fields as the input Display object was, plus some dummy values (nulls or 0s) in the other fields.
So this works for us, but I still have no idea why the Jersey people needed to make that change!

Related

Spring Data Rest custom controller with patch method - how to merge resource with entity

By default when we have a repository with save method exposed we can do a PATCH request. Then Spring Data REST retrieves the original object from the database and apply changes to entity and then saves it for us (inside JsonPatchHandler class). This allows us to do the following request for class
class Address {
Long id;
String street;
Long houseNumber;
}
PATCH /api/addresses/1 with body
{ houseNumber: 123 }
And only this one field will be changed.
Now having custom controller we would like to in the update method receive the whole object (after HATEOAS merged it with the original object from the DB)
#RepositoryRestController
#ExposesResourceFor(Address.class)
#ResponseBody
#RequestMapping("/addresses")
public class AdddressController {
#PatchMapping("/{addressId}")
public Resource<Address> update(#RequestBody Resource<Address> addressResource, #PathVariable Long addressId) {
Address address= addressResource.getContent();
// .... some logic
address = addressRepository.save(address);
return new Resource<>(address);
}
}
Unfortunately in the place where I would do some logic I get the Address with null fields instead of the merged object.
Is it possible to plug the custom controller in the Spring Data REST stack so that when patching the request it will merge it for me (as it does for normal repositories)?
edit:
I would like to find a solution that works transparently both with PATCH(content-type:application/json-patch+json) and PATCH(content-type: application/hal+json)
After browsing the Spring sources I haven't found a reasonable solution. As a result I've created issue in their - JIRA
For the moment the only reasonable workaround is following - create custom controller that has PersitentEntityResource as a parameter and has both {id} and {repository} placeholders in its path i.e.
#PatchMapping("/addresses/{id}/{repository}")
public Resource<Address> update(PersistentEntityResource addressResource) {
...
}
which makes the invocation endpoint /addresses/123/addresses

Can I "inject" values from message resources into model objects before implicit Jackson serialisation?

I have a REST API built with Spring Boot / Spring MVC, using the implicit JSON serialization via Jackson.
Now, just before the implicit serialization, I would like to "inject" some UI texts from message resources into the objects that Jackson converts into JSON. Is there some neat, simple way to do this?
As a much simplified example, below I'd like to set Section title to a user-visible value, based purely based on its SectionType.
(Sure, I could hardcode the UI texts in SectionType, but I'd rather keep them separate, in resource files, because it's cleaner, and they might be localised at some point. And I can't autowire MessageSource in the entities / model objects which are not Spring-managed.)
#Entity
public class Entry {
// persistent fields omitted
#JsonProperty
public List<Sections> getSections() {
// Sections created on-the-fly, based on persistent data
}
}
public class Section {
public SectionType type;
public String title; // user-readable text whose value only depends on type
}
public enum SectionType {
MAIN,
FOO,
BAR;
public String getUiTextKey() {
return String.format("section.%s", name());
}
}
Somewhere in a #RestController:
#RequestMapping(value = "/entry/{id}", method = RequestMethod.GET)
public Entry entry(#PathVariable("id") Long id) {
return service.findEntry(id);
}
UI texts that I'd like to keep separate from code (messages_en.properties):
section.MAIN=Main Section
section.FOO=Proper UI text for the FOO section
section.BAR=This might get localised one day, you know
And what I'd like to do in a Spring-managed service/bean somewhere (using Messages, a very simple helper wrapping a MessageSource):
section.title = messages.get(section.type.getUiTextKey())
Note that if I call entry.getSections() and set the title for each, it will not affect the JSON output, since the Sections are generated on the fly in getSections().
Do I have to go all the way to custom deseriazation, or is there a simpler way to hook into the model objects just before they get serialized by Jackson?
Sorry if the question is unclear; I can try to clarify if needed.
As I said in the comment you can write an Aspect around every controller method that returns Section.
I wrote a simple example. You have to modify it with the message source.
Controller:
#RestController
#RequestMapping("/home")
public class HomeController {
#RequestMapping("/index")
public Person index(){
Person person = new Person();
person.setName("evgeni");
return person;
}
}
Aspect
#Aspect
#Component
public class MyAspect {
#Around("execution(public Person com.example..*Controller.*(..))")//you can play with the pointcut here
public Object addSectionMessage(ProceedingJoinPoint pjp) throws Throwable {
Object retVal = pjp.proceed();
Person p = (Person) retVal; // here cast to your class(Section) instead of Person
p.setAge(26);//modify the object as you wish and return it
return p;
}
}
Since the aspect is also a #Component you can #Autowire in it.

Handle extra elements outside of deserialized class

Putting extra elements property in the class to support backward/forward compatibility and implement ISupportInitialize seems ugly for me and it is also OCP violation.
I want to handle extra elements outside of deserialized class and run some migration logic in external classes.
I mean that Bson serializer will put extra elements data somewhere not on the deserialized class and than after finishing all deserialization staff call some migrator for loaded object.
That way I can can support compatibility between fetched document (that may be in older or newest version) and currently running code.
Something like:
public interface IMigrate<T>
{
void Migrate(T obj, BsonDocument extraElements)
}
public class MigrateClazzA : IMigrate<ClazzA>
{
public void Migrate(ClazzA obj, BsonDocument extraElements)
{
...
}
}
How can I do it?

Swagger response type getting defaulted to void unless ApiOperation.response() explicitly provided

I have an application for my REST APIs using jersey (version 1.17).
I am trying to set up swagger to document the same using swagger-maven-plugin (version 2.2)
Swagger documentation suggests:
In JAX-RS applications, the return type of the method would automatically be used,
unless it is {#code javax.ws.rs.core.Response}.In that case, the operation return type would default to void as the actual response type cannot be known.
I am using a custom object (not javax.ws.rs.core.Response) as my method return type.
But the response type remains void unless I specify it as part of the ApiOperation annotation.
#GET
#Produces({MediaType.APPLICATION_JSON})
#ApiOperation(
value = "Description",
response = MyCustomResponse.class
)
public MyCustomResponse myApiMethod() {
return apiResponse;
}
I want to remove the response value from the annotation.
What am I doing wrong here?

How can I set a value after data binding, but before validation, in Spring MVC 3?

I need to set a "time submitted" field on one of my domain objects. Validation rules require that property to be there, but I can't have it set by data binding from the form because then the user could change it. How can I set a property of the bound object (or about-to-be-bound object) before validation occurs?
I don't know if I understood your question correctly, but you have 2 options to manipulate your data, on client side using Java Script, on server side using customer Validator.
public class PersonValidator implements Validator {
/**
* This Validator validates just Person instances
*/
public boolean supports(Class clazz) {
return Person.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
Person p = (Person) obj;
p.setanyValue(some value); //changing object value
if (p.getAge() < 0) {
e.rejectValue("age", "negativevalue");
} else if (p.getAge() > 110) {
e.rejectValue("age", "too.darn.old");
}
}
}
Hope it helps.
Maybe Stuff
I believe that binding starts by creating a new instance of a class. Assuming that this is true, you could add a blah = new Date(); in the constructor. Although this would happen before binding, I believe it fulfills the core requirement of get the submit time, but don't let the client edit it.
If this is wrong, check out the InitializingBean interface (or the corresponding init-method bean attribute).
Looks like spring 3.0 has even more lifecycle options. check out section 3.6.1.4 Combining lifecycle mechanisms in the Spring 3.0 Reference.
Better Info
You should be able to register a handler interceptor and set the submit time therein.
Check out the 15.4.1 Intercepting requests - the HandlerInterceptor interface section in the Spring 3.0 Reference (link is above).

Resources