How to implement JSR-303 for Enums - java

I'm building a Rest WS and to validate the request elements I'm using a JSR-303 BeanValidation, but there's a field type Enum.
EmploymentType.java
public enum EmploymentType {
EMPTY, FULL, PARTTIME, CONTRACT, CASUAL;
public static EmploymentType getDefaultEnum() {
return EMPTY;
}
}
and the class I'm using use to implement this:
Employment.java
public class Employment implements Serializable{
private static final long serialVersionUID = 1L;
#NotNull(message="employmentType does not accept null values")
private EmploymentType employmentType;
#Valid
#NotNull(message="orgData does not accept null values")
private OrgData orgData;
public Employment() {
employmentType = EmploymentType.getDefaultEnum();
orgData = new OrgData();
}
public EmploymentType getEmploymentType() {
return employmentType;
}
public void setEmploymentType(EmploymentType employmentType) {
this.employmentType = employmentType;
}
public OrgData getOrgData() {
return orgData;
}
public void setOrgData(OrgData orgData) {
this.orgData = orgData;
}
}
the implementation I developed only prevents the enum being a null object, is there a way to validate that the value of the enum is only within the range of declared values? (EMPTY, FULL, PARTTIME, CONTRACT, CASUAL)

I believe you have to do the validation of the valueOf OR the name of the enum
Here is the excerpt what will make the validation happen
public class Employment implements Serializable {
#NotNull(message = "employmentType does not accept null values")
#Valid
private EmploymentType employmentType;
public EmploymentType getEmploymentType() {
getEmploymentTypeOfEnum();
return employmentType;
}
#Pattern(regexp = "EMPTY|FULL")
private String getEmploymentTypeOfEnum(){ // you don't need it to be public
return employmentType.name();
}
}

Related

Spring annotated controller works, but router/handler approach does not appear to retrieve *Mono<>* from *ServerRequest*

