カスタム宣言の作成
interface FromTo {
@Nullable ZonedDateTime getFrom();
ZonedDateTime getTo();
}
@Documented
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(FromToIntervalCheck.List.class)
@Constraint(validatedBy=FromToIntervalCheckValidator.class)
public @interface FromToIntervalCheck {
long minutes() default 10L;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String message() default "{bus.loona.FromToIntervalCheck.message}";
@Documented
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface List { FromToIntervalCheck[] value(); }
}
public class FromToIntervalCheckValidator implements ConstraintValidator<FromToIntervalCheck, FromTo> {
private Long minutes = 60L;
private final MessageSourceAccessor accessor;
@Autowired
public FromToIntervalCheckValidator(final MessageSourceAccessor accessor) {
this.accessor = accessor;
}
@Override
public void initialize(final FromToIntervalCheck annotation) {
this.minutes = annotation.minutes();
}
@Override
public boolean isValid(final BaseReq.FromTo req, final ConstraintValidatorContext context) {
final ZonedDateTime from = req.getFrom();
final ZonedDateTime to = req.getTo();
if (Objects.isNull(from)) {
return true;
}
if (TimeUnit.MINUTES.toSeconds(minutes) <= Duration.between(from, to).getSeconds()) {
return true;
}
final String messageTemplate = context.getDefaultConstraintMessageTemplate();
final String code = messageTemplate.substring(1, messageTemplate.length()-1);
final String message = accessor.getMessage(code, new Object[]{minutes, from, to}, LocaleContextHolder.getLocale());
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message).addConstraintViolation();
return false;
}
}
@Documented
@Repeatable(IpCheck.List.class)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=IpCheckValidator.class)
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
public @interface IpCheck {
String desc() default "";
boolean nullable() default false;
boolean receiveIpv6() default false;
boolean receiveIpv4Reserved() default false;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String message() default "{bus.loona.IpCheck.message}";
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@interface List { IpCheck[] value(); }
}
final class IpCheckPattern {
private static final String EXPR;
static final Pattern EXPR_IPV4;
static final Pattern EXPR_IPV6;
static final List<Pattern> EXPR_IPV4_RESERVED;
static {
EXPR = "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
// https://www.owasp.org/index.php/OWASP_Validation_Regex_Repository
EXPR_IPV4 = Pattern.compile("^" + EXPR + "\\." + EXPR + "\\." + EXPR + "\\." + EXPR + "$");
/*
fe80:0000:0000:0000:0204:61ff:fe9d:f156 // full form of Ipv6
fe80:0:0:0:204:61ff:fe9d:f156 // drop leading zeroes
fe80::204:61ff:fe9d:f156 // collapse multiple zeroes to :: in the Ipv6 address
fe80:0000:0000:0000:0204:61ff:254.157.241.86 // Ipv4 dotted quad at the end
fe80:0:0:0:0204:61ff:254.157.241.86 // drop leading zeroes, Ipv4 dotted quad at the end
fe80::204:61ff:254.157.241.86 // dotted quad at the end, multiple zeroes collapsed
*/
// https://community.helpsystems.com/forums/intermapper/miscellaneous-topics/5acc4fcf-fa83-e511-80cf-0050568460e4
EXPR_IPV6 = Pattern.compile("^(((?=(?>.*?::)(?!.*::)))(::)" +
"?([0-9a-fA-F]{1,4}::?){0,5}|([0-9a-fA-F]{1,4}:){6})" +
"(\\2([0-9a-fA-F]{1,4}(::?|$)){0,2}|((25[0-5]|(2[0-4]|1\\d|[1-9])" +
"?\\d)(\\.|$)){4}|[0-9a-fA-F]{1,4}:[0-9a-fA-F]{1,4})(?<![^:]:|\\.)\\z");
// https://en.wikipedia.org/wiki/Reserved_IP_addresses#cite_note-1
EXPR_IPV4_RESERVED = ImmutableList.<Pattern>builder()
// 0.0.0.0 – 0.255.255.255 / 10.0.0.0 – 10.255.255.255 / 127.0.0.0 – 127.255.255.255
.add(Pattern.compile("^" + "(0|10|127)" + "\\." + EXPR + "\\." + EXPR + "\\." + EXPR + "$"))
// 100.64.0.0 – 100.127.255.255
.add(Pattern.compile("^" + "100" + "\\." + "(6[4-9]|[789][0-9]|1[01][0-9]|12[0-7])" + "\\." + EXPR + "\\." + EXPR + "$"))
// 169.254.0.0 – 169.254.255.255
.add(Pattern.compile("^" + "169" + "\\." + "254" + "\\." + EXPR + "\\." + EXPR + "$"))
// 172.16.0.0 – 172.31.255.255
.add(Pattern.compile("^" + "172" + "\\." + "(1[6-9]|2[0-9]|3[01])" + "\\." + EXPR + "\\." + EXPR + "$"))
// 192.0.0.0 – 192.0.0.255 / 192.0.2.0 – 192.0.2.255
.add(Pattern.compile("^" + "192" + "\\." + "0" + "\\." + "([02])" + "\\." + EXPR + "$"))
// 192.88.99.0 – 192.88.99.255
.add(Pattern.compile("^" + "192" + "\\." + "88" + "\\." + "99" + "\\." + EXPR + "$"))
// 192.168.0.0 – 192.168.255.255
.add(Pattern.compile("^" + "192" + "\\." + "168" + "\\." + EXPR + "\\." + EXPR + "$"))
// 198.18.0.0 – 198.19.255.255
.add(Pattern.compile("^" + "198" + "\\." + "(18|19)" + "\\." + EXPR + "\\." + EXPR + "$"))
// 198.51.100.0 – 198.51.100.255
.add(Pattern.compile("^" + "198" + "\\." + "51" + "\\." + "100" + "\\." + EXPR + "$"))
// 203.0.113.0 – 203.0.113.255
.add(Pattern.compile("^" + "203" + "\\." + "0" + "\\." + "113" + "\\." + EXPR + "$"))
// 224.0.0.0 – 239.255.255.255 / 240.0.0.0 – 255.255.255.254 / 255.255.255.255
.add(Pattern.compile("^(22[4-9]|2[34][0-9]|25[0-5])" + "\\." + EXPR + "\\." + EXPR + "\\." + EXPR + "$")).build();
}
private IpCheckPattern() {
throw new UnsupportedOperationException(Constants.UNSUPPORTED_OPERATION_EXCEPTION_MESSAGE);
}
}
public class IpCheckValidator implements ConstraintValidator<IpCheck, String> {
private boolean nullable = false;
private boolean receiveIpv6 = false;
private boolean receiveIpv4Reserved = false;
@Override
public void initialize(final IpCheck annotation) {
this.nullable = annotation.nullable();
this.receiveIpv6 = annotation.receiveIpv6();
this.receiveIpv4Reserved = annotation.receiveIpv4Reserved();
}
@Override
public boolean isValid(@Nullable final String value, final ConstraintValidatorContext context) {
return Objects.isNull(value) ? nullable : checkIpv6(value, checkIpv4Reserved(value, checkIpv4(value)));
}
private boolean checkIpv4(final String source) {
return IpCheckPattern.EXPR_IPV4.matcher(source).matches();
}
private boolean checkIpv4Reserved(final String source, final boolean checked) {
return receiveIpv4Reserved ? checked : checked && IpCheckPattern.EXPR_IPV4_RESERVED.stream().noneMatch(p -> p.matcher(source).matches());
}
private boolean checkIpv6(final String source, final boolean checked) {
return receiveIpv6 ? checked || IpCheckPattern.EXPR_IPV6.matcher(source).matches() : checked;
}
}
Reference
この問題について(カスタム宣言の作成), 我々は、より多くの情報をここで見つけました https://velog.io/@looniverse/커스텀-어노테이션-작성テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol