How to define general enumeration in annotation? - spring

I have an interface
public interface ErrorCode {
String getMessage ();
String getCode ();
}
and tow enum
public enum BaseCode implements ErrorCode {
.........
}
public enum BusinessCode implements ErrorCode {
.........
}
i want create a annotation
#Documented
#Retention(RUNTIME)
#Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
public #interface Digits {
ErrorCode baseCode();
}
How should I define a generic, containing class that implements ErrorCode the interface

Related

enforce enum serialization with a spring jpa projection?

Say I have the following JPA entity, with an enum field ON/OFF mapped to an SQL enum("on", "off").
#Entity
public class Process {
#Id
private Long Id;
#Convert(converter = StatusConverter.class)
private Status status;
// getter/setter omitted
}
public enum Status {
ON("on"),
OFF("off");
private final String status;
Status(String status) {
this.status = status;
}
// JSON (de)serialization
#JsonCreator
public static Status decode(String status) {
return valueOf(status.toUpperCase());
}
#JsonValue
public getStatus() {
return status;
}
// DAO layer conversion
public String toDatabaseColumn() {
return this.name().toLowerCase();
}
}
#Converter
public class StatusConverter implements AttributeConverter<Status, String> {
#Override
public String convertToDatabaseColumn(Status attribute) {
return attribute.toDatabaseColumn();
}
#Override
public Status convertToEntityAttribute(String dbData) {
return Status.decode(dbData);
}
}
// Spring JPA projection
public interface ProcessSummary {
String getStatus();
}
// a minimalist JPA repository
public interface ProcessRepository extends Repository<Process, Long> {
<T> T findById(Long id, Class<T> type;
}
If I use repository.findById(1L, Process.class) in a REST controller, both the DAO layer conversion and the JSON serialization work as expected :
my database record has its status set to on
it is mapped to the Java Status.ON
the entity is serialized as
{
"status" : "on"
}
But if I use repository.findById(1L, ProcessSummary.class) instead, the entity is serialized as
{
"status" : "ON"
}
How can I get the same result when using a projection as target type? Is it possible with a projection, or should I try something else (a DTO class maybe)?
Sorry folks, it was just me and a textbook case of of PEBKAC :)
The getStatus() method in the interface MUST return a Status, not a String.
public interface ProcessSummary {
String getStatus();
}
does what it's asked: converts the enum to a String, hence Status.ON is serialized as "ON", while
public interface ProcessSummary {
Status getStatus();
}
indeed uses the #JsonValue annotated method and serializes Status.ON as "on".

How do I validate a #QueryParam?

I've got a simple REST resource which accepts a couple of query parameters. I'd like to validate one of these parameters, and came across ConstraintValidator for this purpose. The REST resource expects the query param territoryId to be a UUID, so I'd like to validate that it indeed is a valid UUID.
I've created an #IsValidUUID annotation, and a corresponding IsValidUUIDValidator (which is a ConstraintValidator). With what I have now, nothing gets validated and getSuggestions accepts anything I throw at it. So clearly I'm doing something wrong.
What am I doing wrong?
The REST resource now looks like this :
#Component
#Path("/search")
public class SearchResource extends AbstractResource {
#GET
#Path("/suggestions")
#Produces(MediaType.APPLICATION_XML)
public Response getSuggestions(
#QueryParam("phrase") List<String> phrases,
#IsValidUUID #QueryParam("territoryId") String territoryId) {
[...]
}
}
IsValidUUID
#Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy = {IsValidUUIDValidator.class})
public #interface IsValidUUID {
String message() default "Invalid UUID";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
IsValidUUIDValidator
public class IsValidUUIDValidator implements ConstraintValidator<IsValidUUID, String> {
#Override
public void initialize(IsValidUUID constraintAnnotation) {
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
try {
UUID.fromString(value);
return true;
} catch (Exception e) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("The provided UUID is not valid")
.addConstraintViolation();
return false;
}
}
}
You need to set the supported targets on IsValidUUID, using the following annotation.
#SupportedValidationTarget(ValidationTarget.ANNOTATED_ELEMENT)
or
#SupportedValidationTarget(ValidationTarget.PARAMETERS)
Edit:
Sorry, I wasn't able to make it work either on a RequestParam directly. However, if you can, try creating a POJO that you can bind your request parameters to and annotate the binding field with your constraint instead. This worked for me.
public class MyModel {
#IsValidUUID
private String territoryId;
public String getTerritoryId() {
return territoryId;
}
public void setTerritoryId(String territoryId) {
this.territoryId = territoryId;
}
}
#GET
#Path("/suggestions")
#Produces(MediaType.APPLICATION_XML)
public Response getSuggestions(
#QueryParam("phrase") List<String> phrases,
#Valid #ModelAttribute MyModel myModel) {
[...]
}