Still playing around and trying to understand the "how" of Spring's Webflux and Reactor.
The following successfully adds a new DemoPOJO to the repo when the annotated controller is used (i.e., POST issued at //localhost:8080/v1/DemoPOJO).
However, when issuing the same POST using the router/handler implementation (i.e., //localhost:8080/v2/DemoPOJO), request.bodyToMono(DemoPOJO.class) does not appear to retrieve the DemoPOJO instance from the ServerRequest (i.e., DemoPOJO.printme() is not being invoked).
I'm "working on this", but thought I'd see if anyone can help me "get there faster". For-what-it's-worth, the router/handler implementations (i.e., GET) that don't require getting a DemoPOJO out of ServerRequest are working.
RESTful endpoints using annotation...
#RestController
public class DemoPOJOController {
private Logger logger = LoggerFactory.getLogger(DemoPOJOHandler.class);
#Autowired
DemoPOJOService service;
#RequestMapping(method = POST, value = "/v1/DemoPOJO")
public Mono<Boolean> addDemoPOJO(#RequestBody DemoPOJO demoPOJO) {
logger.debug("DemoPOJOController.addDemoPOJO( {} )", demoPOJO.getId());
return service.add(demoPOJO);
}
}
"Router" part of the corresponding router/handler implementation...
#Configuration
public class DemoPOJORouter {
private Logger logger = LoggerFactory.getLogger(DemoPOJOHandler.class);
#Bean
public RouterFunction<ServerResponse> route(DemoPOJOHandler requestHandler) {
logger.debug("DemoPOJORouter.route( DemoPOJOHandler )");
return nest(path("/v2"),
nest(accept(APPLICATION_JSON),
RouterFunctions.route(RequestPredicates.POST("/DemoPOJO"), requestHandler::add)));
}
}
"Handler" part of the router/handler implementation...
#Component
public class DemoPOJOHandler {
public static final String PATH_VAR_ID = "id";
private Logger logger = LoggerFactory.getLogger(DemoPOJOHandler.class);
#Autowired
private DemoPOJOService service;
public Mono<ServerResponse> add(ServerRequest request) {
logger.debug("DemoPOJOHandler.add( ServerRequest )");
request.bodyToMono(DemoPOJO.class).doOnSuccess(DemoPOJO::printMe);
return ServerResponse.ok().build();
}
}
DemoPOJORepo implementation (hoping to simplify my learning experience by avoiding a "real" repository)...
#Component
public class DemoPOJORepo {
private static final int NUM_OBJS = 15;
private Logger logger = LoggerFactory.getLogger(DemoPOJORepo.class);
private static DemoPOJORepo demoRepo = null;
private Map<Integer, DemoPOJO> demoPOJOMap;
private DemoPOJORepo() {
logger.debug("DemoPOJORepo.DemoPOJORepo()");
initMap();
}
public boolean add(DemoPOJO demoPOJO) {
logger.debug("DemoPOJORepo.add( DemoPOJO )");
boolean pojoAdded = false;
if (!demoPOJOMap.containsKey(demoPOJO.getId())) {
logger.debug("DemoPOJORepo.add( DemoPOJO ) -> adding for id {}", demoPOJO.getId());
demoPOJOMap.put(demoPOJO.getId(), demoPOJO);
pojoAdded = true;
}
return pojoAdded;
}
private void initMap() {
logger.debug("DemoPOJORepo.initMap()");
demoPOJOMap = new TreeMap<Integer, DemoPOJO>();
for (int ndx = 1; ndx < (NUM_OBJS + 1); ndx++) {
demoPOJOMap.put(ndx, new DemoPOJO(ndx, "foo_" + ndx, ndx + 100));
}
}
}
The objects being manipulated...
public class DemoPOJO {
private Logger logger = LoggerFactory.getLogger(DemoPOJOHandler.class);
public static final String DEF_NAME = "DEFAULT NAME";
public static final int DEF_VALUE = 99;
private int id;
private String name;
private int value;
public DemoPOJO(int id) {
this(id, DEF_NAME, DEF_VALUE);
}
public DemoPOJO(#JsonProperty("id") int id, #JsonProperty("name") String name, #JsonProperty("value") int value) {
logger.debug("DemoPOJO.DemoPOJO( {}, {}, {} )", id, name, value);
this.id = id;
this.name = name;
this.value = value;
}
// getters and setters go here
public void printMe() {
logger.debug("DemoPOJO.printMe()");
System.out.printf("id->%d, name->%s, value->%d%n", id, name, value);
}
}
i am guesstimating here since i am writing from mobile. But i think this is your problem.
request.bodyToMono(DemoPOJO.class).doOnSuccess(DemoPOJO::printMe);
return ServerResponse.ok().build();
You are thinking imperative, that first row will be executed then the second which is not the case in webflux. You have to think events-callbacks.
return request.bodyToMono(DemoPOJO.class)
.doOnSuccess(DemoPOJO::printMe)
.thenReturn(ServerResponse.ok().build());
I think this is it but i could be wrong.

Why does IntelliJ tell me that my class never returns null?

private void addCompoundsFrom(Verse verse) {
Optional<List<Compound>> compounds = Optional.of(verse.getCompounds());
if (compounds.isPresent()) {
for (Compound compound : compounds.get()) {
addCompoundsFrom(compound);
}
}
}
The IntelliJ inspector tells me that the if-statement is always true. How can it know that? This is the Compounds class:
public class Compounds extends PositionalIds {
#XmlElement(name = "verse")
private List<Verse> verses;
public List<Verse> getVerses() {
return verses;
}
}
#XmlTransient
public abstract class PositionalIds {
#XmlAttribute(name = "start")
private String startId;
#XmlAttribute(name = "end")
private String endId;
public String getStartId() {
return startId;
}
public String getEndId() {
return endId;
}
}
And the Verse class:
public class Verse extends PositionalIds {
#XmlElement(name = "compound")
private List<Compound> compounds;
#XmlAttribute(name = "notation")
private String notation;
public List<Compound> getCompounds() {
return compounds;
}
public String getNotation() {
return notation;
}
}
If I stop using Optional to wrap the verse.getCompounds() result and instead just do a null check, the inspection message goes away.
I'm using Java 8.
The Optional class has two methods:
Optional.of -> throws exception if the parameter is null
Optional.ofNullable -> creates an empty optional if the parameter is null
Hence if your method returns null, the of() method will throw an exception and empty optional will never reach your if statement
Optional.of(verse.getCompounds()); returns an Optional that contains a valid value.
The isPresent check that follows will always be true because the Optional compounds will never not have a value, since you just set it to a valid value on the line above.

Field validation from options on condition from other field javax.validation

I need to perform a field validation (it can be one of values) if another field is present.
import javax.validation.*;
class Person {
#NotBlank
private String name;
private Long groupId;
#Valid // if group id is not null, select one from available.
private String specialization;
// getters, setters.
}
class PersonValidaionLogic {
#Autowired
private SpecializationService specializationService;
public void validatePerson(final Person person) {
Long groupId = person.getGroupId();
if (groupId != null) {
Set<String> availableSpecializations = specializationService.getByGroupId(groupId);
if (!availableSpecializations.contains(specialization)) {
addValidationError("specialization is not valid");
}
}
}
}
There is a nice answer on how to validate multiple fields in a class with conditions on each other.
How do I pass specializationService and groupId to the validator.
Feel free to share your solution or ideas! This is how I solved this problem.
I used the idea from the link in my question, but in much easier way.
First, I solved a problem how to pass a Spring component or service into validator. I used a component which holds a static reference to the service.
Second, I validated the whole object as described in the link.
Here is the code!
1) Create annotation #PersonConstraint and put in on Person class.
This may help https://www.baeldung.com/javax-validation-method-constraints
#Target({ TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = PersonValidator.class)
public #interface PersonConstraint {
String message() default "Specialization is not valid";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
CaseMode value();
}
2) Component which holds static reference to the service.
#Component // Spring component.
class ServiceHolderComponent {
private static SpecializationService SPECIALIZATION_SERVICE;
#Autowired
public ServiceHolderComponent(final SpecializationService specializationService) {
GROUP_SERVICE = Validate.notNull(groupService); //apache lib
}
public static SpecializationService getSpecializationService() {
return SPECIALIZATION_SERVICE;
}
}
3) And person validator
public class PersonValidator implements ConstraintValidator<PersonConstraint, Person> {
private final SpecializationService specializationService;
public UserDynamicEnumValidator() {
this(ServiceHolderComponent.getSpecializationService());
}
public UserDynamicEnumValidator(final SpecializationService specializationService) {
this.specializationService = specializationService;
}
#Override
public boolean isValid(final Person person, final ConstraintValidatorContext context) {
final Long groupId = person.getGroupId();
if (groupId == null) {
return true; // We consider it valid.
}
final String specialization = person.getSpecializat();
if (StringUtils.isEmpty(specialization)) {
return true; // We consider it valid.
}
// I changed the code of the service, so it returns a set of strings - projection query and collectors to set.
final Set<String> availableSpecializationValuesByGroup = specializationService.findByValue(groupId);
if (!availableSpecializationValuesByGroup.contains(specialization)) {
// To display custom message
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("Specialization is not valid").addConstraintViolation();
return false;
}
return true;
}
}
To display a custom message in validator check this

