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))
Related
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.
I am using ValdationSchemaFactoryWarapper class to generate validation properties in JSON schema and I need to add my own custom properties to the schema too. If I develop my custom SchemaFactoryWrapper, I can only use one or the other but not the two at the same time.
I was able to achieve what I wanted by extending ValidationSchemaFactoryWrapper by using the below code, however, I was wondering whether there is perhaps a simpler way to do it, i.e. to use two separate instances of SchemaFactoryWrapper instances.
public class ConfigSchemaExtension {
public static class ConfigSchemaFactoryWrapper extends ValidationSchemaFactoryWrapper {
private static class SchemaFactoryWrapperFactory extends WrapperFactory {
private SchemaFactoryWrapperFactory() {
}
public SchemaFactoryWrapper getWrapper(SerializerProvider p) {
SchemaFactoryWrapper wrapper = new ConfigSchemaFactoryWrapper();
wrapper.setProvider(p);
return wrapper;
}
public SchemaFactoryWrapper getWrapper(SerializerProvider p, VisitorContext rvc) {
SchemaFactoryWrapper wrapper = new ConfigSchemaFactoryWrapper();
wrapper.setProvider(p);
wrapper.setVisitorContext(rvc);
return wrapper;
}
}
public ConfigSchemaFactoryWrapper() {
super(new AnnotationConstraintResolver());
schemaProvider = new ConfigJsonSchemaFactory();
visitorFactory = new FormatVisitorFactory(new SchemaFactoryWrapperFactory());
}
}
public static class ConfigJsonSchemaFactory extends JsonSchemaFactory {
#Override
public com.fasterxml.jackson.module.jsonSchema.types.StringSchema stringSchema() {
return new ConfigStringSchema();
}
}
public static class ConfigStringSchema extends StringSchema {
#JsonProperty
private String myLink;
#Override
public void enrichWithBeanProperty(BeanProperty beanProperty) {
super.enrichWithBeanProperty(beanProperty);
Reference ref = beanProperty.getAnnotation(Reference.class);
if(ref != null) this.myLink = ref.value();
}
}
#Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#JacksonAnnotation
public #interface Reference
{
String value() default "";
}
}```
We have to test class, see below. Our question is how can we fill the dependencies, so that we can test the original class you see below.
public class FragenAntwortenDataprovider extends SortableDataProvider<FragenAntworten, String> {
#SpringBean
private IFragenAntwortenService service;
private IModel<FragenAntworten> filter;
public FragenAntwortenDataprovider(IModel<FragenAntworten> filter){
this.filter = filter;
Injector.get().inject(this);
setSort("id", SortOrder.DESCENDING); // absteigend sortieren
}
#Override
public Iterator<? extends FragenAntworten> iterator(long first, long count){
List<FragenAntworten> list = load();
List<FragenAntworten> sublist = list.subList((int) first, (int) (first+count));
return sublist.iterator();
}
#Override
public long size() {return getListSize();}
#Override
public IModel<FragenAntworten> model(FragenAntworten object) {
return Model.of(object);
}
private void sort(final List<FragenAntworten> list){
}
private long getListSize(){
List<FragenAntworten> list = service.ladeAlle(filter.getObject().getSystem());
return list.size();
}
private List<FragenAntworten> load(){
List<FragenAntworten> list = service.ladeAlle(filter.getObject().getSystem());
return list;
}
}
Since you use Spring you can use its ReflectionUtils helper class to inject the dependency:
IFragenAntwortenService service = mock(IFragenAntwortenService.class);
IModel<FragenAntworten> model = ...;
FragenAntwortenDataprovider provider = new FragenAntwortenDataprovider(model);
Field serviceField = ReflectionUtils.findField("service", provider);
ReflectionUtils.setField(serviceField, provider, service);
Another option is to introduce package-private setter for service field and avoid using reflection.
You should have your original class have the dependencies injected with #Autowired or #Inject. Actually you should not use field injection, but constructor injection. Then you should use the #ExtendWith(MockitoExtension.class) for the JUnit 5 test class. Every dependency you have to define as a #Mock and for the class you wish to test you use the #InjectMocks annotation.
This is the constructor injection to use:
public class FragenAntwortenDataprovider extends SortableDataProvider<FragenAntworten, String> {
private IFragenAntwortenService service;
private IModel<FragenAntworten> filter;
#Inject
public FragenAntwortenDataprovider(IFragenAntwortenService service, IModel<FragenAntworten> filter){
this.service = service;
this.filter = filter;
//I guess you don't need the injector anymore
//Injector.get().inject(this);
setSort("id", SortOrder.DESCENDING); // absteigend sortieren
}
//Rest of class
}
And this is then a test-class:
#ExtendWith(MockitoExtension.class)
public class testClass() {
#Mock
private IFragenAntwortenService service;
#Mock
private IModel<FragenAntworten> filter;
#InjectMocks
FragenAntwortenDataprovider sut; //System under Test
#Test
void test() {
//Testcode
// Control the mock with
when(service.anymethod()).thenReturn(result);
var result = sut.callMethodToTest();
// verify all calls
verify(service, times(1)).anymethod();
// and assert
assertNotNull(result);
}
}
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(...);
}
}
I have Neo4j unmanaged extension. I want some services to be created as singletons and be available via #Context in my resources.
Something like this:
#Path("/example")
public class ExampleResource {
public ExampleResource(#Context CostlyService costlyService) { // <<---
// use it here
}
}
How this can be achieved?
Neo4j has PluginLifecycle interface that give us possibility to hook into Neo4j server lifecycle and provide our own services for injection blog post.
So, we have service. Let's take this one as example:
public interface CostlyService {
}
public class CostlyServiceImpl implements CostlyService {
public CostlyService() {
// a LOT of work done here
}
//...
}
Now we need to make our own PluginLifecycle implementation:
public class ExamplePluginLifecycle implements PluginLifecycle {
#Override
public Collection<Injectable<?>> start(GraphDatabaseService graphDatabaseService,
Configuration config) {
final List<Injectable<?>> injectables = new ArrayList<>();
return injectables;
}
#Override
public void stop() {
}
}
As you see, injectable list is empty for now. We will add our service there soon.
Important: you must register your PluginLifecycle implementation, so it will be available via SPI:
// file: META-INF/services/org.neo4j.server.plugins.PluginLifecycle
my.company.extension.ExamplePluginLifecycle
This will make your PluginLifecycle discoverable by Neo4j server.
Now we need to create actual injectable. Let's write implementation for Injectable interface:
public final class TypedInjectable<T> implements Injectable<T> {
private final T value;
private final Class<T> type;
private TypedInjectable(final T value, final Class<T> type) {
this.value = value;
this.type = type;
}
public static <T> TypedInjectable<T> injectable(final T value, final Class<T> type) {
return new TypedInjectable<>(value, type);
}
#Override
public T getValue() {
return value;
}
#Override
public Class<T> getType() {
return type;
}
}
This will serve as simple container for our service. Usage:
import static my.company.extension.TypedInjectable.injectable;
injectable(new CostlyServiceImpl(), CostlyService.class);
Now we can add our injectable into PluginLifecycle.
#Override
public Collection<Injectable<?>> start(GraphDatabaseService graphDatabaseService,
Configuration config) {
final List<Injectable<?>> injectables = new ArrayList<>();
injectables.add(injectable(new CostlyServiceImpl, CostlyService.class)); // <<---
return injectables;
}
After this change our CostlyService will be available for our resources via #Context:
#Path("/example")
public class ExampleResource {
public ExampleResource(#Context CostlyService costlyService) {
// use it here
}
// ...
}
Tip: keep your PluginLifecycle's in same package or in subpackage with your resources.