Spring MVC Validation for list and reporting the invalid value

I have a list of strings which should be of a specific format. I need to return the error message with the strings which are not of the format specified. How to do this with spring validation(I am using the hibernate validator).
The annotation:
#Documented
#Retention(RUNTIME)
#Target({FIELD, METHOD})
#Constraint(validatedBy = HostsValidator.class)
public #interface HostsConstraint {
String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
The implementation:
public class HostsValidator implements ConstraintValidator<HostsConstraint, List<String>>{
#Override
public void initialize(OriginHostsConstraint constraintAnnotation) {
}
#Override
public boolean isValid(List<String> strings, ConstraintValidatorContext context) {
for (String s : strings) {
if (!s.matches("[0-9]+") {
//How do I say: Invalid string <s> ?
return false;
}
}
}
}
The usage:
public class Test {
#HostsConstraint(message="Invalid string ")
private List<String> hosts;
}
Using validatedValue will give the entire list.
Use JSR 380 validation, it allows container element constraints.
Here is a link to the container element section in the Hibernate Validator 6.0.6.FINAL Document
I think I found a solution but it is coupled to hibernate validator. May be it is even a hacky implementation.
The usage:
public class Test {
#HostsConstraint(message="Invalid string : ${invalidStr}")
private List<String> hosts;
}
The implementation
public class HostsValidator implements ConstraintValidator<HostsConstraint, List<String>>{
#Override
public void initialize(OriginHostsConstraint constraintAnnotation) {}
#Override
public boolean isValid(List<String> strings, ConstraintValidatorContext context) {
for (String s : strings) {
if (!s.matches("[0-9]+") {
ConstraintValidatorContextImpl contextImpl =
(ConstraintValidatorContextImpl) context
.unwrap(HibernateConstraintValidatorContext.class);
contextImpl.addExpressionVariable("invalidStr", s);
return false;
}
}
}
}

Jackson deserialization errorhandling in spring-framework

I'm looking for a clean way to handle Jackson Deserialization errors for REST web requests.
More precisely: I have an Enum in a incoming DTO object, mapped from JSON. But if the user sends a wrong value, a 400 Bad Request is returned. I would like to return a 422 Unprocessable Entity with a correct message.
One option would be to accept a String, and use bean validation. However, it's not possible to pass all enum values as a list to the annotation (not a constant), so I would need to pass all enum values separately and keep them up to date. This will be very error prone over the whole application. I'm looking for a more structural way to handle this.
I solved this by using a String in the DTO and using a public #interface EnumValueas annotation.
The EnumValue:
#ReportAsSingleViolation
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = EnumValueValidator.class)
#Target(ElementType.FIELD)
public #interface EnumValue {
Class<? extends Enum> value();
String message() default "The input contains validation errors.";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
The validator:
public class EnumValueValidator implements ConstraintValidator<EnumValue, String> {
private Class<? extends Enum> enumClass;
private String message;
#Override
public void initialize(final EnumValue constraintAnnotation) {
this.enumClass = constraintAnnotation.value();
this.message = constraintAnnotation.message();
}
#Override
public boolean isValid(final String value, final ConstraintValidatorContext context) {
boolean valid = false;
for (final Enum enumValue : enumClass.getEnumConstants()) {
if (enumValue.name().equals(value)) {
valid = true;
}
}
if (!valid) {
context.buildConstraintViolationWithTemplate(message) //
.addConstraintViolation();
}
return valid;
}
}

Inherited methods in Spring-Data-Neo4j repository interfaces not working

I have an abstract domain class containing a uid field, looking as below:
public abstract class GraphEntityWithUid extends GraphEntity {
private String uid = CommonUtils.newUid();
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
}
And, an abstract repository for it:
public abstract interface GraphEntityWithUidRepository<T extends GraphEntityWithUid> extends GraphRepository<T> {
public T findByUid(String uid);
}
I have a concrete domain class that inherits the uid, looking as below:
#NodeEntity
public class Attachment extends GraphEntityWithUid {
...
}
And, its repository looks as below:
public interface AttachmentRepository extends GraphEntityWithUidRepository<Attachment> {
}
Now, when I use the findByUid method as below:
// returns null
attachmentRepository.findByUid(uid);
it always returns null. However, if I re-declare the method in the AttachmentRepository as below, it works properly:
public interface AttachmentRepository extends GraphEntityWithUidRepository<Attachment> {
// Shouldn't this be automatically inherited??
public Attachment findByUid(String uid);
}
Why should I need to re-declare findByUid method in AttachmentRepository? Shouldn't it be automatically inherited from GraphEntityWithUidRepository?

Resources