How to force Jackson to invoke Set methods of all attributes in object (dto) in controller?

I have some validations in my "command", parameter of the controller that are executed within the set of each attribute, the problem is that the attributes not informed, jackson does not invoke the set method to do the validation. Is it possible to force Jackson to invoke the Set method even when the attribute is missing?
For exemple Payload without agency field:
{
"bank": "237",
"account": "20772-1",
"taxId": "36456155800",
"paidAmount": 30.00
}
My Controller:
public Return confirmTransfer(#RequestBody RechargeTransferConfirmationCommand command) {
System.out.println(command);
}
Class Java:
public class RechargeTransferConfirmationCommand {
public static final String ERR_INVALID_AGENCY = "Agency number can not be null.";
private String bank;
private String account;
private String agency;
private String taxId;
public RechargeTransferConfirmationCommand(BigDecimal paidAmount, String bank, String account,
String agency, String taxId) {
setPaidAmount(paidAmount);
setBank(bank);
setAccount(account);
setAgency(agency);
setTaxId(taxId);
}
public void setRechargeId(RechargeId rechargeId) {
assertArgumentNotNull(rechargeId, Recharge.ERR_RECHARGE_ID_INVALID);
this.rechargeId = rechargeId;
}
private void setPaidAmount(BigDecimal paidAmount) {
if (paidAmount == null || paidAmount.compareTo(BigDecimal.ZERO) < 0)
throw new IllegalArgumentException(Recharge.ERR_INVALID_AMOUNT);
this.paidAmount = paidAmount;
}
private void setBank(String bank) {
assertArgumentNotEmpty(bank, TransferInformation.ERR_INVALID_BANK_NUMBER);
this.bank = bank;
}
private void setAccount(String account) {
assertArgumentNotEmpty(account, TransferInformation.ERR_INVALID_ACCOUNT);
this.account = account;
}
private void setAgency(String agency) {
assertArgumentNotEmpty(agency, ERR_INVALID_AGENCY);
this.agency = agency;
}
private void setTaxId(String taxId) {
assertArgumentNotEmpty(taxId, ERR_INVALID_TAX_ID);
this.taxId = taxId;
}
}
In this case, for each field the set method is invoked to do the validation, except the agency field that was not informed in the payload, it should soon throw the exception contained in the method assertArgumentNotEmpty.
yes jackson will not invoke the setter methods of fields that are not passed in payload, if you want to validate missing fields you need custom Deserializer
class RechargeTransferConfirmationCommandDeserializer extends JsonDeserializer<RechargeTransferConfirmationCommand> {
#Override
public RechargeTransferConfirmationCommand deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
RechargeTransferConfirmationCommand responseModel = jp.readValueAs(RechargeTransferConfirmationCommand.class);
RechargeTransferConfirmationCommand finalModel = new RechargeTransferConfirmationCommand();
finalModel.set agency(responseModel. agency == null || age.trim().isEmpty() ? "agency" : responseModel.agency);
return finalModel;
}
}
And you model should be
#JsonDeserialize(using = DefaultModelDeserializer.class)
public class DefaultModel {
...
}
use #JsonSetter in method && declare no args constructor also:
public RechargeTransferConfirmationCommand(){
}
#JsonSetter
public void setRechargeId(RechargeId rechargeId) {
assertArgumentNotNull(rechargeId, Recharge.ERR_RECHARGE_ID_INVALID);
this.rechargeId = rechargeId;
}
But better you use javax validation. Look at this to get details.
Another way:
You can write all validtion in one method and use #AssertTrue:
#AssertTrue
public boolean isValid(){
//if all field is valid then return true;
return false;
}
problem solved using #JsonCreator, thank you all for your help
#JsonCreator
public RechargeTransferConfirmationCommand(#JsonProperty("paidAmount") BigDecimal paidAmount,
#JsonProperty("bank") String bank, #JsonProperty("account") String account,
#JsonProperty("agency") String agency, #JsonProperty("taxId") String taxId) {
setPaidAmount(paidAmount);
setBank(bank);
setAccount(account);
setAgency(agency);
setTaxId(taxId);
}

