Seeding Dagger 2 Factory with Config - java

I'm just getting started with Dagger & Dependency Injection and wondering about configuration at runtime for some of the lower-level dependencies. Is there a way to provide a low-level injected Singleton with a configuration object at runtime?
Basic idea of what I'm after:
#Singleton
class DatabaseService {
#Inject
public DatabaseService(DatabaseConnectionConfig config) { // how can this arg be passed in at runtime?
// make the connection
}
}
#Singleton
class HighLevelService {
#Inject
public HighLevelService(DatabaseService db) {
}
}
#Module
class Module {
#Binds
abstract HighLevelService bindHighLevelService(HighLevelService svc);
#Binds
abstract DatabaseService bindDatabaseService(DatabaseService svc);
}
#Singleton
#Component(modules = {
Module.class
})
interface Factory {
HighLevelService highLevelService();
static Factory create() {
return DaggerFactory.create();
}
}
public class App {
public static void main(String[] args) {
// get the config details from the arguments
DatabaseConnectionConfig config = parseDBConfigFromArgs(args);
// is there a way to configure the DatabaseConnectionConfig from here?
HighLevelService svc = Factory.create().highLevelService();
}
}

You can use a #Component.Factory (or #Component.Builder) with #BindsInstance.
#Singleton
#Component
interface Factory {
HighLevelService highLevelService();
// This nested interface is typically called "Factory", but I
// don't want to look up how to access Factory from Factory.Factory
#Component.Factory
interface MyFactory {
Factory create(#BindsInstance DatabaseConnectionConfig config);
}
static Factory create(DatabaseConnectionConfig config) {
return DaggerFactory.factory().create(config);
}
}
public class App {
public static void main(String[] args) {
DatabaseConnectionConfig config = parseDBConfigFromArgs(args);
HighLevelService svc = Factory.create(config).highLevelService();
}
}

Related

Guice inject an object to a class constructor

I just started looking at Guice for a new project. I have something like this
the ConfigImpl class ans Config interface
interface Config{...}
class ConfigImpl implements Config {
private static final Map<> propMap;
public ConfigImpl(Map<> propMap) {
this.propMap = someProps;
}
}
Guice injection I came up with
public class MyInjector extends AbstractModule {
protected void configure() {
bind(Config.class).to(ConfigImpl.class)
}
}
and finally
public SomeClass {
Config someConfig;
Injector injector = Guice.createInjector(new MyInjector());
someConfig = injector.getInstance(Config.class);
}
Now I am very confused as I can't find a way to pass propMap into ConfigImpl class. I'd like to know the proper way of doing it in Guice. Thanks!
You should inject propMaps from your module:
public class MyInjector extends AbstractModule {
private final Map<String,String> mapProps;
public MyInjector(Map<String,String> mapProps) {
this.mapProps = mapProps;
}
protected void configure() {
bind(Config.class).to(ConfigImpl.class).in(Scope.SINGLETON); // You most than likely want this
bind(new TypeLiteral<Map<String,String>>() {}).toInstance(mapProps); // binding for the map.
}
}
And use it like this:
public class SomeClass {
void doSomething() {
Map<String,String> mapProps = ... ;
Injector injector = Guice.createInjector(new MyInjector(mapProps));
Config someConfig = injector.getInstance(Config.class);
}
}
Also, you should fix your ConfigImpl class:
class ConfigImpl implements Config {
private final Map<String,String> propMap;
#Inject // mandatory since you use a non-default constructor
public ConfigImpl(Map<String,String> propMap) { // add the generic type of the map
this.propMap = propMap;
}
}

How can I properly inject HazelcastMap into a class using Guice?

I have a standard CDI bean named NamedService;
#Named
public class NamedService {
#com.google.inject.Inject
private HazelcastService hazelcastService;
public void writeSomething() {
if (hazelcastService != null) {
System.out.println("injected properly.");
} else {
System.out.println("hazelcastService null");
}
}
}
which injects HazelcastService;
#Named
public class HazelcastService {
#Inject
#HazelcastMap(name = CacheConstants.CONFIGURATION_MAP)
protected IMap<String, String> configurationMap;
}
They are binded in the specified Module class:
public class NamedServiceModule extends AbstractModule {
#Override
public void configure() {
bind(NamedService.class);
bind(HazelcastService.class);
}
}
When I try to see whether hazelcastMap injected properly, It throws an exception;
public class TestGuice {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new NamedServiceModule());
NamedService namedService = injector.getInstance(NamedService.class);
namedService.writeSomething();
}
}
Exception in thread "main" com.google.inject.CreationException: Unable
to create injector, see the following errors:
1) No implementation for com.hazelcast.core.IMap was bound. while locating
com.hazelcast.core.IMap
for field at com.sqills.s3_ticket.service.util.HazelcastService.configurationMap(HazelcastService.java:11)
at
com.sqills.s3_ticket.service.util.NamedServiceModule.configure(NamedServiceModule.java:10)
When I remove the hazelcastMap injection then the injection is successful. How can I properly inject that hazelcast map by using Guice?

Dagger 2 inject singleton without module

