Wire collection of objects dynamically in Guice - java

Guice newbie here, with a complicated scenario.
My company has a large number of constants of a given type (let's call them Thingy) that belong to different teams and are maintained in different parts of our application. However, we need to have a central registry that knows about all of them (let's call this the ThingyService). I am writing a base module that teams can either extend or install, with the purpose of allowing a team to register their Thingys, and giving them access to the ThingyService. This module takes as parameter a list of classes from which I can extract the Thingy constants, this part is working fine.
What I don't understand is how I can a) make each module know about each other module's list of Thingys and b) how I can create my ThingyService as a singleton that contains all of my Thingys. I have experimented with shared static state and with ThreadLocals, but I keep either breaking tests or breaking my main (play) application. In my naive understanding of Guice, I think I need a MultiBinder for the Thingys, but I don't see how I can share that between modules. Here's what I'd like to do:
class ThingyModule extends AbstractModule{
final Set<Class<?>> myThingyClasses; // this is populated in the constructor
private Set<Thingy> extractThingiesFromThingyClasses(){
// I have this working
}
#Provides #Singleton ThingyService thingyService(
Set<Thingy> thingys // all thingys, from all such modules
){
return new ThingyService(thingys);
}
protected void configure(){
extractThingiesFromThingyClasses().forEach(thingy->
// bind thingy to a global MultiBinder?
);
}
}
How can I make my ThingyService unique and global, with all the Thingys from the entire application? Note: I don't necessarily need my Thingys to be managed by Guice, the only place I need them is in ThingyService. Also, this is a play / scala application if that makes a difference, but my ThingyModule code lives in a library written in Java.

It turns out I omitted one important detail, Thingy has a type parameter, it's actually Thingy<T>, and that's the reason it didn't work before. By cheating and registering Thingy as raw type, and then also injecting it as raw type, I got it to work.
Here is a complete working example using JUnit 5 and AssertJ:
class ThingyModuleTest {
static class Thingy<T>{
private final T value;
Thingy(final T value) {this.value = value;}
#Override public boolean equals(final Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
final Thingy<?> thingy = (Thingy<?>) o; return Objects.equals(value, thingy.value); }
#Override public int hashCode() { return Objects.hash(value); }
}
#Singleton
static class ThingyService{
final Set<Thingy<?>> thingies;
#SuppressWarnings({"unchecked", "rawtypes"}) #Inject
ThingyService(Set<Thingy> thingies) {
this.thingies = ImmutableSet.copyOf((Set)thingies);
}
public Set<Thingy<?>> getThingies() { return thingies; }
}
abstract static class ThingyModule extends AbstractModule {
private final Set<Class<?>> classesToScan;
public ThingyModule(Class<?>... classes) {
this.classesToScan = ImmutableSet.copyOf(classes);
}
private Set<Thingy<?>> scanForThingies(){
return classesToScan.stream()
.flatMap(c-> Arrays.stream(c.getDeclaredFields()))
.filter(f->f.getType().isAssignableFrom(Thingy.class))
.filter(f-> Modifier.isStatic(f.getModifiers())&&Modifier.isFinal(f.getModifiers()))
.map(this::readThingy)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet());
}
#SuppressWarnings("unchecked")
private Optional<Thingy<?>> readThingy(final Field field) {
try{
field.setAccessible(true);
return Optional.ofNullable(field.get(null))
.filter(Thingy.class::isInstance)
.map(Thingy.class::cast);
} catch (IllegalAccessException e) { return Optional.empty(); }
}
#Override protected void configure() {
bind(ThingyService.class);
#SuppressWarnings("rawtypes") Multibinder<Thingy> multibinder = Multibinder.newSetBinder(binder(), Thingy.class);
scanForThingies().forEach(thingy -> multibinder.addBinding().toInstance(thingy));
}
}
static class ThingyModule1 extends ThingyModule {
public ThingyModule1() { super(Thingies1.class); }
static class Thingies1{
static final Thingy<Boolean> BooleanThingy = new Thingy<>(true);
static final Thingy<Integer> IntThingy = new Thingy<>(123);
}
}
static class ThingyModule2 extends ThingyModule {
public ThingyModule2() { super(Thingies2.class); }
static class Thingies2{
static final Thingy<String> StringThingy = new Thingy<>("hello");
static final Thingy<Long> LongThingy = new Thingy<>(123L);
}
}
#Test void validateThingyService() {
ThingyService thingyService = Guice.createInjector(new ThingyModule1(), new ThingyModule2())
.getProvider(ThingyService.class)
.get();
assertThat(thingyService).isNotNull()
.extracting(ts -> ImmutableList.copyOf(ts.getThingies()))
.asList()
.containsExactlyInAnyOrder(BooleanThingy, IntThingy, StringThingy, LongThingy);
}
}
I will mark this answer as accepted until somebody else provides a more idiomatic one.