Map selected fields from multiple POJOs to create one POJO

I have a couple of objects from which selected members should be combined to create an output object. All these are POJOs. I am seeing that all object mappers work on a single POJO to another POJO level. Is there any mapper that supports what I am looking for? Of course, I understand that there is some mapping stuff that I need to specify.
Edit:
I know how to get this done by writings own Java class. I am just looking for a way to do it with one of the mapping libraries.
You aren't limited in what you require to be passed to your mapper. You can define it to accept several items and build the object based on the multiple inputs. Here is an example:
public class ClassOne {
private final String someProperty;
public ClassOne(String someProperty) {
this.someProperty = someProperty;
}
public String getSomeProperty() {
return someProperty;
}
}
public class ClassTwo {
private final String someOtherProperty;
public ClassTwo(String someOtherProperty) {
this.someOtherProperty = someOtherProperty;
}
public String getSomeOtherProperty() {
return someOtherProperty;
}
}
public class CombinedClass {
public static CombinedClass mapper(ClassOne one, ClassTwo two){
return new CombinedClass(one.getSomeProperty(), two.getSomeOtherProperty());
}
private final String someProperty;
private final String someOtherProperty;
private CombinedClass(String someProperty, String someOtherProperty) {
this.someProperty = someProperty;
this.someOtherProperty = someOtherProperty;
}
public String getSomeProperty() {
return someProperty;
}
public String getSomeOtherProperty() {
return someOtherProperty;
}
}

Categories

Resources