why does Guice create a new instance for my singleton? - java

I have this guice code
public class MainModule extends AbstractModule {
#Override
protected void configure() {
bind(GlobalSettings.class).toProvider(GlobalSettingsProvider.class).in(Singleton.class);
}
and
public class GlobalSettingsProvider implements Provider<GlobalSettings> {
#Override
public GlobalSettings get() {
GlobalSettings globalSettings = new GlobalSettings();
globalSettings.isDummyRun = Boolean.parseBoolean(System.getProperty("isDummyRun", "false"));
globalSettings.inputFlavor = System.getProperty("input_flavor", "none");
}
}
and
public class A(){
public A() {
this.injector = Guice.createInjector(new MainModule());
injector.getInstance(IHttpClientReRunWrapper.class);
globalSettings = injector.getInstance(GlobalSettings.class);
resultsComparerRunner1 = injector.getInstance(ResultsComparerRunner.class);
resultsComparerRunner2 = injector.getInstance(ResultsComparerRunner.class);
resultsComparerRunner3 = injector.getInstance(ResultsComparerRunner.class);
}
}
and
public class ResultsComparerRunner(){
public class ResultsComparerRunner(){
initStaticFromInjector();
}
private void initStaticFromInjector() {
routingResponseShortRepository = injector.getInstance(IRoutingResponseShortRepository.class);
globalSettings = injector.getInstance(GlobalSettings.class); //verify only one injector per run
}
why do I see globalSettings in instance of A and in instance of ResultsComparerRunner are different?
I wanted it to be singelton

I was curious about the behavior of provider binding plus singleton, so I created a test-gist based on your code:
#Test
public void execute() {
Injector injector = Guice.createInjector(new AbstractModule() {
#Override
protected void configure() {
bind(GlobalSettings.class).toProvider(GlobalSettingsProvider.class).in(Singleton.class);
}
});
GlobalSettings g1 = injector.getInstance(GlobalSettings.class);
GlobalSettings g2 = injector.getInstance(GlobalSettings.class);
assertThat(g1).isSameAs(g2);
}
Result: green. Meaning that your module setup is working, your problem must lie somewhere else. How do you pass the injector to the ResultsComparerRunner? Is this really the injector containing the singleton definition?

My problem was that I called
injector = Guice.createInjector(new MainModule());
twice in two different places-
which created two singletons.

Related

How can I dynamically create an instance of a class in java?

I would like to create an object dynamically.
I have the following structure:
1 abstract class and several classes that inherit from the abstract class
abstract public class A {..}
public class B extends A{..}
public class C extends A{..}
I have a config file where i want to add a class name, to have the ability to control which class it should be used.
# config class name
classname = B
I tried the following, but here I have the problem that I have to cast the result
and I do not know how I can do it dynamically at this point
public class TestClass {
public A instB;
public void getInstance(){
this.instB = Class.forName("B") /*here i put later the config value classname*/
.getConstructor(String.class)
.newInstance(new Object[]{"test"}); // <--- How to cast this dynamicly to a class in the config?
}
}
How can I dynamically create an instance of a class?
Just cast it to A:
instB = (A)Class....newInstance(...);
You don't need to know the exact class.
I don't see the point of why you really need to use reflection. I would suggest using a simple strategy pattern, for example:
Strategy.java
public interface Strategy {
void doWork();
}
StrategyA.java
public class StrategyA implements Strategy {
#Override
public void doWork() {
}
}
Strategy B.java
public class StrategyB implements Strategy {
#Override
public void doWork() {
}
}
Main.java
public class Main {
public static void main(String[] args) {
// read the option from a config file
String option = "B";
Strategy strategy = createStrategy(option);
// create a context with the strategy
MyContext context = new MyContext(strategy);
context.doWork();
// config has changed dynamically, change the strategy
context.useStrategy(new StrategyA());
context.doWork();
}
public static Strategy createStrategy(String strategy) {
if (strategy.equals("A")) {
return new StrategyA();
}
return new StrategyB();
}
}
MyContext.java
public class MyContext {
Strategy strategy;
public MyContext(Strategy strategy) {
this.strategy = strategy;
}
public void useStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void doWork() {
strategy.doWork();
}
}

Guice - Field injection returns null

So what I want to do is inject the instance of A created in MyGame into PlayScreen. Here is my code so far:
class MyGame extends Game {
public A a;
public void create() {
a = new A();
Injector injector = Guice.createInjector(new GameModule(this));
setScreen(new PlayScreen());
}
}
public class GameModule extends AbstractModule {
MyGame game;
public GameModule(MyGame game){
this.game = game;
}
#Override protected void configure() {}
#Provides
#Singleton
A getA() {
return game.a;
}
}
public class PlayScreen extends Screen {
#Inject A a;
public void render() {
// Using a
}
}
But in the method render() from PlayScreen, batch comes out as null.
However, if in MyGame I use injector.getInstance(A.class) everything works, I do not get null.
What am I doing wrong?
I solved it. As chrylis said, I had to use constructor injection instead of field injection. Here is the new code:
class MyGame extends Game {
public A a;
public void create() {
a = new A();
Injector injector = Guice.createInjector(new GameModule(this));
setScreen(injector.getInstance(PlayScreen.class));
}
}
#Singleton
public class PlayScreen extends Screen {
A a;
#Inject
PlayScreen(A a) {
this.a = a;
}
public void render() {
// Using a
}
}
GameModule remained the same.

Why am I getting "classes must have either one (and only one) constructor" error?

I have been trying to get Guice working but end up with this:
Classes must have either one (and only one) constructor
My interface:
public interface AddrBookStore {
public Contact getContactByKey(String key);
public void addContact(Contact c);
}
The implementation:
public class RdbmsBasedAddrBookStore implements AddrBookStore {
private Connection connection;
public RdbmsBasedAddrBookStore(Connection connection) {
this.connection = connection;
}
#Override
public Contact getContactByKey(String key) throws AddrBookException
{}
#Override
public void addContact(Contact c) throws AddrBookException
{}
}
The binding module:
public class ABguiceConfingModule extends AbstractModule {
#Override
protected void configure() {
bind(AddrBookStore.class).to(RdbmsBasedAddrBookStore.class);
}
}
The AddrBook client where I am injecting:
public class AddrBook {
private AddrBookStore store;
#Inject
public AddrBook(AddrBookStore store)
{
this.store = store;
}
... other methods;
}
And my main:
public class App
{
public static void main( String[] args ) throws Exception
{
Injector injector = Guice.createInjector(new ABguiceConfingModule() );
AddrBookStore store = injector.getInstance( AddrBookStore.class );
AddrBook book = new AddrBook(store);
AddrBookCLI cli = new AddrBookCLI(book);
cli.interact(new InputStreamReader(System.in), new OutputStreamWriter(System.out));
}}
After all this, I'm getting this error:
1) Could not find a suitable constructor in addrbook.store.RdbmsBasedAddrBookStore. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
[ERROR] at addrbook.store.RdbmsBasedAddrBookStore.class(RdbmsBasedAddrBookStore.java:23)
[ERROR] at addrbook.ABguiceConfingModule.configure(ABguiceConfingModule.java:13)
I have experience with Spring and not with Guice. Where am I going wrong here?
You haven't set up the primary dependency for AddrBookStore. You need to have a binding for Connection, and then you need to annotate the constructor with #Inject.
You've set up the AddrBookStore class but clearly it's wrapping an Rdbms... except you haven't set up the Rdbms.
There are lots of ways to do this in Guice, in this case I would probably do it with a Provider<Connection>, that way you have an entire class to put the code for spinning up your connection to the database, so something like:
public class ConnectionProvider implements Provider<Connection> {
public Connection get() {
// put your code that connects to the database here
}
}
Then your module would be:
public class ABguiceConfingModule extends AbstractModule {
#Override
protected void configure() {
bind(AddrBookStore.class).to(RdbmsBasedAddrBookStore.class);
bind(Connection.class).toProvider(ConnectionProvider.class);
}
}
And then finally your AddrBookStore:
public class RdbmsBasedAddrBookStore implements AddrBookStore {
private Connection connection;
#Inject
public RdbmsBasedAddrBookStore(Connection connection) {
this.connection = connection;
}
#Override
public Contact getContactByKey(String key) throws AddrBookException
{}
#Override
public void addContact(Contact c) throws AddrBookException
{}
}
Had this error message because I forget to add annotation #Inject on constructor. public AddrBook(AddrBookStore store) in the case of this question.

Triggering event from Class A to Class B using interface in Java

Suppose I have defined a class with interface like this:
public class myClass {
public void test() {
//here I want to trigger `onStartListener`
}
interface OnStartListener {
public void onStart();
}
}
and class B I have defined like this:
public class ClassB implements myClass.OnStartListener {
public void ClassB() {
myClass test1 = new myClass();
myClass.test();
}
#Override
public void onStart() {
System.out.println("start triggered");
}
}
How can I trigger OnStartListener from test method of myClass so ClassB can handle it?
Yes, you need to subscribe your listener and call the method in the class A:
public class ClassB implements myClass.OnStartListener {
public void ClassB() {
myClass test1 = new myClass(this);
//test1.setListener(this);
myClass.test();
}
#Override
public void onStart() {
System.out.println("start triggered");
}
}
and
public class myClass {
OnStartListener myListener;
public myClass(OnStartListener myListener) {
this.myListener = myListener;
}
public void test() {
//here I want to trigger `onStartListener`
myListener.onStart();
}
interface OnStartListener {
public void onStart();
}
}
Have a look at how frameworks like swing handle listeners. Basically you need to "register" the listener instance (ClassB instance) with myClass and call onStart() on it.
ClassB would probably contain a List<OnStartListener> which is used in a loop and onStart() is called on each element. Registering would mean assing the instance of ClassB to that list.

using guice injection with actor throws null pointer

I'm getting null pointer exception on the field injection of a server which is started as an akka actor.
Schedular part:
private ActorRef myActor = Akka.system().actorOf(
new Props(Retreiver.class));
#Override
public void onStart(Application app) {
log.info("Starting schedular.....!");
Akka.system()
.scheduler()
.schedule(Duration.create(0, TimeUnit.MILLISECONDS),
Duration.create(30, TimeUnit.MINUTES), myActor, "tick",
Akka.system().dispatcher());
}
Retreiver class part:
public class Retreiver extends UntypedActor {
private Logger.ALogger log = Logger.of(Retreiver .class);
#Inject
private myDataService dataService;
#Override
public void onReceive(Object arg0) throws Exception {
if (0 != dataService.getDataCount()) {
....
....
....
}
}
I'm getting null for dataService. Please advice me on this.
Thanks.
For anyone who needs this:
public class GuiceInjectedActor implements IndirectActorProducer {
final Injector injector;
final Class<? extends Actor> actorClass;
public GuiceInjectedActor(Injector injector, Class<? extends Actor> actorClass) {
this.injector = injector;
this.actorClass = actorClass;
}
#Override
public Class<? extends Actor> actorClass() {
return actorClass;
}
#Override
public Actor produce() {
return injector.getInstance(actorClass);
}
}
AND
Akka.system().actorOf(Props.create(GuiceInjectedActor.class, INJECTOR,Retreiver.class))
Thats it...!!!
You're getting the NullPointerException because Akka is instantiating your Retriever actor and not Guice. You need to get Guice to construct your instance and then pass that to Akka, IndirectActorProducer can help you achieve this, e.g.:
class RetrieverDependencyInjector implements IndirectActorProducer {
final Injector injector;
public RetrieverDependencyInjector(Injector injector) {
this.injector = injector;
}
#Override
public Class<? extends Actor> actorClass() {
return Retriever.class;
}
#Override
public Retriever produce() {
return injector.getInstance(Retriever.class);
}
}
Note that produce() must create a new Actor instance each time it is invoked, it cannot return the same instance.
You can then get Akka to retrieve your actor through the RetrieverDependencyInjector, e.g.:
ActorRef myActor = Akka.system().actorOf(
Props.create(RetrieverDependencyInjector.class, injector)
);
UPDATE
I thought about you comment further, you might be able to turn RetrieverDependencyInjector into a GenericDependencyInjector by providing the class of the Actor you want as a constructor parameter, that perhaps will allow you to do something like:
Props.create(GenericDependencyInjector.class, injector, Retriever.class)
I haven't tried this, but it might give you a starting point.
There can be other ways, eg, you can put a static injector handle in Boot or Some-Injector-Holder-class, and inject when you create the actor by call the inherited method: preStart ()
public class Retreiver extends UntypedActor {
private Logger.ALogger log = Logger.of(Retreiver .class);
#Override
public void preStart () {
super.preStart ();
Boot.injector.injectMembers (this);
//Some-Injector-holder.injectMembers (this);
}
#Inject
private myDataService dataService;
#Override
public void onReceive(Object arg0) throws Exception {
if (0 != dataService.getDataCount()) {
....
....
....
}
}
and also, you can also inject the injector in actor provider to initialize the actor by the injector in a scope of UntypedActorFactory:
public class InjectingActorProvider implements Provider<ActorRef> {
#Inject
private Injector injector;
#SuppressWarnings("serial")
#Override
public final ActorRef get() {
Props props = new Props(new UntypedActorFactory() {
#Override
public Actor create() {
return injector.getInstance(actorClass);
}
});
props = props.withRouter(...);
props = props.withDispatcher(...);
actor = system.actorOf(props, actorName);
return actor;
}
}

Categories

Resources