JsonGenerationException: Can not write a field name - java

My problem lies in the fields of JsonSerialization as implemented in Jackson FasterXML Library. I have a series of endpoints through which I exchange content between my back-end and a MVVM front-end framework. This is working, but now I am a little stuck as I got to the point where I want to handle user creation/registration.
This is the model (entity) that represents a group in my application (I omit irrelevant import declarations and JPA annotations):
#JsonRootName(value="userGroup")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Groups extends MetaInfo implements Serializable {
private String groupName;
private Set<Credential> credentials = new HashSet<>();
public Groups() {
super();
}
public Groups(String groupName) {
this();
this.groupName = groupName;
}
public Groups(String createdBy, String groupName) {
this();
setCreatedBy(createdBy);
this.groupName = groupName;
}
#JsonGetter("group_Name")
// #JsonValue
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
updateModified();
}
#JsonIgnore
public Set<Credential> getCredentials() {
return credentials;
}
public void setCredentials(Set<Credential> credentials) {
this.credentials = credentials;
}
public void addCredential(Credential c) {
credentials.add(c);
if (c.getGroup() != this) {
c.setGroup(this);
}
}
#Override
public String toString() {
return "Groups{" + "groupName=" + groupName + '}';
}
}
And this is the method in the endpoint that retrieves (if exists) and returns a serialized version of a Groups to a JavaScript client:
#Path("/groups")
#Produces("application/json")
public class GroupsResourceService extends RimmaRestService{
#Inject
#Production
private GroupsRepository groupsRepository;
...
#GET
#Path("{group}")
public Response getGroup(#PathParam("group") String group){
if(InputValidators.stringNotNullNorEmpty.apply(group)){
//proceed with making call to the repo
Optional<Groups> optGroup = ofNullable(groupsRepository.getByGroupName(group));
if(optGroup.isPresent()){//ultimately success scenario
try {
String output = getMapper().writeValueAsString(optGroup.get());
return Response.ok(output).build();
} catch (JsonProcessingException e) {
logger.error("Serialization error: "+e.getMessage()+
"\n"+e.getClass().getCanonicalName());
throw new InternalServerErrorException("Server error "
+ " serializing the requested group \""+group+"\"");
}
} else{
throw new NotFoundException("Group " + group + " could not be found");
}
}else{//empty space after the slash
throw new BadRequestException("You haven't provided a group parameter",
Response.status(Response.Status.BAD_REQUEST).build());
}
}
}
Trying to test this code like this:
#Test
public void successfullResponse(){
Response success = groupsResourceService.getGroup("admin");
assertTrue(success.getStatus()==200);
}
...cruelly fails:
<< ERROR!
javax.ws.rs.InternalServerErrorException: Server error serializing the requested group "admin"
at com.vgorcinschi.rimmanew.rest.services.GroupsResourceService.getGroup(GroupsResourceService.java:54)
at com.vgorcinschi.rimmanew.services.GroupsResourceServiceTest.successfullResponse(GroupsResourceServiceTest.java:48)
In this case stack trace is of 0 help, though - that's why I am pasting the output of the log that catches the underlying Json exception:
15:05:05.857 [main] ERROR com.vgorcinschi.rimmanew.rest.services.GroupsResourceService - Serialization error: Can not write a field name, expecting a value
com.fasterxml.jackson.core.JsonGenerationException
Having visited and analyzed 13 similar complaints links (of which 3 are from stackoverflow) I came up with a solution which is more a workaround - if you look back at the entity, I have commented #JsonValue. If I uncomment that and comment #JsonGetter("group_Name") then the test passes with the following output:
{"userGroup":"admin"}
This being only a workaround, I decided to recur to asking for help which I hope someone will be able and kind enough to provide.

Related

Kafka throws an exception when trying to consume an event