Related

How can I mock a method that has a parameter with a private constructor?

I have an abstract cache client with an implementation that I'm trying to add unit tests to, and it has a protected class implementation of the key. Like this:
public abstract class SimpleCacheClient<V extends Serializable> {
// Autowired RedissonClient and RedisKeyGenerator
public V get(SimpleCacheKey key) {
// return the cache entry from the autowired RedissonClient
}
public void set(SimpleCacheKey key, V value) {
// set the cache entry
}
public SimpleCacheKey getCacheKey(Object...keys) {
return new SimpleCacheKey(keyGenerator.generateKey(keys));
}
/**
* Simple wrapper for cache key to guarantee that implementations
* are using the key generator provided in this class
*/
protected class SimpleCacheKey {
private String key;
SimpleCacheKey(String key) {
this.key = key;
}
public String getKey() {
return key;
}
#Override
public String toString() {
return getKey();
}
}
}
And here's the implementation I'm trying to test:
public class CacheClientImplementation extends SimpleCacheClient<ArrayList<DateTime>> {
public void addEventDateTimes(String key, ArrayList<DateTime> eventDateTimes) {
// Do stuff with eventDateTimes and then
set(getCacheKey(key), eventDateTimes);
}
public ArrayList<DateTime> getEventDateTimes(String key) {
ArrayList<DateTime> eventDateTimes = get(getCacheKey(key));
// Do stuff with eventDateTimes.
return eventDateTimes;
}
}
I'm trying to test to make sure that CacheClientImplementation performs certain operations on the values provided to it before setting and getting.
I'm trying to mock the redis cache itself by hijacking the get() and set() methods to read and write from/to a HashMap so that I can check the contents of the "cache" in my tests.
#RunWith(MockitoJUnitRunner.class)
public class CacheClientImplementationTest{
#Mock
private RedissonClient redissonClient;
#Mock
private RedisKeyGenerator redisKeyGenerator;
#Spy
#InjectMocks
private CacheClientImplementation cacheClient = new CacheClientImplementation();
private final HashMap<String, ArrayList<DateTime>> cacheMap = new HashMap<>();
#Before
public void setup() {
Mockito.doAnswer((ver) -> {
cacheMap.put(ver.getArgumentAt(0, Object.class).toString(), ver.getArgumentAt(1, ArrayList.class));
return null;
}).when(cacheClient).set(Mockito.any(), Mockito.any(ArrayList.class));
Mockito.doAnswer((ver) -> cacheMap.getOrDefault(ver.getArgumentAt(0, Object.class).toString(), null))
.when(cacheClient).get(Mockito.any());
}
#After
public void teardown() {
cacheMap.clear();
}
}
However, I end up with this problem when I run a test in the file.
C:\...\CacheClientImplementationTest.java:20: error: SimpleCacheClient.SimpleCacheKey has protected access in SimpleCacheClient
}).when(cacheClient).set(Mockito.any(), Mockito.any(ArrayList.class));
Is there any way I can doAnswer for these methods without changing SimpleCacheKey?
Thanks!
This boils down to the visibility of the SimpleCacheKey class, you simply can't use it from a different package. So Mockito.any() can't use that class as a return type unless the unit test is in the same package as SimpleCacheClient.
One solution would be to move your unit test to the same package as SimpleCacheClient. If this is loaded from a different library that you can't change, you can re-create the same package structure to trick the compiler into thinking the package is the same, giving you access to protected classes.
But i believe this trick doesn't work with Java 9 modules.
A better solution would be to make a small modification to your CacheClientImplementation and unit test; encapsulate the part you can't influence and mock that part.
Since you don't really care about the SimpleCacheKey but just the String key, the following should work for your intentions:
public class CacheClientImplementation extends SimpleCacheClient<ArrayList<DateTime>> {
public void addEventDateTimes(String key, ArrayList<DateTime> eventDateTimes) {
// Do stuff with eventDateTimes and then
setForKey(key, eventDateTimes);
}
public ArrayList<DateTime> getEventDateTimes(String key) {
ArrayList<DateTime> eventDateTimes = getForKey(key);
// Do stuff with eventDateTimes.
return eventDateTimes;
}
protected ArrayList<DateTime> getForKey(String key) {
return super.get(getCacheKey(key));
}
protected void setForKey(String key, ArrayList<DateTime> value) {
super.set(getCacheKey(key), value);
}
}
And in the unit test you rewrite to the forKey variants we just created:
Mockito.doAnswer(myAnswer1()).when(cacheClient).setForKey(Mockito.any(), Mockito.any(ArrayList.class));
Mockito.doAnswer(myAnswer2()).when(cacheClient).getForKey(Mockito.any());
I've made the new methods protected as to not confuse callers which method to use, so in this case the unit test must be in same (test) package as the CacheClientImplementation.

