Problems with gzip compression with jersey - java

I want to enable compression features on jersey. I have a webservice that must return a list of events on json format.
For exemple :
{
"page": 1,
"pageCount": 1,
"list": [
{
"pk": 1,
"state": "ACTIVE",
"locationType": "ADDRESS",
"title": "titre",
"description": "hello",
"country": "osef",
"city": "osef",
"address": "osef",
"inAgendaCount": 0,
"dateBeginning": 1498734400758
}
]
}
But when I enable gzip compression, the response is like this :
{
"page": 1,
"pageCount": 1,
"list": [
{
}
]
}
All the events in the list are cut off.
Here is my webservice :
#Component
#Path("/")
public class EventSearchWebService {
#Autowired
private EventSearchController controller;
#GET
#Path("/event/search")
#Produces(MediaType.APPLICATION_JSON)
#JsonView(SmallView.class)
public Response search(#QueryParam("keywords") String keywords, #DefaultValue("1") #QueryParam("page") Long page) {
if (keywords != null && !keywords.trim().isEmpty()) {
return Response.status(200).entity(controller.search(keywords, page)).build();
} else {
return Response.status(200).entity(controller.search(page)).build();
}
}
}
My resourceConfig with compression :
#Configuration
#ApplicationPath("/rest")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
// Compression
register(EntityFilteringFeature.class);
EncodingFilter.enableFor(this, GZipEncoder.class);
register(EventSearchWebService.class);
register(MultiPartFeature.class);
register(ValidationFeature.class);
register(ValidationExceptionMapper.class);
register(CrossDomainContainerResponseFilter.class);
registerClasses(AccessDeniedExceptionMapper.class,
GeneralUserExceptionMapper.class,
NoResultExceptionMapper.class,
UnknowExceptionMapper.class,
ValidationExceptionMapper.class);
}
}
And my pageableList class :
public class PageableList<E> {
#Getter
#Setter
#JsonView(value = {SmallView.class, FullView.class})
private long page;
#Getter
#Setter
#JsonView(value = {SmallView.class, FullView.class})
private long pageCount;
#Getter
#Setter
#JsonView(value = {SmallView.class, FullView.class})
private List<E> list;
public PageableList() {
super();
}
public PageableList(List<E> list) {
super();
this.list = list;
}
}
There is no errors in the logs. Is there someone that have a clue of what's happening ?
Thank you
EDIT : after further investigations, I found out that if I send directly my list of events without PageableList, I have the error :
Can not resolve PropertyFilter with id 'org.vagrant.server.entity.EventEntity'; no FilterProvider configured (through reference chain: java.util.ArrayList[0])

The problem was in the ResourceConfig.
This line :
register(EntityFilteringFeature.class);
is useless and cause the bug.

Related

Swagger/OpenAPI 3.0 generation - adding a new content-type hides previous schema information

I am using Java annotations to build our swagger documentation. In one case, we want to provide output in either JSON or csv depending on the "Accepts" header. I created the rest interface like this:
#RestController
#EnableAutoConfiguration
#RequestMapping(path="/api/v2/swaggerTest")
#Api
public class SwaggerDocResource {
private static class ItemDto {
String name;
Integer age;
}
#ApiOperation(value = "Get the requested items in json")
#GetMapping(produces = "application/json")
public ResponseEntity<ItemDto> getItems() {
return null;
}
#ApiOperation(value = "Get the requested items in csv")
#GetMapping(produces = "text/csv")
public ResponseEntity<String> exportItems() {
return null;
}
}
If I comment out the csv method, the generated Swagger doc generates a schema for my DTO class and references it:
...
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ItemDto"
}
}
}
},
...
However, if I do include the csv method, the schema for my DTO class is no longer generated and both types of response are given the same schema:
...
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "string"
}
},
"text/csv": {
"schema": {
"type": "string"
}
}
}
},
...
Is it possible to assign a different schema to these different content types? I have been unable to figure out how.

Lombok returns null as a value of response