I am building a Domain-Driven Design microservice web application in Java Spring Boot and I have a problem where kafka might help. I am new to Kafka and what I'm trying to accomplish is simply to do something after an event occurs. The event is updating an order (changing it from isApproved = false to isApproved = true), and after updating the order status, I am sending a topic in another package called pet-catalog and from there when the package listen for the topic, it updates the entity Pet (from isAdopted = false to isAdopted = true).
This is what I have so far:
The Domain Event publisher (location package com.ddd.sharedkernel.infra;)
public interface DomainEventPublisher {
void publish(DomainEvent event);
}
The Domain Event (location package com.ddd.sharedkernel.domain.events;)
#Getter
public class DomainEvent {
private String topic;
private Instant occurredOn;
public DomainEvent(String topic) {
this.occurredOn = Instant.now();
this.topic = topic;
}
public String toJson() {
ObjectMapper objectMapper = new ObjectMapper();
String output = null;
try {
output = objectMapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
}
return output;
}
public String topic() {
return topic;
}
public static <E extends DomainEvent> E fromJson(String json, Class<E> eventClass) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json,eventClass);
}
}
A Class where we call its constructor when an Order is approved
(location package com.ddd.sharedkernel.domain.events.orders;)
#Getter
public class OrderApproved extends DomainEvent {
private final String orderId;
public OrderApproved(String orderId){
super(TopicHolder.TOPIC_ORDER_APPROVED);
this.orderId=orderId;
}
}
Topic holder
(location package com.ddd.sharedkernel.domain.config;)
public class TopicHolder {
public final static String TOPIC_ORDER_APPROVED = "order-approved";
}
Domain event publisher implementation
(location package com.ddd.ordermanagement.infra;)
#Service
#AllArgsConstructor
public class DomainEventPublisherImpl implements DomainEventPublisher {
private final KafkaTemplate<String, String> kafkaTemplate;
#Override
public void publish(DomainEvent event) {
this.kafkaTemplate.send(event.topic(), event.toJson());
}
}
Publishing an event after updating the order
(location: package com.ddd.ordermanagement.service.impl;)
#Override
#Transactional
public void approveOrder(OrderId orderId) {
Order order = orderRepository.getById(orderId);
order.approveOrder();
//Now after we approve the order that we want to approve
//we need to delete all other orders that are made for the same pet
//because we just approved one order for that pet and now that pet is adopted
//we dont need the remaining orders for that pet
List<Order> orderList = orderRepository.findAll();
for (Order orderToBeDeleted : orderList) {
if (!orderToBeDeleted.isApproved() && orderToBeDeleted.getPetId().getId().equals(order.getPetId().getId())){
orderRepository.deleteById(orderToBeDeleted.getId());
}
}
//After deleting the remaining orders
//we need to update the adopted pet (change its status from false to true)
//this is just hard coded ID for the pet to be updated but the listener throws an
//exception even before it reads the ID
domainEventPublisher.publish(new OrderApproved("e49b4057-582b-42fa-beed-e3b9e6811cdc"));
//The exception is thrown in the code below (PetEventListener)
}
And finally the Event Listener
(location: package com.ddd.petcatalog.xport.events;)
#Service
#AllArgsConstructor
public class PetEventListener {
private final PetService petService;
#KafkaListener(topics = TopicHolder.TOPIC_ORDER_APPROVED, groupId = "petCatalog")
public void consumeOrderApproved(#Payload(required = false) String jsonMessage){
try{
System.out.println("Topic consumed successfully"); //Prints
OrderApproved event = DomainEvent.fromJson(jsonMessage, OrderApproved.class);
System.out.println("Print after creating an event"); //Doesn't print
petService.updatePet(event.getOrderId());
System.out.println("event.getOrderId(): "+ event.getOrderId());//Doesn't print
} catch (Exception e){
e.printStackTrace();
}
}
}
This is the exception output.
Topic consumed successfully
java.lang.IllegalArgumentException: argument "content" is null
at com.fasterxml.jackson.databind.ObjectMapper._assertNotNull(ObjectMapper.java:4757)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3515)
at mk.ukim.finki.emt.sharedkernel.domain.events.DomainEvent.fromJson(DomainEvent.java:37)
at mk.ukim.finki.emt.petcatalog.xport.events.PetEventListener.consumeOrderApproved(PetEventListener.java:27)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:171)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:120)
at org.springframework.kafka.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:56)
at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:347)
at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:92)
at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:53)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeOnMessage(KafkaMessageListenerContainer.java:2323)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeOnMessage(KafkaMessageListenerContainer.java:2304)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeRecordListener(KafkaMessageListenerContainer.java:2218)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeWithRecords(KafkaMessageListenerContainer.java:2143)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeRecordListener(KafkaMessageListenerContainer.java:2025)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeListener(KafkaMessageListenerContainer.java:1707)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeIfHaveRecords(KafkaMessageListenerContainer.java:1274)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.pollAndInvoke(KafkaMessageListenerContainer.java:1266)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.run(KafkaMessageListenerContainer.java:1161)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.lang.Thread.run(Thread.java:831)

