Guice: injecting factory-generated instances properly - java

I'm using Guice 3.0 on a Groovy project and am running into bizarre/unexpected injection behaviors. My main/bootstrapping class:
class WidgetServicePerfTester {
#Inject
WidgetGenerator widgetGenerator
static main(args) {
Injector injector = Guice.createInjector(new WidgetServicePerfTesterModule())
WidgetServicePerfTester perfTester = injector.getInstance(WidgetServicePerfTester)
perfTester.run()
}
void run() {
List<Widget> widgets = widgetGenerator.generateWidgets()
widgets.each {
it.doStuff()
}
}
}
My POJO:
class Widget extends Thingy {
WidgetClient widgetClient
int numFoos
#Override
void doStuff() {
widgetClient.doSomethingOnServer()
}
}
My POJO generators (API + impl):
interface WidgetGenerator {
List<Widget> generateWidgets()
}
class SimpleWidgetGenerator implements WidgetGenerator {
#Inject
WidgetClient widgetClient
int numWidgets
SimpleWidgetGenerator() {
super()
}
SimpleWidgetGenerator(int numWidgets) {
super()
this.numWidgets = numWidgets
}
#Override
List<Widget> generateWidgets() {
List<Widget> widgets = []
Widget firstWidget = new Widget(widgetClient: widgetClient, numFoos: getRandomNumber())
widgets.add(firstWidget)
// Code that populates the rest of 'widgets' with 'numWidgets' number of Widgets.
}
}
My Guice module:
class WidgetServicePerfTesterModule extends AbstractModule {
#Override
protected void configure() {
bind(WidgetClient).to(SimpleWidgetClient)
}
#Provides
public WidgetGenerator provideSimpleWidgetGenerator() {
new SimpleWidgetGenerator(50)
}
}
When I run this code I get null pointer exceptions because the WidgetClient injected into each Widget is NULL. Why and what's the fix?

The whole problem begins here in WidgetServicePerfTesterModule class, in the following block of code:
#Provides
public WidgetGenerator provideSimpleWidgetGenerator() {
new SimpleWidgetGenerator(50)
}
An instance of SimpleWidgetGenerator is created using this constructor:
SimpleWidgetGenerator(int numWidgets) {
super()
this.numWidgets = numWidgets
}
and since this object is created manually injecting WidgetClient into SimpleWidgetGenerator will not work - it's null and it's explainable.
To fix the problem guice-assistedinject should be used and factory for Widgets should be created. Here you can find refactored project using the mentioned extensions. Everything works fine.
Is that clear now?

Related

Replacement for isInstance() method when deploying to web in libgdx

I'm making a game with libGDX that I want to export to HTML using Gradle. The issue comes when I use this method to get a list of actors. Apparently isInstance() and isInstanceOf are not compatible with GWT so I'm looking for a way to get around this. Gradle tell me isInstance is not defined. It runs fine on desktop.
public static ArrayList<BaseActor> getList(Stage stage, String className) {
ArrayList<BaseActor> list = new ArrayList<BaseActor>();
Class theClass = null;
try {
theClass = ClassReflection.forName("com.mygdx.game.actors." + className);
} catch (Exception error) {
error.printStackTrace();
}
for (Actor a : stage.getActors()) {
if (theClass.isInstance(a))
list.add((BaseActor) a);
}
return list;
}
The Actor class has a user object property (getUserObject()/setUserObject()) that you can use to attach data. You could make your BaseActor use this as a class tag property, and use an abstract method so you won't forget to add it to any of your Actor implementations.
abstract class BaseActor {
//...
public BaseActor() {
setUserObject(getClassTag());
//...
}
protected abstract String getClassTag();
}
class SomeSpecificActor extends BaseActor {
public SomeSpecificActor () {
super();
//...
}
#Override
protected String getClassTag() {
return "SomeSpecificActor";
}
}
public static ArrayList<BaseActor> getList(Stage stage, String classTag) {
ArrayList<BaseActor> list = new ArrayList<BaseActor>();
for (Actor a : stage.getActors()) {
if (classTag.equals(a.getUserObject()))
list.add((BaseActor) a);
}
return list;
}

Inject Multiple Parameters Same interface in Java