I've got a problem with my Api tests.
When i'm trying to get data from api, lombok returns null as an acceptance value, but there are values with real numbers in api.
Screenchot: https://prnt.sc/w98nt2
My DTO for the responce:
#Data
#Builder
#EqualsAndHashCode
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
public class PositionStatResponceDto {
private Integer keywordTasksCount;
private Integer doneKeywordTasksCount;
private Integer tasksCount;
private Integer doneTasks;
}
My steps that extacts body and send post request
public class PositionSteps {
PositionsController positionsController = new PositionsController();
#Step("Post body with url: http://prod.position.bmp.rocks/api/aparser/get-statistic")
public PositionStatResponceDto postBody(PositionStatDto positionStatDto) {
return positionsController
.getStatistic(positionStatDto)
.statusCode(200)
.extract().body().as(PositionStatResponceDto.class);
}
}
Api json responce getting properly. it means that the request working right:
{
"period": {
"20201224": {
"startTime": "2020-12-24 00:00:19",
"endTime": "2020-12-24 06:39:30",
"totalRequestsCount": 0,
"totalQueriesCount": 161887,
"totalQueriesDoneCount": 161887,
"totalFailCount": 161,
"successfulQueries": 161726,
"proxiesUsedCount": 6.49,
"retriesUsedCount": 0,
"avgSpeed": 13.74,
"tasksCount": 1537,
"doneTasks": 1537,
"keywordTasksCount": 725,
"doneKeywordTasksCount": 725,
"runTime": "06:39:11",
"avgTimePerKeyword": 0.15,
"keywordsLost": 0.1
}
},
"avg": {
"totalRequestsCount": 0,
"totalQueriesCount": 161887,
"totalQueriesDoneCount": 161887,
"totalFailCount": 161
}
}
I made post request in the similar way to the api:
{
"success": 1,
"data": {
"45.90.34.87:59219": [
"http"
],
"217.172.179.54:39492": [
"http"
],
"144.76.108.82:35279": [
"http"
],
"5.9.72.48:43210": [
"http"
],
"144.76.108.82:47165": [
"http"
],
"45.90.34.87:57145": [
"http"
],
"144.76.108.82:53108": [
"http"
],
...
} }
And it works correctly with dto:
#Data
#Builder
#EqualsAndHashCode(exclude = "success")
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
public class AparsersResponceDto {
private Integer success;
private Map<String, List<String>> data;
}
Help me please. I can't understand what's wrong with the first example. Each of the Dto values returs 'null'.
Your DTO does not match the structure of the response you are parsing. You have a nested structure where on DTO you are expecting only to receive primitive values. On upper level you have a structure with two fields.
{
"period": {...},
"avg": {...}
}
From the example I would assume that period is a key-value pair of dates as a key and your PositionStatResponceDto as a value.
{
"period" : {
"20201224": { <-- nested key with a value matching your DTO
PositionStatResponceDto
}
...
}
So this means only single item in the key-value pairs match the DTO you have defined but is ignoring all other nested structure elements. For this it would make sense to introduce new wrapper DTO to handle the nested structure.
e.g.
public class StatDTO {
private Map<String,PositionStatResponceDto> period;
//add avg if needed
}

Jackson deserialization bellow JSON property

I want to fetchMultiple(ParameterizedTypeReference<List<T>> responseType) for a given List<T>, in this case, I want to get directly a List<Account> but I am getting an error because the list of accounts is encapsulated in another object, as shown below:
{
"accounts": [
{
"accountUid": "c75deb59-5d52-4a23-af7b-fce29927ce9d",
"defaultCategory": "b4189da5-7688-42d0-86e3-14ae9031e01d",
"currency": "GBP",
"createdAt": "2020-08-05T16:50:50.536Z"
}
]
}
There is some Jackson annotation to filter this somehow in order to be processed like this:
[
{
"accountUid": "c75deb59-5d52-4a23-af7b-fce29927ce9d",
"defaultCategory": "b4189da5-7688-42d0-86e3-14ae9031e01d",
"currency": "GBP",
"createdAt": "2020-08-05T16:50:50.536Z"
}
]
POJO
#Data
public class Account {
private String accountUid;
private String defaultCategory;
private String currency;
private String createdAt;
}
RestRequestTemplate.java
public List<T> fetchMultiple(ParameterizedTypeReference<List<T>> responseType) {
return new RestTemplate().exchange(this.url, this.httpMethod, this.request, responseType).getBody();
}
AccountsServiceImpl.java
public List<Account> getAccounts() {
RestRequestTemplate restRequestTemplate = new RestRequestTemplate(GET_ACCOUNTS, HttpMethod.GET, Collections.EMPTY_MAP);
return restRequestTemplate.fetchMultiple(new ParameterizedTypeReference<List<Account>>() {});
}
There is indeed an annotation to ignore the root object. It is called #JsonUnwrapped. Annotate your method with that annotation and your json should be without the root object.

Spring Data REST - Exclude Subtypes

Lets say I have the following Hibernate Entities (fields ommitted)
#Entity
#DiscriminatorColumn(name = "T")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class SuperClass {
}
#Entity
#DiscriminatorValue(name = "subClassA")
public SubClassA extends SuperClass {
}
#Entity
#DiscriminatorValue(name = "subClassB")
public SubClassB extends SuperClass {
}
With Spring Data REST I would get the following JSON representation:
{
"_links": {
},
"_embedded": {
"subclassA": [
{
"field1": "",
"field2": ""
}
],
"subclassB": [
{
"field1": "",
"field2": "",
"field3": ""
}
]
}
}
Again ommiting the _links attributes. Is there some sort of configuration I can use so the Serializer can ignore the subclasses and do a representation like this:
{
"_links": {
},
"_embedded": {
"superClass": [
{
"field1": "",
"field2": ""
},
{
"field1": "",
"field2": "",
"field3": ""
}
]
}
}
One way to solve the problem would be the implementation of a RelProvider. All you need to do is to implement it and add it to spring container (this could be done but i.e. annotating the implementation with #Component).
Considering that you would be able to get the response you are expecting simply by adding the following implementation (considering it will be scanned by spring):
#Component
public class MessageRelProvider implements RelProvider {
public boolean supports(Class<?> arg0) {
return SuperClass.class.isAssignableFrom(arg0);
}
public String getItemResourceRelFor(Class<?> type) {
return "superClass";
}
public String getCollectionResourceRelFor(Class<?> type) {
return "superClasses";
}
}

Jackson: deserializing recursive object

I'm trying to parse the filter parameters sent by a KendoUI grid to my web service and am having some issues convincing Jackson to parse this JSON. As far as I know, I can control the format of the parameters that Kendo sends, but I do not know how I would marshal the parameters into a better format so they remain unchangeable for now.
I intend to convert these parameters into a SQL query for an Oracle database.
Example JSON:
{
"filters":
[
{
"field": "Name",
"operator": "contains",
"value": "John"
},
{
"filters": [
{
"field": "Age",
"operator": "gt",
"value": 20
},
{
"field": "Age",
"operator": "lt",
"value": 85
}
],
"logic", "and"
},
{
"field": "Address",
"operator": "doesnotcontain",
"value": "street"
}
],
"logic": "or"
}
Filters. Java
public class Filters {
private List<Filter> filters;
private String logic;
// accessors/mutators/toString
}
Filter.java
public class Filter {
private String field;
private String operator;
private String value;
// accessors/mutators/toString
}
Unit Test
public class KendoGridFilterTest {
private ObjectMapper mapper;
#Before
public void before() {
mapper = new ObjectMapper();
}
#Test
public void jsonParseTest() {
final String json = "{\"filters\":[{\"field\":\"Name\",\"operator\":\"contains\",\"value\":\"John\"},{filters: [{\"field\":\"Age\",\"operator\": \"eq\",\"value\": 85},{\"field\": \"Age\",\"operator\": \"eq\",\"value\": 85}]\"logic\", \"and\",},{\"field\": \"Address\",\"operator\": \"doesnotcontain\",\"value\": \"street\"}],\"logic\":\"or\"}";
Filters filters = mapper.readValue(json, Filters.class);
assertTrue(json.equals(filters.writeValueAsString(filters);
}
}
Errors
com.fasterxml.jackson.databind.UnrecognizedPropertyException: Unrecognized field
'logic'(com.example.Filter) not market as ignorable (3 known properties
"value", "field", "operator")
at [Source: java.io.StringReader#3bb2b8; line: 1, column 76] (through reference
chain: com.example.Filters["filters"]->com.example.Filter["logic"]
I've also tried adding #JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#id") to the Filters class and get the same errors.
your Filter class is not correct. It should extend Filters.
After correcting your unit test (json is incorrect) it can load your json into a Filters Object.
public class Filter extends Filters {
private String field;
private String operator;
private String value;
// accessors/mutators/toString
}

Categories

Resources