Ways to Avoid if-else, switch-case in Factory design pattern

I am designing a validation module. It has 100 error codes(i.e. errcd_01, errcd_02,..,errcd_100) to be validated. In input I am getting a specific error code(i.e. errcd_01) out of above 100.
Module should perform validation for that specific error code.
I am using factory pattern.
/* Interface */
public interface validateErrCd {
void check_errcd();
}
/* Concrete classes implementing the same interface */
public class validateErrCd_01 implements validateErrCd {
#Override
public void check_errcd() {
//business logic related to errcd_01
}
}
public class validateErrCd_02 implements validateErrCd {
#Override
public void check_errcd() {
//business logic related to errcd_02
}
}
.
.
.
public class validateErrCd_100 implements validateErrCd {
#Override
public void check_errcd() {
//business logic related to errcd_100
}
}
/* Factory */
public class ErrorValidationFactory {
//use check_errcd method to get object of type shape
public validateErrCd getValidation(String errorCode){
if(errorCode == null){
return null;
}
if(errorCode.equalsIgnoreCase("errcd_01")){
return new validateErrCd_01();
} else if(errorCode.equalsIgnoreCase("errcd_02")){
return new validateErrCd_02();
} ..
.......
else if(errorCode.equalsIgnoreCase("errcd_100")){
return new validateErrCd_100();
}
else {
return null;
}
}
}
/* I am using the Factory to get object of concrete class by passing an specific error code to be validated (i.e. "errcd_01"). */
public class FactoryPatternDemo {
public static void main(String[] args) {
ErrorValidationFactory errorFactory = new ErrorValidationFactory();
//get an object of validateErrCd_01 and call its check_errcd method.
validateErrCd errcd01 = errorFactory.getValidation("errcd_01");
//call check_errcd method of validateErrCd_01
errcd01.check_errcd();
}
}
Now due to multiple if/else inside Factory class ErrorValidationFactory, I am getting couple of CI/CD errors while performing mvn clean install.
e.g. [MethodLength] - checkstyle, Rule:CyclomaticComplexity - PMD.
So is there a way I can replace if/else, switch case kind of decision making inside factory which does not trigger above CI/CD errors in Java?
Note : If possible I would like to avoid reflection
You could use a Map:
public class ErrorValidationFactory {
private Map<String,Supplier<validateErrCd>> creators=new HashMap<>();
public ErrorValidationFactory(){
creators.put("errcd_100",validateErrCd_100::new);
//Same for others
}
//use check_errcd method to get object of type shape
public validateErrCd getValidation(String errorCode){
if(errorCode == null){
return null;
}
return creators.getOrDefault(errorCode,()->null);
}
}
Supplier is a functional interface that contains a method returning an object. SomeClass::new or ()->new SomeClass() means that the constructor of the class will be used for that.
This allows to to create the instances later.
If you want to create the Map only once, you can make it static and populate it in a static initializer.
However, if you really want to dynamically get the constructors, you would need to use reflection.

Dynamicaly instanciate class from name with injector

