Reputation: 11
I have three projects:
1- common-lib, Written with Java 11. It doesn't use Spring, but contains shared classes, error codes, models, DTOs, etc., including custom validation annotations (see below).
2- x service, Written with Java 11, using Spring 2.6.
3- y service, Written with Java 17, using Spring 3.2.
Problem: My Java 11 services (x service) can use common-lib easily, including the validation annotations. However, my Java 17 service (y service) can use the models, error codes, and other common classes from common-lib, but cannot use the validation annotations.
The issue appears to be related to the transition from javax.validation to jakarta.validation and other potential bytecode issues.
@Target({TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = IsValidSms.SmsValidator.class)
public @interface IsValidSms {
String message() default "INVALID_REQUEST_BODY";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
class SmsValidator implements ConstraintValidator<IsValidSms, SmsRequest> {
public boolean isValid(SmsRequest smsRequest, ConstraintValidatorContext constraintContext) {
final String smsType = smsRequest.getSmsType();
if (Objects.isNull(smsType) || smsType.isBlank()) {
return Objects.nonNull(smsRequest.getContent());
}
return isValidPlaceholders(smsRequest.getPlaceholders());
}
private boolean isValidPlaceholders(Map<String, String> placeholders) {
if (Objects.isNull(placeholders) || placeholders.isEmpty()) {
return true;
}
return placeholders.keySet().stream().allMatch(key -> key.startsWith("{{") && key.endsWith("}}"));
}
}
}
Context: common-lib is in Java 11 and uses javax.validation annotations. y service is in Java 17 and uses Spring 3.2, which is built on jakarta.validation. The Java 17 service can’t resolve the javax.validation annotations from the common-lib due to changes in the bytecode and the switch from javax to jakarta.
Question: How can I resolve this issue and make the common-lib (Java 11, javax.validation) work with the Java 17 service (which uses Spring 3.2 and jakarta.validation)?
What I've tried: Including jakarta.validation dependencies in the y service, but that doesn't resolve the issue. Adding javax.validation dependency manually to the y service. I'm looking for a solution that allows me to use the validation annotations in the y service without changing the common-lib project.
common-lib ->
dependencies {
testImplementation 'org.mockito:mockito-core:3.5.9'
testImplementation 'org.mockito:mockito-junit-jupiter:3.5.9'
compileOnly 'org.projectlombok:lombok'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.11.3'
implementation group: 'org.hibernate.validator', name: 'hibernate-validator', version: '6.1.6.Final'
implementation 'commons-validator:commons-validator:1.7'
testImplementation group: 'org.glassfish', name: 'javax.el', version: '3.0.0'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.5.2'
implementation 'org.springframework:spring-beans:5.3.20'
}
solution suggestion.
Upvotes: 1
Views: 77
Reputation: 44328
You have to make two annotation types, which will be identical except for their import
statements. The good news is, they can coexist without any problems:
import java.util.Map;
import java.util.Objects;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
@Target({TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = IsValidSmsLegacy.SmsValidator.class)
public @interface IsValidSmsLegacy {
// ...
}
And:
import java.util.Map;
import java.util.Objects;
import jakarta.validation.Constraint;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.Payload;
@Target({TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = IsValidSms.SmsValidator.class)
public @interface IsValidSms {
// ...
}
And of course, in your code, you’ll have things like:
@IsValidSms
@IsValidSmsLegacy
public class Message {
// ...
}
Side note: Objects.isNull is not meant to be a universal replacement for the != null
check. Objects.isNull is intended for cases where code needs a method reference; for example:
public void process(Collection<String> values) {
Objects.requireNonNull(values, "Value set cannot be null.");
if (values.stream().anyMatch(Objects::isNull)) {
throw new IllegalArgumentException(
"Value set cannot contain null elements.");
}
// ...
}
Upvotes: 1