What is the best implementation of custom extension point - java

I need to make an extension point for custom validation rules in java. Can you help me to find the best way to do it?
My code looks like this
...
public myClass() {
...
//some business logic
customValidator.validate(event); //custom validator
...
}
I know that it is suitable to use base Validator interface and implement it with custom validators.
interface Validator {
public static Boolean validate();
}
public class TrueValidator implements Validator {
public static Boolean validate() { return true;}
}
public class TrueValidator implements Validator {
public static Boolean validate() { return false;}
}
I want to know what is the best pattern of calling validation depending on some String variable. Is it okay just to get class with Reflection API? I will get my String from database, create Class and create instance of it.
The other solution I know is to make Validator factory and get Validator by String variable, but I think that it is too excessive.
Can you recommend me somethink?

You basically want to instantiate a validator based on a string? There are multiple possible implementations and I can't tell which one is best, but the implementation you choose should be abstracted away in a factory. You do not want your code littered with logic to find and instantiate those validators from strings.
E.g.
public interface Validator {
public boolean validate(ThingToValidate thingToValidate);
}
public final class TrueValidator implements Validator {
//Note that the method is not static
public boolean validate(ThingToValidate thingToValidate) {
return true;
}
}
public interface ValidatorFactory {
public Validator createFromType(String type);
}
//Concrete validation factory using reflection
public final class ReflectiveValidatorFactory implements ValidatorFactory {
public Validator createFromType(String type) {
/*Use reflection to resolve the class based on the interface
and naming conventions and/or annotations.
You may also cache the validator to avoid re-resolving it.*/
}
}
//Concrete validation factory where validator types are hard-coded.
public final class HardcodedValidatorFactory implements ValidatorFactory {
public Validator createFromType(String type) {
switch (type) {
case 'True': return new TrueValidator();
//...
}
}
}
//Example of a client class that would use the ValidatorFactory
public final class ValidationService {
private final ValidatorFactory validatorFactory;
public ValidationService(ValidatorFactory validatorFactory) {
this.validatorFactory = validatorFactory;
}
public boolean validate(String validatorType, ThingToValidate thingToValidate) {
Validator validator = validatorFactory.createFromType(validatorType);
return validator.validate(thingToValidate);
}
}
Please note that your Valiator.validate method shouldn't be static! I understand that since validators are stateless you may want to re-use the same instance hence the idea to make the validate method static, but doing so will make your design much less flexible and testable.
If you wish to ensure that the same validator instance is re-used, then you can make sure that only one concrete ValidatorFactory is instantiated in your Composition Root and that this factory caches the validators.

Related

Can I explicitly call custom validator from service in Spring Boot?

I have a custom validator class that implements Validator, like this:
public class MyCustomValidator implements Validator
I want to be able to call its validate() method from a Service.
This is how this method looks:
#Override
public void validate(Object target, Errors errors) {
// validation goes here
MyClass request = (MyClass) target;
if (request.getId() == null) {
errors.reject("content.id", "Id is missing";
}
}
I don't want to have this validator in my endpoint, because I need to fetch the object to be validated from the database and then call the validation on it, so I need to do it from my service.
Can you please guide me on how to achieve this?
Use validation annotations in class but don't use #Valid on request body, then spring won't validate your class.
public class MyClass{
#NotNull
private Integer id;
#NotBlank
private String data;
}
Autowired Validator first
#Autowired
private final Validator validator;
Then for class validate using the validator conditionally when needed.
if(isValidate) {
Set<ConstraintViolation<MyClass>> violations = validator.validate(myClassObj);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(new HashSet<ConstraintViolation<?>>(violations));
}
}
The Validator interface is, as far as i understand it, called as soon as a matching object (determined by the public boolean Validator.supports(Class clazz) method).
However, your goal seems to be to validate an object of MyClass only at a specific time, coming from your persistence layer to your service layer.
There are multiple ways to achieve this.
The first and most obvious one is to not extend any classes, but to use a custom component with some notion of a validation function:
#Component
public class CustomValidator{
public void validate(MyClass target) throws ValidationException {
// validation goes here
if (target.getId() == null) {
throw new ValidationException("Id is missing");
}
}
}
And inject/autowire it into your service object:
#Component
public class MyClassService{
// will be injected in first instance of this component
#Autowired
private CustomValidator validator
public MyClass get(MyClass target) {
try {
validator.validate(target);
return dao.retrieve(target);
} catch (ValidationException) {
// handle validation error
} catch (DataAccessException) {
// handle dao exception
}
}
}
This has the benefit that you yourself can control the validation, and error handling.
The negative side is the relatively high boilerplate.
However, if you want different Validators for different CRUD-Operations (or Service Methods), you may be interested in the Spring Validation Groups Feature.
First, you create a simple marker interface for each Operation you want to differ:
interface OnCreate {};
interface OnUpdate {};
Then, all you need to do is use the marker interfaces in the fields of your entity class,
using the Bean Validation Annotations:
public class MyClass{
#Null(groups = OnCreate.class)
#NotNull(groups = OnUpdate.class)
String id;
}
In order to use those groups in your Service Class, you will have to use the #Validated annotation.
#Validated
#Service
public class MyService {
#Validated(OnCreate.class)
void validateForCreate(#Valid InputWithGroups input){
// do something
}
#Validated(OnUpdate.class)
void validateForUpdate(#Valid InputWithGroups input){
// do something
}
}
Note that #Validated is applied to the service class as well as the methods. You can also set the group for the whole service, if you plan on using multiple services.
I for once mostly use the built-in Jakarta Bean Validation annotations in combination with marker interfaces, because of their ease of use and almost no boilerplate, while staying somewhat flexible and adjustable.
You could inject Validator and call validate
#Autowired
Validator validator;
And then call validate:
Set<ConstraintViolation<Driver>> violations = validator.validate(yourObjectToValidate);