Context
I develop, for my company a software that classifies phishing and malware containing website thanks to multiple feature extraction algorithm.
Once features are extracted we use a pool of empirical and machine learning classifiers. We choose among them thanks to election function of our own.
the code
Basically we have our classifier classes that implement the AnalysisFunction contract.
public abstract class AnalysisFunction {
abstract public StatusType analyze(List<TokenEntity> tokens);
abstract public double getPhishingProbability(List<TokenEntity> tokens);
}
Our pool of classifier is contained by a "pool" that implements AnalysisFunction.
public class PoolAnalysisFunction extends AnalysisFunction{
private final List<AnalysisFunction> candidates;
private final ChoiceFunction choice;
private static final Logger LOG = LogManager.getLogger(PoolAnalysisFunction.class);
public PoolAnalysisFunction(List<AnalysisFunction> candidates, ChoiceFunction choice) {
this.candidates = candidates;
this.choice = choice;
}
#Override
public StatusType analyze(List<TokenEntity> tokens) {
try {
return choice.chooseAmong(candidates, tokens).analyze(tokens);
} catch (ImpossibleChoiceException e){
LOG.fatal("Not enough analysis function.", e);
return StatusType.CLEAN;
}
}
#Override
public double getPhishingProbability(List<TokenEntity> tokens) {
try {
return choice.chooseAmong(candidates, tokens).getPhishingProbability(tokens);
} catch (ImpossibleChoiceException e){
LOG.fatal("Not enough analysis function.", e);
return 0;
}
}
}
To ease the deployment and testing of new function, we want to make our pool fully customizable and instanciate every function by its name. To achieve this purpose we have a key in our property file that is like analysis.pool.functions=com.vadesecure.analysis.empirical.Function1,com.vadesecure.analysis.machine.AutomaticClassifier1.
I want to instantiate my functions thanks to that.
My problem is that those classifiers depend on different things such as custom configuration object and machine learning model.
I would like to inject those dependencies that are already bound in my hk2 injector.
import org.glassfish.hk2.api.Factory;
public class PoolFunctionFactory implements Factory<AnalysisFunction> {
private final PoolAnalysisParameters parameters;
private static final Logger LOG = LogManager.getLogger(PoolAnalysisFunction.class);
#Inject
public PoolFunctionFactory(PoolAnalysisParameters parameters) {
this.parameters = parameters;
}
#Override
public AnalysisFunction provide() {
try {
Class<?> choice = Class.forName(parameters.getChoiceFunctionFQDN());
ChoiceFunction choiceFunction = new PhishingPriorityChoiceFunction(); // default choice
if(choice.getSuperclass().isInstance(ChoiceFunction.class)){
choiceFunction = (ChoiceFunction) choice.newInstance();
}
List<AnalysisFunction> analysisFunctions = new LinkedList<>();
// I want to instantiate here
}
return new PoolAnalysisFunction(analysisFunctions, choiceFunction);
} catch (ClassNotFoundException|IllegalAccessException|InstantiationException e){
LOG.fatal(e, e);
}
return null;
}
#Override
public void dispose(AnalysisFunction analysisFunction) {
LOG.trace(String.format("%s end of life", analysisFunction));
}
}
On example of model-dependant classifier is :
public class SVMF2AnalysisFunction extends AnalysisFunction {
private final SVMContainer modelContainer;
private double probability = 0.0;
private double threshold = 0.9;
#Inject // i build this model in a parallel thread
public SVMF2AnalysisFunction(SVMContainer modelContainer) {
this.modelContainer = modelContainer;
}
#Override
public StatusType analyze(List<TokenEntity> tokens) {
if (modelContainer.getModel() == null) {
return null;
}
probability = modelContainer.getModel().analyse(tokens.stream());
return probability >= threshold ? StatusType.PHISHING : StatusType.CLEAN;
}
#Override
public double getPhishingProbability(List<TokenEntity> tokens) {
return probability;
}
}
How can I achieve those instanciations.
My first approach was to inject the serviceLocator but i found no documentations for doing this and a colleague said me it was not good.
He told be to document myself about proxies but it doesn't seem to be a good thing for me or perhaps I missed something.
You could just configure all this in your binder. This way you don't need to worry about trying to instantiate everything yourself. Just let HK2 do all the work
#Override
protected void configure() {
bindAsContract(PoolAnalysisFunction.class).in(Singleton.class);
bind(choiceFnClass).to(ChoiceFunction.class);
for (Class<AnalysisFunction> analysisFnClass: analyisFnClasses) {
bind(analysisFnClass).to(AnalysisFunction.class).in(Singleton.class);
}
}
Then you can just inject everything into the PoolAnalysisFunction class, without the need to use a factory.
#Inject
public PoolAnalysisFunction(IterableProvider<AnalysisFunction> candidates,
ChoiceFunction choice) {
this.choice = choice;
this.candidates = new ArrayList<>();
candidates.forEach(this.candidates::add);
}
Notice the IterableProvider class. This is an HK2 class for injecting multiple services bound to the same contract.
Or if you want to use the factory, you could, and just inject the functions into the factory. That way you can make the PoolAnalysisFunction class independent of an HK2 classes (i.e. the InjectableProvider).

