Proper use of GWT RequestFactory ServiceLocator and DI

502 views Asked by At

I am experimenting with RequestFactory (RF) for the first time and am struggling to implement my first ServiceLocator.

From the RequestContext:

// Sign a user in or out of the app.
@ServiceName(
    value="com.myapp.server.DefaultSignInOutService",
    locator="com.myapp.server.DefaultSignInOutServiceLocator"
)
public interface SignInOutService extends RequestContext {
    public Request<String> signIn(SignIn signIn);
    public Request<Void> signOut(SignOut signOut);
}

And then the DefaultSignInOutServiceLocator:

public class DefaultSignInOutServiceLocator implements ServiceLocator {
    // I am using Guice-3.0 for server-side DI, and ServiceLocatorModule is an AbstractModule.
    ServiceLocatorModule serviceLocatorModule = new ServiceLocatorModule();

    // Will be initialized by Guice.
    private DefaultSignInOutService signInOutService;

    public DefaultSignInOutServiceLocator() {
        super();

        // Bootstrap DI.
        Injector injector = GWT.createInjector(serviceLocatorModule);

        // injector.getInstance() returns a fully-configured/wired
        // DefaultSignInOutService instance.
        setSignInOutService(injector.getInstance(SignInOutService.class));
    }

    @Override
    public Object getInstance(Class<?> clazz) {
        // I'm trying to use proper DI best practices here, and avoid code like:
        //
        // return new DefaultSignInOutService(true, "Yes", 35);
        //
        // Rather, I'd like to be able to return an already pre-configured service impl:
        return signInOutService;
    }

    // Getters/setters, etc.
}

My understanding is that the ServiceLocators are basically factories for service implementations. If that's true, then if I'm using Guice for server-side DI, I need to initialize my Guice module from inside the locator's constructor. However, if there is any code that I need to write myself (elsewhere in the app) that creates an instance of DefaultSignInOutServiceLocator and calls its getInstance() method explicitly, then I don't need to put the ServiceLocatorModule inside DefaultSignInOutServiceLocator. In that case, I could have code like this:

public class DefaultSignInOutServiceLocator implements ServiceLocator {
    @Injected
    private DefaultSignInOutService signInOutService;

    @Override
    public Object getInstance(Class<?> clazz) {
        return signInOutService;
    }

    // Getters/setters, etc.
}

So here are my questions:

  1. Are ServiceLocator impls an appropriate place to put Guice modules (and thus bootstrap DI from inside them)? Otherwise, how can I inject the locator with a properly-wired/confgured service impl?
  2. Or, am I just not understanding the purpose of ServiceLocator#getInstance()?
  3. And if I am on the right track here, then what "scope" (Spring DI terminology) should the injected signInOutService be? Should it be a singleton or a multiton/prototype? Do I need to worry about thread-safety here (multiple threads obtaining the same signInOutService instance)? Or does GWT somehow ensure that the RequestFactoryServlet accesses locators in a thread-safe way?
1

There are 1 answers

4
Thomas Broyer On BEST ANSWER

ServiceLocators are instantiated by a ServiceLayerDecorator, and you can plug your own.

ServiceLocators and the service instances they create a almost singletons (they could be garbage-collected in case the available memory is low, and new instances recreated afterwards), so you should either configure them as singletons on your side or at least make sure you treat them as such (i.e. inject Providers for request-scoped values such as the current user).

You can find a complete example in the form of a Maven archetype at https://github.com/tbroyer/gwt-maven-archetypes