Mocking a Predicate in Java using Mockito

I have a Predicate which checks for a row existence in Database.I am not sure if this is a good use of predicate but it made my code clean and concise.But when I am Tesing this code I am not able to mock the DAO class and not sure why is the case.
public class validator{
public Predicate<String> doesRowExists = fileName -> makeDao().isRowReturned(RowId);
public AlertFileDAO makeDataDao(){
return new DataDao();
}
public boolean validate(String RowId){
return doesRowExists.test(rowId)
}
}
//Test
public class ValidatorTest{
#setup
void beforeAll(){
mockValidator = spy(new Validator());
doReturn(mockDataDao)
.when(mockValidator)
.makeDataDao();
}
#Test
test_whenRowExists(){
new Validator.validate("1-abc-34");
}
When Im triggering the test it is hitting the actual DB and not using the mocked DAO class.Im not sure what exactly I am missing here.Please suggest.
Why don’t you simply inline the predicate and deliver the dao as constructor argument? This makes your api cleaner: method call vs getter for predicate and test on predicate you ended up with.
With your accepted answer, the user has to use the following:
validator.doesRowExist().test(rowId);
I believe the following would be easier to use:
validator.doesRowExist(rowId);
or even:
validator.validate(rowId);
Lets make a series of refactorings to achieve that:
Step 1:
You use your predicate to implement validate function. There are no other calls, nor passing to another functions (higher-order functions accepting a predicate are a typical use for them). Let's change the predicate to a method:
public class Validator {
public DataDao makeDataDao(){
return new DataDao();
}
public boolean validate(String rowId){
return doesRowExist(rowId);
}
private boolean doesRowExist(String rowId) {
return makeDataDao().isRowReturned(rowId);
}
}
Step 2:
Daos are typically singletons (one instance of them is enough). Depending on the frameworks you use, creating a Dao may be more costly than calling a method on it. Let's apply dependency injection principles (class receives it dependencies, not creates them):
public class Validator {
private final DataDao dataDao;
Validator(DataDao dataDao) {
this.dataDao = dataDao;
}
public boolean validate(String rowId){
return doesRowExist(rowId);
}
private boolean doesRowExist(String rowId) {
return dataDao.isRowReturned(rowId);
}
}
If you really need to create Dao each time, you can provide a fecory in the constructor.
Result:
Your class:
has nicer api
is likely more efficient
is trivially testable:
#ExtendWith(MockitoExtension.class)
public class ValidatorTest {
#Mock
DataDao mockDataDao;
#InjectMocks
Validator validator;
#Test
void whenValidateReturnsValueFromIsRowReturned(){
var rowId = "1-abc-34";
doReturn(false)
.when(mockDataDao)
.isRowReturned(rowId);
assertEquals(false, validator.validate(rowId));
}
}
I see your problem as example of more common task: how to stub a field. In your case, you need to stub field doesRowExists.
The common task has common solution: use getter instead: public Predicate<String> getDoesRowExists() { return doesRowExists;} or, with common code style, public Predicate<String> isRowExists() { return doesRowExists;}
So, in your production code you call getter instead field: return isRowExists().test(rowId)
In your test code you just mock this getter: when(isRowExists).thenReturn(true)

Autowire Spring Bean into interface for default method

I need to add a default method to an interface some classes implement, but my IDE complains (bean may not have been initialized).
Code would be something like this:
public interface IValidator {
MyValidationBean beanToBeAutowired;
...
default Boolean doSomeNewValidations(){
return beanToBeAutowired.doSomeNewValidations();
}
}
Is it just that autowiring into interfaces is not allowed or there's something wrong with the code?
Using #Component on the interface doesn't make any difference.
I'd rather keep this design instead of using an abstract class.
Adding a Variable into interface is not possible in Java. It will be by default a public static final constant. So you have to do either the following:
MyValidationBean beanToBeAutowired = new MyValidationBeanImpl();
or the following:
MyValidationBean beanToBeAutowired();
default Boolean doSomeNewValidations(){
return beanToBeAutowired().doSomeNewValidations();
}
And you can override the beanToBeAutowired method in the implementation class.
i can think of solution as below -
public interface IValidator {
public Service getBeanToBeAutowired();
default Boolean doSomeNewValidations(){
return getBeanToBeAutowired().doSomeNewValidations();
}
}
public class ValidatorClass implements IValidator {
#Autowire private Service service;
#Override
public Service getBeanToBeAutowired() {
return service;
}
}
Just an idea, send validation bean to interface as parameter;
public interface IValidator {
default Boolean doSomeNewValidations(MyValidationBean beanToBeAutowired){
return beanToBeAutowired.doSomeNewValidations();
}
}
Your callerClass;
public class CallerClass implements IValidator{
#Autowired
MyValidationBean beanToBeAutowired;
...
doSomeNewValidations(beanToBeAutowired);
}

How to delegate to services by class type?

I have different class types, and depending on some conditions, I want to delegate to the appropriate service that can handle those class types.
Example:
I have several classes as follows.
class Student;
class Prof;
...
For each class there is a service, implementing:
interface IPersonService {
void run();
}
And I have a mode that is found by some conditions:
enum PersonType {
STUDENT, PROF;
}
When I delegate:
#Autowired
private StudentService studentService;
#Autowired
private ProfService profService;
//#param mode assume known
public void delegate(PersonType mode) {
//assume there are several of those switch statements in my business code
switch (mode) {
case STUDENT: studentService.run(); break;
case PROF: profService.run(); break;
default: break;
}
}
Problem: When introducing additional classes, I have to both modify the PersonType and add an additional enum (which is no problem), but I also have to extend any switch statement and add calls to additional delegation services. Also I have to explicit autowire those services to the switch delegator.
Question: how could I optimize this code, to just implementing new Services for any additional class, and not having to touch any of the switch statements?
Add a method to IPersonService so that the implementation of the method can tell the program what type of persons it handles:
interface IPersonService {
PersonType supportedPersonType();
void run();
}
In the service that does the delegation, inject a List<IPersonService>, which Spring will fill with all the implementations of IPersonService that it can find. Then implement the delegate method to look through the list to find the first IPersonService that can handle the specific type.
#Autowired
private List<IPersonService> personServices;
public void delegate(PersonType mode) {
for (IPersonService personService : personServices) {
if (personService.supportedPersonType().equals(mode)) {
personService.run();
break;
}
}
}
This way, you can add new implementations of IPersonService without having to change the service that does the delegation.
To avoid having to go through the loop each time delegate is called, you could build a Map beforehand so that the right IPersonService can be looked up quickly:
class DelegatingService {
#Autowired
private List<IPersonService> personServices;
private Map<PersonType, IPersonService> personServiceMap;
#PostConstruct
public void init() {
personServiceMap = new HashMap<>();
for (IPersonService personService : personServices) {
personServiceMap.put(personService.supportedPersonType(), personService);
}
}
public void delegate(PersonType mode) {
personServiceMap.get(mode).run();
}
}
(Error handling omitted for simplicity).
In my application, we solved similar problem, by putting services into a map. Consider Map<PersonType,IPersonService> serviceMap defined as bean and injected into your class.
Then delegate method simple do
public void delegate(PersonType mode) {
IPersonService service = serviceMap.get(mode);
if (service!=null){
service.run();
}else{
//do something if service is null
}
}
You can store the service bean name (or class type) in the enum, and fetch the beans from the application context using getBean by name (or by class type respectively).
Also, all the services will need to implement a interface which has the run method.
interface ModeService {
void run();
}
enum PersonType {
STUDENT("studentService"), PROF("profService");
private String serviceBean;
public PersonType(String serviceBean) {
this.serviceBean = serviceBean);
}
public String getServiceBean() {
return serviceBean;
}
}
in delegate then the following can be used. ((ModeService)applicationContext.getBean(mode.getServiceBean()).run()
This way only the enum needs to be updated with the service type that is to be used, and no change to delegate method is required.

Guice inject based on annotation value

I would like to use goolge/guice inject a value based on a class i provide with the annotation.
AutoConfig annotation
#BindingAnnotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.PARAMETER, ElementType.FIELD })
public #interface AutoConfig {
// default null not possible
Class<? extends Provider<? extends ConfigLoader<?>>> provider() default XMLAutoConfigProvider.class;
}
This is my annotation which allows configuring the type of config, that should be used for the annotated fields.
Usecase:
#AutoConfig()
ConfigLoader<?> defaultConfig;
#AutoConfig(provider = JsonConfigProvider)
ConfigLoader<?> jsonConfig;
I want to have two configs, one default/xml one and a json one. They will probably never occur in the same class at the same time. But i don't know when the one or the other is used. I used the approach with a class because they are provided by some dependencies/libs and this annotation will be used for some (plugable) submodules.
MyGuiceModule
public class MyGuiceModule extends AbstractModule {
#Override
protected void configure() {
bind(new TypeLiteral<ConfigLoader<?>>() {})
.annotatedWith(AutoConfig.class)
.toProvider(autoConfig.provider());
}
}
This the critical part, i just cannot imagine how to implement it.
So basically i just want to use the provider class specified in the annotation.
Its not necessary to use the provider class here too. Because autoConfig.provider().newInstance() is basically all i need. (I need to use a setter on the new instance but thats all i want to do at this place)
To sum it up all i really want to do is push the annotation (or its values to the provider) either using the get(AutoConfig autoConfig) or in the constructor.
Currently i only use the constructor to inject the configFile value i want to set on the newly generated config instance.
If you know that #AutoConfig(provider = JsonConfigProvider) ConfigLoader<?> jsonConfig is going to return you exactly the results of jsonConfigProvider.get(), and JsonConfigProvider obviously has a public parameterless constructor for newInstance to work, why wouldn't you just ask for a JsonConfigProvider in the first place?
Fundamentally Guice is just a Map<Key, Provider> with fancy wrapping. The bad news is that this makes variable bindings like "bind Foo<T> for all T" impossible to express concisely, and that includes your "bind #Annotation(T) Foo for all T". The good news is that you still have two options.
Bind each provider separately
Though you can't inspect annotations during provision (or tell Guice to do so for you), Guice will compare annotations using their equals methods if you bind an annotation instance rather than an annotation class (the way you would with Names.named("some-name")). This means that you can bind a ConfigLoader<?> with each expected annotation in a Module. Of course, this also means you'll have to have a list of possible ConfigLoader Providers available at configuration time, but they have to be compile-time constants anyway if you're using them as annotation parameters.
This solution works with constructor injection as well, but for fields you'll need both #Inject and #AutoConfig(...), and AutoConfig will need to keep its #BindingAnnotation meta-annotation.
To do this, you're going to have to write an implementation of your annotation, the way Guice does with NamedImpl. Note that the implementations of equals and hashCode must match the ones Java provides in java.lang.Annotation. Then it's just a matter of (redundantly) binding like this:
for(Class<ConfigLoader<?>> clazz : loaders) {
bind(ConfigLoader.class).annotatedWith(new AutoConfigImpl(clazz))
.toProvider(clazz);
}
The definition of equals is up to you, which means you can (and should) bind #AutoConfig(ConfigEnum.JSON) and keep the Guice bindings in your modules rather than specifying your requested implementation all over your codebase.
Use custom injections
You can also use custom injections to search your injected types for custom annotations like #AutoConfig. At this point, you'd be using Guice as a platform to interpret #AutoConfig instead of #Inject, which means that constructor injection won't work but that you can control your injection based on the injected instance, field name, field annotation, annotation parameters, or any combination thereof. If you choose this style, you can drop #BindingAnnotation from AutoConfig.
Use the example in the wiki article linked above as your template, but at minimum you'll need to:
Use bindListener on Binder or AbstractModule to match types that need this custom injection.
In the TypeListener you bind, search injected types for #AutoConfig-annotated fields, and if they have any matching methods then bind those matching methods to a MembersInjector or InjectionListener. You'll probably want to tease the class literal out of the annotation instance here, and pass in the Field and Class as constructor arguments to the MembersInjector/InjectionListener.
In the MembersInjector or InjectionListener you write, instantiate the provider and set the field to the instance the provider provides.
This is a very powerful feature, which would futher allow you to--for instance--automatically provide the configuration based on which instance you're injecting into or based on the name of the field. However, use it carefully and document it heavily, because it may be counter-intuitive to your coworkers that Guice is providing for an annotation other than #Inject. Also bear in mind that this won't work for constructor injection, so refactoring from field injection to constructor injection will cause Guice to complain that it's missing a required binding to instantiate the class.
I had a similar problem. I wanted to use a custom annotation that receives a enum param to choose the implementation. After a lot of research, debug and testing, I came to the following solution:
//enum to define authentication types
public enum AuthType {
Ldap, Saml
}
//custom annotation to be used in injection
#Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
#BindingAnnotation
public #interface Auth {
AuthType value();
}
//defintion of authenticator
public interface Authenticator {
public void doSomehting();
}
//Authenticator implementations
public class LdapAuthenticator implements Authenticator {
#Override
public void doSomehting() {
// doing ldap stuff
}
}
public class SamlAuthenticator implements Authenticator {
#Override
public void doSomehting() {
// doing saml stuff
}
}
public class MyModule extends AbstractModule {
// annotate fields to bind to implementations
private #Auth(AuthType.Ldap) Authenticator ldap;
private #Auth(AuthType.Saml) Authenticator saml;
#Override
protected void configure() {
//bind the implementation to the annotation from field
bindAnnotated("ldap", LdapAuthenticator.class);
bindAnnotated("saml", SamlAuthenticator.class);
}
private void bindAnnotated(String fieldName, Class<? extends Authenticator> implementation) {
try {
//get the annotation from fields, then bind it to implementation
Annotation ann = MyModule.class.getDeclaredField(fieldName).getAnnotation(Auth.class);
bind(Authenticator.class).annotatedWith(ann).to(implementation);
} catch (NoSuchFieldException | SecurityException e) {
throw new RuntimeException(e);
}
}
}
//usage: add #Auth(<AuthType>) to the dependency
public class ClientClass {
private Authenticator authenticator;
#Inject
public ClientClass(#Auth(AuthType.Ldap) Authenticator authenticator) {
this.authenticator = authenticator;
}
}
Check the documentation of Binder
I tested the Jeff Bowman solution, but it apparently works only binding to providers
As a BindingAnnotations#binding-annotations-with-attributes states equals() and hashCode() should be properly implemented. So given that there is MyAnnotation
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER})
public #interface MyAnnotation {
SomeEnum value() default SomeEnum.A;
}
which is used to specify SomeInterface implementation(SomeDefault and SomeOther), SomeModule class could look like
public class SomeModule extends AbstractModule {
#Override
protected void configure() {
bind(Key.get(SomeInterface.class, createAnnotationClass(A))).to(SomeDefault.class);
// more common binding expresion
bind(SomeInterface.class).annotatedWith(createAnnotationClass(B)).to(SomeDefault.class);
}
private Annotation createAnnotationClass(SomeEnum someEnum) {
return new MyAnnotation() {
#Override
public SomeEnum value() {
return someEnum;
}
#Override
public Class<? extends Annotation> annotationType() {
return MyAnnotation.class;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyAnnotationCl myAnnoCl = (MyAnnotationCl) o;
return A == myAnnoCl.getValue();
}
#Override
public int hashCode() {
// from java annotation documentation
return (127 * "value".hashCode()) ^ value().hashCode();
}
};
}
}
Then annotation could be used as follows:
public class DoSomethingWithSomething {
private final SomeInterface someImplementation;
#Inject
public DoSomethingWithSomething(
#MyAnnotation SomeInterface someDefault
// #MyAnnotation(A) SomeInterface someDefault
// #MyAnnotation(B) SomeInterface someOther
) {
this.someImplementation = someDefault;
}
}

Categories

Resources