Tapestry: Inject at runtime

again a small problem by understanding "how tapestry works".
I've got a Tapestry component (in this case a value encoder):
public class EditionEncoder implements ValueEncoder<Edition>, ValueEncoderFactory<Edition> {
#Inject
private IEditionManager editionDao;
public EditionEncoder(IEditionManager editionDao) {
this.editionManager = editionDao;
}
#Override
public String toClient(Edition value) {
if(value == null) {
return "";
}
return value.getName();
}
#Override
public Edition toValue(String clientValue) {
if(clientValue.equals("")) {
return null;
}
return editionManager.getEditionByName(clientValue);
}
#Override
public ValueEncoder<Edition> create(Class<Edition> type) {
return this;
}
}
Injecting the the Manager is not working, because the Encoder is created within a page like that:
public void create() {
editionEncoder = new EditionEncoder();
}
casued by this, i'm forced to use this ugly solution:
#Inject
private IEditionManager editionmanager;
editionEncoder = new EditionEncoder(editionManager);
Is there a better way to inject components during runtime or is there a better solution in general for it?
Thanks for your help in advance,
As soon as you use "new" then tapestry-ioc is not involved in object creation and can't inject. You should inject everything and never use "new" for singleton services. This is true for all ioc containers, not just tapestry-ioc.
Also if you put #Inject on a field then you don't also need a constructor to set it. Do one or the other, never both.
You should do something like this:
public class MyAppModule {
public void bind(ServiceBinder binder) {
binder.bind(EditionEncoder.class);
}
}
Then in your page/component/service
#Inject EditionEncoder editionEncoder;
If you wanted to put your own instantiated objects in there you can do
public class MyServiceModule {
public void bind(ServiceBinder binder) {
binder.bind(Service1.class, Service1Impl.class);
binder.bind(Service2.class, Service2Impl.class);
}
public SomeService buildSomeService(Service1 service1, Service2 service2, #AutoBuild Service3Impl service3) {
Date someDate = new Date();
return new SomeServiceImpl(service1, service2, service3, someDate);
}
}

Can I pass arguments into GWT Module constructor?

I have the following GWT module:
public class FizzModule implements EntryPoint {
private Buzz buzz;
public FizzModule() {
this(null);
}
public FizzModule(Buzz bz) {
super();
setBuzz(bz);
}
#Override
public void onModuleLoad() {
// ...etc.
}
}
I would like to "inject" FizzModule with a Buzz instance. However, all of the code examples I see for GWT modules do not use constructors. Instead, they bootstrap the DI mechanism (typically either ClientFactory or GIN) from inside the onModuleLoad() method. Is this something that GWT forces, or can I somehow inject my module before it loads to the client-side? Thanks in advance!
GWT instantiates your module using its zero-arg constructor, always.
(technically, I think it uses GWT.create() so you could use deferred binding rules, but that wouldn't change anything re. how its instantiated)
BTW, where would the Buzz instance come from?
You could add parameters to the URL and use PlaceController. Then get those values on module load.
public void onModuleLoad() {
SimplePanel mainPanel = new SimplePanel();
EventBus eventBus = GWT.creat(EventBus.class);
// Start ActivityManager for the main widget with ActivityMapper
ActivityManager activityManager = new ActivityManager(injector.getActivityMapper(),
eventBus);
activityManager.setDisplay(mainPanel);
RootPanel.get().add(mainPanel);
// Start PlaceHistoryHandler with our PlaceHistoryMapper
AppPlaceHistoryMapper contentHistoryMapper = GWT.create(AppPlaceHistoryMapper.class);
PlaceHistoryHandler historyHandler = new PlaceHistoryHandler(contentHistoryMapper);
PlaceController placeController = new PlaceController(eventBus)
historyHandler.register(placeController, injector.getEventBus(), new MainPlace());
// Goes to the place represented on URL else default place
historyHandler.handleCurrentHistory();
if(placeController.getWhere() instanceof MainPlace) {
(MainPlace).getFoo();
}
}
public class MainPlace extends Place {
private String foo;
public MainPlace(String token) {
String foo = token;
}
#Override
public String getFoo() {
return foo;
}
public static class Tokenizer implements PlaceTokenizer<MainPlace> {
#Override
public MainPlace getPlace(String token) {
return new MainPlace(token);
}
#Override
public String getToken(MainPlace place) {
return place.getFoo();
}
}
}

Categories