I have to process multiple parsers(irrelevant). How can I inject correctly so StepProcessor could receive all classes? My class is:
#Inject
public StepProcessor(IParser... parsers) {
if (parsers == null) {
throw new IllegalArgumentException("Parsers cannot be null");
}
this.parsers = parsers;
}
#Override
public void process( String name ) {
for (IParser parser : parsers) {
System.out.println(parser.getName());
}
}
How am I injecting?:
public class BasicModule extends AbstractModule {
#Override
protected void configure() {
bind(IParser.class).to(XmlParser.class);
bind(IParser.class).to(JsonParser.class);
bind(IParser.class).to(TextParser.class);
bind(IStepProcessor.class).to(StepProcessor.class);
}
}
I got:
com.google.inject.CreationException: Unable to create injector, see the following errors:
1) A binding to IParser was already configured at BasicModule.configure(BasicModule.java:7).
at BasicModule.configure(BasicModule.java:8)
MY usage:
Injector injector = Guice.createInjector(new BasicModule());
IStepProcessor comms = injector.getInstance(IStepProcessor.class);
comms.process("name");
You can use something called MultiBinding from Guice to achieve this.
#Inject
public StepProcessor(Set<IParser> parsers) { //Inject a set of IParser
if (parsers == null) {
throw new IllegalArgumentException("Parsers cannot be null");
}
this.parsers = parsers;
}
#Override
public void process( String name ) {
for (IParser parser : parsers) {
System.out.println(parser.getName());
}
}
Now change your module to this.
public class BasicModule extends AbstractModule {
#Override
protected void configure() {
MultiBinder<IParser> iParserBinder = MultiBinder.newSetBinder(binder(), IParser.class);
iParserBinder.addBinding().to(XmlParser.class);
iParserBinder.addBinding().to(JsonParser.class);
iParserBinder.addBinding().to(TextParser.class);
iParserBinder.addBinding().to(StepProcessor.class);
}
Don't forget the relevant imports. Do read the documentation before using it to understand how exactly it works. Hope this helps :)

Assisted injection error while trying to create presenter

I already used Gin assisted injection a few times but I'm new to gwtp and I'm facing an error message I don't understand while trying to instantiate a PresenterWidget using my factory, even after reading several posts on the subject...
[ERROR] Error injecting #com.google.inject.assistedinject.Assisted(value=) java.lang.Integer:
Unable to create or inherit binding:
Binding requested for constant key '#com.google.inject.assistedinject.Assisted(value=) java.lang.Integer' but no explicit binding was found;
Path to required node:
org.moshika.dtp.client.application.book.page.PagePresenter [com.gwtplatform.mvp.client.gin.AbstractPresenterModule.bindPresenterWidget(AbstractPresenterModule.java:260)]
Context : I have to display the content of a book on the screen. For the moment I use a BookPresenter with proxy and place and I will try to bind / unbind pages on the fly as PresenterWidget. I don't think my pages could be implemented as CellWidget because I will implement lots of DTP / WYSIWYG functionalities.
My Module :
public class CommonModule extends AbstractGinModule {
#Override
protected void configure() {
// DefaultPlaceManager Places
install(new DefaultModule.Builder().defaultPlace(NameTokens.SEARCH).errorPlace(NameTokens.ERROR).unauthorizedPlace(NameTokens.ERROR).build());
install(new GinFactoryModuleBuilder().build(PagePresenter.Factory.class));
RestDispatchAsyncModule.Builder dispatchBuilder = new RestDispatchAsyncModule.Builder();
install(dispatchBuilder.build());
bindConstant().annotatedWith(RestApplicationPath.class).to("rest");
bind(ResourceLoader.class).asEagerSingleton();
bindPresenter(BookPresenter.class, BookPresenter.MyView.class, BookViewTablet.class, BookPresenter.MyProxy.class);
bindPresenterWidget(PagePresenter.class, PagePresenter.MyView.class, PageViewTablet.class);
}
BookPresenter :
public class BookPresenter extends Presenter<BookPresenter.MyView, BookPresenter.MyProxy>
implements BookUiHandlers {
public interface MyView extends View, HasUiHandlers<BookUiHandlers> {
}
#ProxyStandard
#NameToken(NameTokens.BOOK)
public interface MyProxy extends ProxyPlace<BookPresenter> {
}
static final Slot<PagePresenter> SLOT_BOOK = new Slot<PagePresenter>();
private ResourceDelegate<PageResources> pageDelegate;
private PagePresenter.Factory factory;
#Inject
BookPresenter(EventBus eventBus,
MyView view, MyProxy proxy,
ResourceDelegate<PageResources> pageDelegate,
PagePresenter.Factory factory) {
super(eventBus, view, proxy, ApplicationPresenter.SLOT_MAIN);
view.setUiHandlers(this);
this.pageDelegate= pageDelegate;
this.factory= factory;
}
#Override
protected void onReveal() {
super.onReveal();
NavigationVisibilityEvent.fire(this, true);
fetchPages(0, 5);
}
#Override
public void fetchPages(final int offset, int limit) {
pageDelegate.withCallback(new AsyncCallback<List<PageDto>>() {
#Override
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
#Override
public void onSuccess(List<PageDto> dtos) {
clearSlot(SLOT_BOOK);
for (PageDto dto : dtos) {
PagePresenter pagePresenter = factory.create(dto.getFolioPage());
addToSlot(SLOT_DEROULE, pagePresenter);
pagePresenter.refreshModel();
}
}
}).list(offset, limit);
}
}
PagePresenter and Factory :
public class PagePresenter extends PresenterWidget<PagePresenter .MyView>
implements PageUiHandlers {
public interface MyView extends View {
void setFolio(Integer folio);
}
public interface Factory {
CahierPageJourPresenter create(Integer folio);
}
private ResourceDelegate<PageResources> pageDelegate;
private Integer folioPage;
#Inject
PagePresenter(EventBus eventBus, MyView view,
ResourceDelegate<PageResources> pageDelegate,
#Assisted Integer folio) {
super(eventBus, view);
this.pageDelegate= pageDelegate;
this.folio= folio;
}
public void refreshModel() {
pageDelegate.withCallback(new AsyncCallback<PageDto>() {
#Override
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
#Override
public void onSuccess(PageDtodto) {
getView().setFolio(dto.getFolio());
}
}).get(folio);
}
}
It might be a very stupid mistake since I don't see what I'm doing different from all the other posts on the same subject...
If you want to use assisted injection here, don't call bindPresenterWidget(PagePresenter.class, PagePresenter.MyView.class, PageViewTablet.class); directly. Instead, bind only the view: bind(PagePresenter.MyView.class).to(PageViewTablet.class);
bindPresenterWidget actually calls bind(PagePresenter.class). Then bindings are resolved and since you don't have a constant for an Integer annotated with #Assisted, it throws.
If you only ever have one page visible at a time, you can also use a URL parameter to store the page number. Then you can override onReset() and update the content based on the requested page. This technique would avoid instantiating multiple PagePresenter and you can get rid of assisted factories.

Java Guice Provider

I have a small problem which I can't figure out to save my life.
Basically I need to register classes anytime dynamically using guice and then loop through them all.
Lets say this is my class to register Strategies but these strategies can be added anytime through the application running.
// Strategy registration may happen anytime, this is just an example
strategyManager.register(ExampleStrategy1.class);
strategyManager.register(ExampleStrategy2.class);
StrategyImpl class
public class StrategyImpl implements Strategy {
#Override
public void register(Class<? extends StrategyDispatcher> strat) {
//Add this class into provider or create an instance for it and add it into guice but how?
}
#Override
public void dispatchStrategy() {
//Find all strategies and execute them
}
}
I've tried using a Provider but have no idea how i'd add the registered class into the provider and retrieve them all?
#Override
protected void configure() {
bind(Strategy.class).toProvider(StrategyProvider.class);
}
My provider class always gets the same instance
public class StrategyProvider implements Provider<StrategyDispatcher> {
public LogManager get() {
return new StrategyDispatcherImpl();
}
}
The strategies that I add extend the StrategyDispatcherImpl class so i could cast them?
I need to add multiple binds to a same instance but it needs to be done dynamically and not using the bind method in configure but another way then be able to find all these strategies and execute them.
If you truly need it to happen at "any time" during the application life cycle then Guice then I think you will need some sort of Guice-aware Factory. I.e.
public class TestStuff {
#Test
public void testDynamicCreation() {
Injector injector = Guice.createInjector();
StrategyManager manager = injector.getInstance(StrategyManager.class);
Hello hello = injector.getInstance(Hello.class);
manager.doStuff();
assertThat(hello.helloCalled, is(false));
manager.register(Hello.class); // DYNAMIC!!
manager.doStuff();
assertThat(hello.helloCalled, is(true));
}
}
interface Strategy {
void doStuff();
}
#Singleton
class Hello implements Strategy {
boolean helloCalled = false;
public void doStuff() {
helloCalled = true;
}
}
class StrategyManager {
private final Collection<Strategy> strategies = new ArrayList<>();
private final StrategyFactory factory;
#Inject
StrategyManager(StrategyFactory factory) {
this.factory = factory;
}
public void register(Class<? extends Strategy> strat) {
strategies.add(factory.create(strat));
}
public void doStuff() {
for (Strategy s : strategies) {
s.doStuff();
}
}
}
class StrategyFactory {
private final Injector injector;
#Inject
StrategyFactory(Injector injector) {
this.injector = injector;
}
public Strategy create(Class<? extends Strategy> clazz) {
return injector.getInstance(clazz);
}
}
If it is not "dynamic" after the initialization phase then you are after the "multibinder" I think.

How to implement and test mapbinding correctly with Guice and Play framework

I just started using Guice and Play so I guess this is a long but basic question. I checked the guide here: http://eng.42go.com/play-framework-dependency-injection-guice/ but I don't know why my code fails.
First I have a global injector:
public class GlobalInjector {
private static Injector guiceInjector;
private static List<AbstractModule> modules = new ArrayList<AbstractModule>();
public static Injector getInjector() {
return guiceInjector;
}
public static loadModules() {
guiceInjector = Guice.createInjector(modules);
}
public static addModule(AbstractModule module) {
modules.add(module);
}
}
Also I have added Guice to Play by extending the GlobalSettings class (also modified application.global)
public class GuiceExtendedSettings extends GlobalSettings {
#Override
public void onStart(Application app) {
GlobalInjector.loadModules();
}
#Override
public <A> A getControllerInstance(Class<A> controllerClass) {
return GlobalInjector.getInjector().getInstance(controllerClass);
}
}
Then I have my test module acting as a plugin in Play (some required methods are omitted as they do nothing in this piece):
public class TestModule extends AbstractModule implements Plugin {
#Override
public void configure() {
// Worker is a simple class
Worker worker = new SimpleWorker();
MapBinder<String, Worker> mapBinder = MapBinder.newMapBinder(binder(), String.class, Worker.class);
mapBinder.addBinding(worker.getName()).toInstance(worker);
}
#Override
public void onStart() {
GlobalInjector.addModule(this);
}
}
Worker is a simple interface:
public interface Worker {
public String getName();
public String getResult();
}
SimpleWorker:
public class SimpleWorker implements Worker {
public String getName() {
return "SimpleWorker";
}
public String getResult() {
return "works";
}
}
And here is the code piece showing the controller logic: nothing but just print all worker results in the map injected
public class TestController extends Controller {
#Inject
Map<String, Worker> workers;
public Result showWorkers() {
StringBuilder sb = new StringBuilder();
for (Worker worker : workers) {
sb.append(worker.getName() + ": " + worker.getResult() + "</br>");
}
return ok(sb.toString()).as("text/html");
}
}
OK. To make this work, I put the following line in play.plugins:
100:test.TestModule
My idea is:
Play loads the plugin (TestModule) -> TestModule adds itself to the GlobalInjector -> GlobalInjector creates Guice injector -> Guice injects the map to the controller
However the result was
Guice didn't inject the map. The map is still null.
Also how should I test it? (i.e. how can I inject different workers to that map? I hard-coded that part in the above code. But I'm looking for a dynamic way by using different modules.)
public class Test {
#Test
public void testInjector() {
running(fakeApplication(), new Runnable() {
public void run() {
// how can I inject using different modules here?
}
});
}
}
You need to use the fakeApplication helper method that allows you to specify both your global settings object and additional plugins. See http://www.playframework.com/documentation/api/2.1.x/java/play/test/Helpers.html#fakeApplication(java.util.Map,%20java.util.List,%20play.GlobalSettings) for more information.
But basically, your test should look something like:
public class Test {
#Test
public void testInjector() {
Map<String, Object> config = new HashMap<String, Object>();
// add any additional config options, e.g. in-memory db
List<String> plugins = new ArrayList<String>();
plugins.add("full.package.name.TestModule");
GlobalSettings global = null;
try {
global = (GlobalSettings) Class.forName("full.package.name.GuiceExtendedSettings").newInstance();
} catch(Exception e) {}
running(fakeApplication(config, plugins, global), new Runnable() {
public void run() {
// do some assertions
}
});
}
}
You also need to make sure that guice instantiates the test controller or the workers map won't be injected.

Categories

Resources