I am using Dagger 2 injection to provide some dependency to client:
public class Dependency {
#Inject
Dependency() {
}
void check() {
System.out.print("Instantiated");
}
}
public class Client {
#Inject Dependency dependency;
Client() {
ClientComponent component = DaggerClientComponent.create();
component.inject(this);
}
void checkDependency() {
dependency.check();
}
}
#Component
public interface ClientComponent {
void inject(Client client);
}
public class Test {
public static void main(String... args) {
Client client = new Client();
client.checkDependency();
}
}
It works fine, but now I want to make my dependency singleton. How can I achieve it? Should I create module for this dependency and annotate provide method with singleton annotation or I have another options to avoid module creation?
Add #Singleton on the top of your class and add #Singleton annotation to your component.
#Singleton
public class Dependency {
#Inject
Dependency() {
}
void check() {
System.out.print("Instantiated");
}
}
#Singleton
#Component
public interface ClientComponent {
void inject(Client client);
}
You should also move creation of your component to better place - onCreate method from app object is right place.

bind feign target to guice

is there a way to bind a feign target to guice?
My usecase case is as follows:
I have a service, which can be either started in the same JVM or as a separate service.
if the service is started in same JVM, then I will bind it using Guice.
if the service is started outside the jvm, I want to bind the service using fiegn and have guice inject the same.
I solved this using a Provider implementation in Google Guice.
Here is a sample
public class Main {
public static AccountService get() {
return Feign.builder()
.contract(new JAXRSContract())
.decoder(new GsonDecoder())
.target(AccountService.class, "http://localhost:9090");
}
static class RestClientProvider implements Provider<AccountService> {
RestClientProvider() {
}
#Override
public AccountService get() {
return Main.get();
}
}
static class AppInjector extends AbstractModule {
#Override
protected void configure() {
Provider<AccountService> prov = new RestClientProvider();
bind(AccountService.class).toProvider(prov);
}
}
public static void main (String... args) {
Injector inj = Guice.createInjector(new AppInjector());
AccountService ac = inj.getInstance(AccountService.class);
Account a = ac.getAccountByName("Mihir");
System.out.println(a.getName());
}
}

how to implement a global state with guice?

I want to create a global state (data object, not a service object).
I have created class MyDataObject.
I want to avoid regular global state,
but prefer using Guice dependency injection.
However all the tutorials show how to set a DI for service object with registration to interface.
How can I use Guice injection for my need?
Edit
I have tried:
public class AppInjector extends AbstractModule {
#Override
protected void configure() {
bind(E2eResult.class).toInstance(new E2eResult());
}
}
with:
#Test
public void sendSearchRequest() throws Exception {
...
e2eResult = injector.getInstance(E2eResult.class);
timerUtils.setTimeOut(criticalBlockTimeOutMilli);
timerUtils.startStopWatch();
...
long timeElapsed = timerUtils.stopStopWatch();
e2eResult.runTime = timeElapsed;
...
}
and:
public static void main(String... args) throws ClassNotFoundException, IOException {
Injector injector = Guice.createInjector(new AppInjector());
Result result = runTest(classAndMethod);
E2eResult e2eResult = injector.getInstance(E2eResult.class);
}
and yet I saw the in the main was without the new long value.
To inject GlobalState class you should first a create an instance of it(set it as you like) and then bind class to instance:
bind(GlobalState.class)
.toInstance(globalState);
GlobalState can be created and configured in your "module", you can read about it more here:
https://github.com/google/guice/wiki/GettingStarted
So you have a plain old java object GlobalState:
public class GlobalState {
// whatever...
}
You can use the singleton mechanism provided by guice:
bind(GlobalState.class).in(Singleton.class);
Or use the instance binding:
bind(GlobalState.class).toInstance(new GlobalState());
In this way, you will be able to inject an unique instance of GlobalState in your application.
I eventually create an "old" bad singleton
No need for special binding
because i didn't have any pre-loaded object.
#Override
protected void configure() {
}
just to carry a one and only guice injector
public class InjectorSingleton {
public Injector guiceInjector;
private static InjectorSingleton singleton;
private InjectorSingleton() {
guiceInjector = Guice.createInjector(new AppInjector());
}
public static InjectorSingleton getInstance() {
if (singleton == null) {
singleton = new InjectorSingleton();
}
return singleton;
}
}
and I call this from my main class and from my test class
InjectorSingleton.getInstance().guiceInjector.getInstance(MyDataObject.class);
Fashionably late to the party. I just wanted to share this pattern.
package modules;
public class MetricsModule extends AbstractModule {
#Override
protected void configure() {
// The initializer is an eager singleton
bind(modules.MetricsModule.MeterRegistryInitializer.class).asEagerSingleton();
}
private static class MeterRegistryInitializer {
#Inject
// When initialized, Guice will handle the injection as usual
public MeterRegistryInitializer(Config config, MeterRegistry registry) {
var instance = config.getString("instance.id");
registry.config().commonTags(List.of(
Tag.of("instance", instance)
));
// This is global state
Metrics.addRegistry(registry);
}
}
#Provides
#Singleton
MeterRegistry provideMeterRegistry(
#MetricsDriver String driver,
PrometheusMeterRegistry prometheusRegistry
) {
MeterRegistry registry;
switch (driver) {
case "none":
registry = new CompositeMeterRegistry();
break;
case "prometheus":
registry = prometheusRegistry;
break;
default:
throw new UnsupportedOperationException();
}
return registry;
}
}

Categories