I implemented a validation using the chain of responsibility pattern. The request payload to validate can have different parameters. The logic is: if the payload has some parameters, validate it and continue to validate other, else throw an exception. In a level of the validation chain I need to call other services, and here comes into play the Dependency Injection.
The validation structure is like a tree, starting from top to bottom.
So, the class where I need to start the Validation
#Service
public class ServiceImpl implements Service {
private final .....;
private final Validator validator;
public ServiceImpl(
#Qualifier("lastLevelValidator") Validator validator, .....) {
this.validator = validator;
this...........=............;
}
/...../
private void validateContext(RequestContex rc) {
Validator validation = new FirstLevelValidator(validator);
validation.validate(rc);
}
}
So the Validator Interface
public interface Validator<T> {
void validate(T object);
}
The validation classes that implements Validator
#Component
public class FirstLevelValidator implements Validator<RequestContext>{
private final Validator<RequestContext> validator;
#Autowired
public FirstLevelValidator(#Qualifier("lastLevelValidator") Validator<RequestContext> validator) {
this.validator = validator;
}
#Override
public void validate(RequestContext requestContext) {
if ( requestContext.getData() == null ) {
LOGGER.error(REQUEST_ERROR_MSG);
throw new BadRequestException(REQUEST_ERROR_MSG, INVALID_CODE);
}
if (requestContex.getData() == "Some Data") {
Validator validator = new SecondLevelValidator(this.validator);
validator.validate(requestContext);
} else {/* other */ }
}
#Component
public class SecondLevelValidator implements Validator<RequestContext>{
private final Validator<RequestContext> validator;
#Autowired
public SecondLevelValidator(#Qualifier("lastLevelValidator") Validator<RequestContext> validator) {
this.validator = validator;
}
#Override
public void validate(RequestContext requestContext) {
if ( requestContext.getOption() == null ) {
LOGGER.error(REQUEST_ERROR_MSG);
throw new BadRequestException(REQUEST_ERROR_MSG, INVALID_CODE);
}
if ( requestContext.getOption() == " SOME " ) {
validator.validate(requestContext); //HERE WHERE I CALL THE Qualifier
}
}
#Component
public class LastLevelValidator implements Validator<RequestContext>{
private final ClientService1 client1;
private final ClientService2 client2;
public LastLevelValidator(ClientService1 client1, ClientService2 client2) {
this.client1 = client1;
this.client2 = client2;
}
#Override
public void validate(RequestContext requestContext) {
Integer userId = client2.getId()
List<ClientService1Response> list = client1.call(requestContext.id(), userId);
boolean isIdListValid = list
.stream()
.map(clientService1Response -> clientService1Response.getId())
.collect(Collectors.toSet()).containsAll(requestContext.getListId());
if (!isIdListValid) {
LOGGER.error(NOT_FOUND);
throw new BadRequestException(NOT_FOUND, INVALID_CODE);
} else { LOGGER.info("Context List validated"); }
}
}
In the LastLevelValidator I need to call other services to make the validation, for that I inject into each validator class (First.., Second..) the #Qualifier("lastLevelValidator") object, so when I need to instantiate the LastLevelValidation class I can call it like validator.validate(requestContext); instance of validator.validate(ClientService1, ClientService2 ) that it would force me to propagate the ClientServices objects through all the chain from the ServiceImpl class.
Is it this a good solution ?
Is there any concern I didn't evaluate?
I tried also declaring the services I need to call for the validation as static in the LastLevelValidation, in the way that I can call it like LastLevelValidation.methodvalidar(), but look like not a good practice declares static objects.
I tried to pass the objects I need propagating it for each Validation class, but seems to me that if I need another object for the validation I have to pass it through all the validation chain.
Related
In Spring, when I inject a list of beans, I only want to inject specific implementations of the interface, when used from different places. Is this possible to do? What would be the cleanest way to configure this? For example, I have the following validators:
public interface Validator {
Optional<Error> validate(MultipartFile file);
}
public class Validator1 implements Validator {
public Optional<Error> validate(MultipartFile file) {
// do stuff
}
}
public class Validator2 implements Validator {
public Optional<Error> validate(MultipartFile file) {
// do stuff
}
}
public class Validator3 implements Validator {
public Optional<Error> validate(MultipartFile file) {
// do stuff
}
}
And then I have a validation service which looks similar to this:
public class ValidationService {
#Autowired
private List<Validator> validators;
public List<Error> validate(MultipartFile file) {
List<Error> errors = new ArrayList<>();
validators.forEach(v -> {
Optional<Error> error = v.validate(file);
if (error.isPresent()) {
errors.add(error.get());
}
});
return errors;
}
}
And then I have some services, which use the ValidationService, e.g:
public class Service1 {
#Autowired
private ValidationService validationService;
public void doStuff(MultipartFile file) {
...
validationService.validate(file);
...
}
}
public class Service2 {
#Autowired
private ValidationService validationService;
public void doStuff(MultipartFile file) {
...
validationService.validate(file);
...
}
}
When Service1 calls validate, I only want Validator1 and Validator2 to have been injected into the ValidatorService.
When Service2 calls validate, I only want Validator2 and Validator3 to have been injected into the ValidatorService.
Hope I have explained this clearly enough. Thanks in advance for any help offered.
Create the bean like this with #Qualifier annotations --
#Qualifier("validator1")
public class Validator1 implements Validator {
public Optional<Error> validate(MultipartFile file) {
// do stuff
}
}
#Qualifier("validator2")
public class Validator2 implements Validator {
public Optional<Error> validate(MultipartFile file) {
// do stuff
}
}
#Qualifier("validator3")
public class Validator3 implements Validator {
public Optional<Error> validate(MultipartFile file) {
// do stuff
}
}
and inject it like this ---
#Autowired("validator1")
private ValidationService validationService;
Update
You can also create a bean collection for all the validators like this -
#Bean("validators")
public List<Validator> validatorList(Validator1 validator1, Validator2 validator2, Validator3 validator3) {
return Arrays.asList(validator1, validator2, validator3);
}
and the inject the list bean as --
#Autowired("validators")
private List<Validator> validators;
Check this page fore a detailed example - https://www.baeldung.com/spring-injecting-collections
This is likely not the best way to do this.
Here is how I would do it,
based on my current understanding of Spring.
Summary:
Create a bean method for each collection of implementations.
In your case, create a bean method for a List<Validator> that contains Validator1 and Validator2 and create a second List<Validator> that contains Validator2 and Validator3.
Inject the desired List using #Qualifier.
The code should be something like this:
#Configuration
public class ValidatorLists
{
private void getAndAddBean(
final ApplicationContext applicationContext,
final List<Validator> list,
final String beanName)
{
final Validator bean;
bean = applicationContext.getBean(beanName);
if (bean != null)
{
list.add(bean);
}
}
#Bean("ValidatorList1")
public List<Validator> validatorList1(final ApplicationContext applicationContext)
{
Validator bean;
final List<Validator> returnValue = new LinkedList<>();
getAndAddBean(applicationContext, returnValue, "ValidatorImpl1");
getAndAddBean(applicationContext, returnValue, "ValidatorImpl2");
return returnValue;
}
#Bean("ValidatorList2")
public List<Validator> validatorList2(final ApplicationContext applicationContext)
{
Validator bean;
final List<Validator> returnValue = new LinkedList<>();
getAndAddBean(applicationContext, returnValue, "ValidatorImpl2");
getAndAddBean(applicationContext, returnValue, "ValidatorImpl3");
return returnValue;
}
}
Then reference the list by qualifier.
#Autowired
#Qualifier("ValidatorList1")
private List<Validator> validators;
I have a Quarkus application in which I implemented the ContainerRequestFilter interface to save a header from incoming requests:
#PreMatching
public class SecurityFilter implements ContainerRequestFilter {
private static final String HEADER_EMAIL = "HD-Email";
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String email = requestContext.getHeaders().getFirst(HEADER_EMAIL);
if (email == null) {
throw new AuthenticationFailedException("Email header is required");
}
requestContext.setSecurityContext(new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return () -> email;
}
#Override
public boolean isUserInRole(String role) {
return false;
}
#Override
public boolean isSecure() {
return false;
}
#Override
public String getAuthenticationScheme() {
return null;
}
});
}
}
In a class annotated with ApplicationScoped I injected the context as follows:
#ApplicationScoped
public class ProjectService {
#Context
SecurityContext context;
...
}
The problem is that the context attribute is actually never injected, as it is always null.
What am I doing wrong? What should I do to be able to retrieve the SecurityContext throughout the application's code?
I like to abstract this problem, so that the business logic does not depend on JAX-RS-specific constructs. So, I create a class to describe my user, say User, and another interface, the AuthenticationContext, that holds the current user and any other authentication-related information I need, e.g.:
public interface AuthenticationContext {
User getCurrentUser();
}
I create a RequestScoped implementation of this class, that also has the relevant setter(s):
#RequestScoped
public class AuthenticationContextImpl implements AuthenticationContext {
private User user;
#Override
public User getCurrentUser() {
return user;
}
public void setCurrentUser(User user) {
this.user = user;
}
}
Now, I inject this bean and the JAX-RS SecurityContext in a filter, that knows how to create the User and set it into my application-specific AuthenticationContext:
#PreMatching
public class SecurityFilter implements ContainerRequestFilter {
#Inject AuthenticationContextImpl authCtx; // Injecting the implementation,
// not the interface!!!
#Context SecurityContext securityCtx;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
User user = ...// translate the securityCtx into a User
authCtx.setCurrentUser(user);
}
}
And then, any business bean that needs the user data, injects the environment-neutral, application-specific AuthenticationContext.
#Context can only be used in JAX-RS classes - i.e. classes annotated with #Path.
In your case, ProjectService is a CDI bean, not a JAX-RS class.
The canonical way to do what you want is to inject the SecurityContext into a JAX-RS resource and then pass that as a method parameter to your ProjectService
I have instantiated a parametrized constructor here called request operation with dynamic values. how to #Autowire this to Requestclass? subsequently, in Request class, I have created a new RatingResponse how to #Autowire this as well?
class Initializer
public class Intializer
{
NewClass newclass = new NewClass();
String testName = Number + "_" + "Test"; -->getting the String number dynamically
Test test = new Test(testName); -> this is a different class
Operation operation = new RequestOperation(test, newclass ,
sxxx, yyy, zzz); - argumented constructor
opertaion.perform();
}
RequestClass
public class RequestOperation implements Operation {
// the constructor
public RequestOperation(Test test, Sheet reportSheet, XElement element, TestDataManager testDataManager, Report report)
{
this.test = test;
this.newclass = newclass ;
this.sxxx= sxxx;
this.yyy= yyy;
this.zzz= zzz;
}
#Override
public boolean perform(String CompanyName, String Province) {
Response response = new RatingResponse(this.test, this.reportSheet,
callService(this.buildRequest(CompanyName, Province)), this, this.report);-> create a new paramterizedconstructor
}
private String buildRequest(String CompanyName, String Province) {
return pm.getAppProperties().getProperty(constructedValue); }
}
**Response class **
public class RatingResponse implements Response {
public RatingResponse(Test test, Sheet reportSheet, Object obj, RequestOperation requestOperation, Report report) {
this.test = test;
if (obj instanceof Document) {
this.document = (Document) obj;
}
this.operation = requestOperation;
this.reportSheet = reportSheet;
this.report = report;
}
** interface **
#Component
public interface Operation {
public boolean perform(String Name, String Province);
}
#Component
public interface Response {
void construct();
}
In spring boot, you can autowire only types marked with #Bean or classes marked with #Component or its derivitives like #Service, #Controller
The speciality of these annotations is that only a single instance of the class is kept in memory.
So if your requirement needs you to create new classes for each set of new dynamic values, then autowiring them is not the right way to go.
However if you have limited number of possible dynamic values that your class can have, you can create beans for each of them like this
#Configuration
class MyBeans{
#Bean
public RatingResponse ratingResponse(){
Response response = new RatingResponse(this.test, this.reportSheet,
callService(this.buildRequest(CompanyName, Province)), this, this.report);
return response
}
}
Then in the class you need to use it, you can do
#Autowired
RatingResponse ratingResponse
I have a service which needs injection of mulitmap - Map<String, List<Enricher>>
public class EnrichService {
private Map<String, List<Enricher>> typeEnrichers;
#Inject
public EnrichService(Map<String, List<Enricher>> typeEnrichers) {
this.typeEnrichers = typeEnrichers;
}
public void enrich(Entity entity) {
List<Enricher> enrichers = typeEnrichers.get(entity.type);
//.. enriching entity with enrichers
}
}
class Entity {
String id;
String type = "shapedColorful";
String color;
String shape;
}
interface Enricher {
void enrich(Entity entity);
}
class ColorEnricher implements Enricher {
#Inject
private ColorService colorService;
public void enrich(Entity entity) {
entity.color = colorService.getColor(entity.id);
}
}
class ShapeEnricher implements Enricher {
#Inject
private ShapeService shapeService;
public void enrich(Entity entity) {
entity.shape = shapeService.getShape(entity.id);
}
}
I need help with configuring typeEnrichers binder in juice
Here is what I'm trying, but stuck
bind(ColorService).to(ColorServiceImpl.class);
bind(ShapeService).to(ShapeServiceImpl.class);
MapBinder<RelationType, List<Enricher>> mapBinder = MapBinder.newMapBinder(
binder(),
new TypeLiteral<String>() {},
new TypeLiteral<List<Enricher>>() {});
mapBinder.addBinding("shapedColorful", to(/*how to bind list of Enrichers here??*/))
Any help, how I can bind such multimap?
You are trying mix together MapBinder with Multibinder.
I would suggest you to create a Provider for each MapBinder relation. Actually Multibinder is a List Provider itself, to be specific its RealMultibinder implementation unfortunatelly is package private and forbidden from use. If it would not be package private maybe we could use it this way. Most likely it would not work anyway... Imho, it would be nice.
bind(ColorService).to(ColorServiceImpl.class);
bind(ShapeService).to(ShapeServiceImpl.class);
MapBinder<RelationType, List<Enricher>> mapBinder = MapBinder.newMapBinder(
binder(),
new TypeLiteral<String>() {},
new TypeLiteral<List<Enricher>>() {});
mapBinder.addBinding("shapedColorful", toProvider(Multibinder.newSetBinder(this.binder(), Enricher.class).addBinding().to(ColorService.class).addBinding().to(ShapeService.class).asEagerSingleton()))
You can still create a provider and use it:
public class ShapeColorfulProvider implements Provider<List<Enricher>> {
#Inject private ColorService colorService;
#Inject private ShapeService shapeService;
public List<Enricher> get() {
return Lists.newArrayList(colorService,shapeService);
}
}
then
mapBinder.addBinding("shapedColorful", toProvider(ShapeColorfulProvider.class))
Let's say we have interface:
public interface IAuthentication { }
and two implementations:
public class LdapAuthentication implements IAuthentication {}
public class DbAuthentication implements IAuthentication {}
And finally we have a bean that is responsible for processing authentication. This bean should use one of the implementations shown above (based on configuration specified in for example db).
#Service
public class AuthenticationService {
public boolean authenticate(...) {
boolean useDb = ...; //got from db
//my problem here
//how to get right implementation: either LdapAuthentication or DbAuthentication?
IAuthentication auth = ...;
return auth.authenticate(...);
}
}
Question:
How to get the right implementation?
If parameter value does not change:
#Service
public class AuthenticationService {
private IAuthentication auth;
#PostConstruct
protected void init() {
boolean useDb = ...; //got from db
this.auth = ...; //choose correct one
}
public boolean authenticate(...) {
return auth.authenticate(...);
}
}
If parameter is dynamic
#Service
public class AuthenticationService {
#Autowired
private ApplicationContext сontext;
public boolean authenticate(...) {
boolean useDb = ...; //got from db
IAuthentication auth = context.getBean(useDb ? DbAuthentication.class : LdapAuthentication.class);
return auth.authenticate(...);
}
}