NullPointerException in generated PlaceHistoryMapperImpl (with factory)- how/when to "setFactory" (Problem only in hosted mode)

141 views
Skip to first unread message

tanteanni

unread,
May 22, 2012, 4:55:15 AM5/22/12
to google-we...@googlegroups.com
I am using "PlaceHistoryMapperWithFactory" as suggested here. The factory is set within my "ClientModule" (gin):

    @Provides
    @Singleton
    @Inject
    public final PlaceHistoryMapperWithFactory<TokenizerFactory> getPlaceHistoryMapper(AppPlaceHistoryMapper hm,
                                                                                       TokenizerFactory tf) {
        hm.setFactory(tf);
        return hm;
    }

I watched the code with debugger (Breakpoints on two lines above and on "NullPointerException"): 
1. The factory is set within  "AbstractPlaceHistoryMapper<F>" and "hm" with set factory is returned
2. AppPlaceHistoryMapperImpl throws NPEx at "return new PrefixAndToken("ContentMenu", factory.contentMenuPlace().getToken(place));" because factory is null

What is going wrong here?

One probably important thing: This problem is only (re)producible in hosted mode. If app is deployed it works fine!

Thomas Broyer

unread,
May 22, 2012, 5:06:31 AM5/22/12
to google-we...@googlegroups.com
Are you sure you're having the same AppPlaceHistoryMapper instance in both places?

If I were you, I'd rather write my module as:
@Provides @Singleton
PlaceHistoryMapperWithFactory<TokenizerFactory> providePlaceHistoryMapper(TokenizerFactory tf) {
   PlaceHistoryMapperWithFactory<TokenizerFactory> hm = GWT.create(AppPlaceHistoryMapper.class);
   hm.setFactory(tf);
   return hm;
}

Alternatively, you can also remove that method entirely and simply override and annotate the setFactory method in your AppPlaceHistoryMapper interface:
public interface AppPlaceHistoryMapper extends PlaceHistoryMapperWithFactory<TokenizerFactory> {
   @Inject
   void setFactory(TokenizerFactory tf);
}
That will work with GIN (would fail with Guice though); see http://code.google.com/p/google-web-toolkit/issues/detail?id=6151#c4

(BTW, why is your method annotated with @Inject?)

As for the difference between DevMode and prod mode, you should probably file a bug on GIN.

tanteanni

unread,
May 22, 2012, 5:41:14 AM5/22/12
to google-we...@googlegroups.com
thx thomas for fast reply,

the problem is indeed that two instances are created one " PlaceHistoryMapperWithFactory<TokenizerFactory> " instance and one instances where constructors inject "AppPlaceHistoryMapper" (this one has null factory). 
But how to fix this? Your suggested provides-Code shouldn't change anything, should it?
So I tried your 2nd suggestion:
plus this in module:
        bind(new TypeLiteral<PlaceHistoryMapperWithFactory<TokenizerFactory>>() {
        }).to(AppPlaceHistoryMapper.class).in(Singleton.class);
It is working! 
But with this every user of AppPlaceHistoryMapper get a different instance - (i guess this is not really a problem) - is it possible to get/bind a singleton. 
Is it possible to bind AppPlaceHistoryMapper and  PlaceHistoryMapperWithFactory<TokenizerFactory> to the same instance of generated  AppPlaceHistoryMapper Impl?

Thomas Broyer

unread,
May 22, 2012, 5:58:39 AM5/22/12
to google-we...@googlegroups.com


On Tuesday, May 22, 2012 11:41:14 AM UTC+2, tanteanni wrote:
thx thomas for fast reply,

the problem is indeed that two instances are created one " PlaceHistoryMapperWithFactory<TokenizerFactory> " instance and one instances where constructors inject "AppPlaceHistoryMapper" (this one has null factory). 
But how to fix this? Your suggested provides-Code shouldn't change anything, should it?

It depends.
If you have code that depends on AppPlaceHistoryMapper, then change the return type of the provider-method to that.
And if some code depends on PlaceHistoryMapperWithFactory<TokenizerFactory>, and/or PlaceHistoryMapper, then bind those types to AppPlaceHistoryMapper (see below).
 
So I tried your 2nd suggestion:
plus this in module:
        bind(new TypeLiteral<PlaceHistoryMapperWithFactory<TokenizerFactory>>() {
        }).to(AppPlaceHistoryMapper.class).in(Singleton.class);
It is working! 
But with this every user of AppPlaceHistoryMapper get a different instance - (i guess this is not really a problem) - is it possible to get/bind a singleton. 
Is it possible to bind AppPlaceHistoryMapper and  PlaceHistoryMapperWithFactory<TokenizerFactory> to the same instance of generated  AppPlaceHistoryMapper Impl?

Yes:
bind(new TypeLiteral<PlaceHistoryMapperWithFactory<TokenizerFactory>>() { }).to(AppPlaceHistoryMapper.class);
bind(AppPlaceHistoryMapper.class).in(Singleton.class); // either that, or a @Provides @Singleton method.

i.e.: when you need a PlaceHistoryMapperWithFactory<TokenizerFactory>, then use an AppPlaceHistoryMapper; and when you need an AppPlaceHistoryMapper, then bind it as a singleton.

tanteanni

unread,
May 22, 2012, 6:11:19 AM5/22/12
to google-we...@googlegroups.com
thx!

it works
Reply all
Reply to author
Forward
0 new messages