Is there a way to Spring custom validator reject value without field having a getter method?

I'm currently studying building API's with Spring. I'm working with Spring Validator to validate my input. Here it is my custom validator:
public class NewHoldValidator implements Validator {
private EntityManager manager;
public NewHoldValidator(EntityManager manager) {
this.manager = manager;
}
#Override
public boolean supports(Class<?> clazz) {
return NewHoldRequest.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
if (errors.hasErrors()) {
return;
}
NewHoldRequest request = (NewHoldRequest) target;
Patron patron = manager.find(Patron.class, request.patronId);
BookInstance bookInstance = manager.find(BookInstance.class, request.bookInstanceId);
Assert.state(patron != null, "Patron does not exists.");
Assert.state(bookInstance != null, "Book instance does not exists.");
if (!bookInstance.acceptToBeHoldTo(patron)) {
errors.reject(null, "This book instance cannot be hold to this patron");
}
if (!request.hasDaysHold()) {
if (!patron.researcher()) {
errors.rejectValue("daysHold", null, "You need to pass a daysHold attribute");
}
}
}
}
And here is my NewHoldRequest class:
public class NewHoldRequest {
#NotNull
public final Long patronId;
#NotNull
public final Long bookInstanceId;
#Positive
#Max(60)
public final Integer daysHold;
public NewHoldRequest(#NotNull Long patronId, #NotNull Long bookInstanceId, #Positive #Max(60) Integer daysHold) {
this.patronId = patronId;
this.bookInstanceId = bookInstanceId;
this.daysHold = daysHold;
}
#Override
public String toString() {
return "NewHoldRequest{" + "patronId=" + patronId + ", bookId=" + bookInstanceId + ", daysHold=" + daysHold + '}';
}
public boolean hasDaysHold() {
return this.daysHold != null;
}
Even if my field "daysHold" is public I still need to create a getter to it so Spring can show the rejected error properly, otherwise, it will throw a NotReadablePropertyException. Is there a way to define that Spring can reject public fields without getters or I will need to add accessor methods to all fields I want to reject?
Here is the message that shows up when my validation is triggered.
org.springframework.beans.NotReadablePropertyException: Invalid property 'daysHold' of bean class [com.api.library.usecases.newhold.NewHoldRequest]: Bean property 'daysHold' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
I'll advise you to use the interface ConstraintValidator. It's a generic's interface, without casting and other problems.
You should create a custom constraint annotation for you it's #NewHoldValid after that implement CustomValidator interface:
#Component
public class NewHoldValidator implements ConstraintValidator<NewHoldValid, NewHoldRequest> {
#Override
public boolean isValid(NewHoldRequest value, ConstraintValidatorContext context) {
if(ANY_CONDITION){
return true;
}else {
return false;
}
}
After that use #NewHoldValid annotation above your NewHoldRequest.
If you'll want to set an error message text which different from default use that:
context.buildConstraintViolationWithTemplate("Your error message").addConstraintViolation();

Restrict acces in DTO class for fields and return different value

I'm implementing authorization in my Spring Boot application. And I want to apply authorization to dto's.
See the class below
public class ExampleDTO{
private String phoneNumber; // want to restrict acces on this field.
}
E.g.
User A with permission A can see phoneNumber 123456789
User B with permission B can see phoneNumber 123456***
A solutions can be something like this:
public void setPhoneNumber(String iban) {
if (Service.checkPermission("A")) {
this.phoneNumber= 123456789;
} else if (Service.checkPermission("B")) {
this.phoneNumber= "123456***";
} else {
this.phoneNumber= "*********";
}
}
But this is against the OOP principle.
Also I can do this in a service:
public ExampleDTO getExampleDto(String iban) {
if (checkPermission("A")) {
return ExampleDtoA;
} else if (checkPermission("B")) {
return ExampleDtoB;
} else {
return ExampleDtoC;
}
}
But this will allow code duplication;
So does one of you have a better solution?
One way to go is to use a builder to build ExampleDto. Something like this:
public class ExampleDTOBuilder {
public enum PhonePermission { A, B, NONE };
private String phoneNumber;
private PhonePermission phonePermission = PhonePermission.NONE;
public ExampleDTOBuilder setPhoneNumber( String phoneNumber ) {
this.phoneNumber = phoneNumber;
return this;
}
public ExampleDTOBuilder setPermission(PhonePermission phonePermission) {
this.phonePermission = phonePermission;
return this;
}
public ExampleDTO build() {
ExampleDTO dto = new ExampleDTO();
switch (this.phonePermission) {
case A:
dto.setPhoneNumber(this.phoneNumber);
break;
case B:
dto.setPhoneNumber(getMaskedPhoneNumber(this.phoneNumber));
default:
dto.setPhoneNumber("********");
break;
}
return dto;
}
private String getMaskedPhoneNumber(String phoneNumber) {
return "123***"; // do your masking
}
}
Use:
ExampleDTO dto = new ExampleDTOBuilder()
.setPermission(PhonePermission.A)
.setPhoneNumber("123456")
.build();
More details about the Builder pattern can be found here: https://en.wikipedia.org/wiki/Builder_pattern
I am assuming that you are saving these phone numbers in a database and that you want to persist the real phone numbers, but return different values based on permissions.
If this is the case, then you should not alter the setPhoneNumber method of the DTO as this could put the following phone number values into your DB: 123456***, *********.
You can encapsulate the logic in a converter between your entity and dto layer
public class ExampleService {
ExampleDto getExample(String iban) {
// Get entity from your DB
ExampleEntity entity = entityRepository.getEntity(iban);
return converter.convert(entity);
}
}
public class Converter {
public ExampleDto convert(ExampleEntity entity) {
ExampleDto dto = new ExampleDto();
if (checkPermission("A"))
dto.setPhoneNumber(entity.getPhoneNumber());
else if (checkPermission("B"))
dto.setPhoneNumber(entity.getPhoneNumberPartialMasked();
else
dto.setPhoneNumber(entity.getPhoneNumberMasked());
return dto;
}
}
If you are using the spring framework, then you can see the following classes: org.springframework.core.convert.converter.Converter and org.springframework.core.convert.ConversionService. There are lots of examples out there on how to use these.

How to remove try/catch repetitions in class methods?

I have RESTeasy service. And have implemented simple error handling on methods using try catch and feel something is not very well with it. I've noticed try catch repetition on all my methods. So I want ask way how to avoid repetition (to reduce code size) of try catch but not lost functionality.
#Path("/rest")
#Logged
#Produces("application/json")
public class CounterRestService {
#POST
#Path("/create")
public CounterResponce create(#QueryParam("name") String name) {
try {
CounterService.getInstance().put(name);
return new CounterResponce();
} catch (Exception e){
return new CounterResponce("error", e.getMessage());
}
}
#POST
#Path("/insert")
public CounterResponce create(Counter counter) {
try {
CounterService.getInstance().put(counter);
return new CounterResponce();
} catch (Exception e){
return new CounterResponce("error", e.getMessage());
}
}
#DELETE
#Path("/delete")
public CounterResponce delete(#QueryParam("name") String name) {
try {
CounterService.getInstance().remove(name);
return new CounterResponce();
} catch (Exception e){
return new CounterResponce("error", e.getMessage());
}
}
... // other methods with some try catch pattern
response
public class CounterResponce {
private String status;
#JsonSerialize(include=Inclusion.NON_NULL)
private Object data;
public CounterResponce() {
this.status = "ok";
}
public CounterResponce(Object o) {
this.status = "ok";
this.data = o;
}
public CounterResponce(String status, Object o){
this.status = status;
this.data = o;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
exceptions source
public class CounterService {
private Map<String, StatisticCounter> counters = new HashMap<String, StatisticCounter>();
private static CounterService instance = null;
protected CounterService() {}
public static CounterService getInstance() {
if(instance == null) {
instance = new CounterService();
}
return instance;
}
public StatisticCounter get(String name){
StatisticCounter c = counters.get(name);
if(c == null)throw new IllegalArgumentException("Counter "+name+" not exist");
return c;
}
public void put(String name){
if(name==null)throw new IllegalArgumentException("null can`t be as name");
if(counters.get(name)!=null)throw new IllegalArgumentException("Counter "+name+" exist");
counters.put(name, new Counter(name));
}...
The comments in your question are pointing you in a good direction. Since the answers do not mention it, I'll summarize the general idea in this answer.
Extending WebApplicationException
JAX-RS allows to define direct mapping of Java exceptions to HTTP error responses. By extending WebApplicationException, you can create application specific exceptions that build a HTTP response with the status code and an optional message as the body of the response.
The following exception builds a HTTP response with the 404 status code:
public class CustomerNotFoundException extends WebApplicationException {
/**
* Create a HTTP 404 (Not Found) exception.
*/
public CustomerNotFoundException() {
super(Responses.notFound().build());
}
/**
* Create a HTTP 404 (Not Found) exception.
* #param message the String that is the entity of the 404 response.
*/
public CustomerNotFoundException(String message) {
super(Response.status(Responses.NOT_FOUND).
entity(message).type("text/plain").build());
}
}
WebApplicationException is a RuntimeException and doesn't need to the wrapped in a try-catch block or be declared in a throws clause:
#Path("customers/{customerId}")
public Customer findCustomer(#PathParam("customerId") Long customerId) {
Customer customer = customerService.find(customerId);
if (customer == null) {
throw new CustomerNotFoundException("Customer not found with ID " + customerId);
}
return customer;
}
Creating ExceptionMappers
In other cases it may not be appropriate to throw instances of WebApplicationException, or classes that extend WebApplicationException, and instead it may be preferable to map an existing exception to a response.
For such cases it is possible to use a custom exception mapping provider. The provider must implement the ExceptionMapper<E extends Throwable> interface. For example, the following maps the JAP EntityNotFoundException to a HTTP 404 response:
#Provider
public class EntityNotFoundExceptionMapper
implements ExceptionMapper<EntityNotFoundException> {
#Override
public Response toResponse(EntityNotFoundException ex) {
return Response.status(404).entity(ex.getMessage()).type("text/plain").build();
}
}
When an EntityNotFoundException is thrown, the toResponse(E) method of the EntityNotFoundExceptionMapper instance will be invoked.
The #Provider annotation declares that the class is of interest to the JAX-RS runtime. Such class may be added to the set of classes of the Application instance that is configured.
Introduce a private method such as "apply" which can take function as parameter if you use Java 8. This method will have the error handling and/or mapping, response mapping and response generation code centralized.
From create and delete methods, invoke this apply method and pass the desired counter operation you wish to perform as a lambda expression.

Best way to structure with many validating methods

Here is my code
public class Validator {
private String message = "ok";
public String mainValidate(String value) {
if(!isAccept1()) {
message = "fail1";
return message;
}
if(!isAccept2()) {
message = "fail2";
return message;
}
if(!isAccept3()) {
message = "fail3";
return message;
}
return message;
}
public boolean isAccept1() {}
public boolean isAccept2() {}
public boolean isAccept3() {}
Requirement is: If the code meet any error, return message immediately.
As you can see, with current code, I repeat myself very much.
How can I structure the code and still keep requirement. If any error occur, the code skip other validate and return error message
Many thanks!
You can put all the checks in one method:
public String mainValidate(String value) {
String message = isAccept();
if(!message.equalsIgnoreCase("ok")) {
return message;
}
}
private String isAccept() {
String returnString = "ok";
//check1 - change returnString to whatever message if check fails
//check2 - change returnString to whatever message if check fails
//check3 - change returnString to whatever message if check fails
//etc...
return returnString;
}
You could store the actions / messages in a map and iterate over it:
private static final Map<Predicate<String>, String> VALIDATIONS = new LinkedHashMap<> ();
static {
VALIDATIONS.put(Validator::isAccept1, "fail1");
VALIDATIONS.put(Validator::isAccept2, "fail2");
//etc.
}
public String mainValidate(String value) {
for (Entry<Predicate<String>, String> v : VALIDATIONS.entrySet()) {
Predicate<String> validator = v.getKey();
String errorMsg = v.getValue();
if (!validator.test(value)) return errorMsg;
}
return "ok";
}
public static boolean isAccept1(String value) { return /* ... */; }
This is known as:
Design by Contract
or Precondition
or Method Argument Validation
See also A good Design-by-Contract library for Java?

Categories

Resources